diff --git a/.changelog/1808.txt b/.changelog/1808.txt new file mode 100644 index 0000000000..a860c635d9 --- /dev/null +++ b/.changelog/1808.txt @@ -0,0 +1,3 @@ +```release-note:bug +helm: add missing `$HOST_IP` environment variable to to mesh gateway deployments. +``` diff --git a/.changelog/1920.txt b/.changelog/1920.txt new file mode 100644 index 0000000000..4b1f151fe4 --- /dev/null +++ b/.changelog/1920.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: When the `global.acls.bootstrapToken` field is set and the content of the secret is empty, the bootstrap ACL token is written to that secret after bootstrapping ACLs. This applies to both the Vault and Consul secrets backends. +``` diff --git a/.changelog/2008.txt b/.changelog/2008.txt new file mode 100644 index 0000000000..ba8bb5fa25 --- /dev/null +++ b/.changelog/2008.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: Set default `limits.cpu` resource setting to `null` for `consul-connect-inject-init` container to speed up registration times when onboarding services onto the mesh during the init container lifecycle. +``` diff --git a/.changelog/2013.txt b/.changelog/2013.txt new file mode 100644 index 0000000000..056253a5d2 --- /dev/null +++ b/.changelog/2013.txt @@ -0,0 +1,3 @@ +```release-note:bug +api-gateway: fix issue where specifying an external server SNI name while using client nodes resulted in a TLS verification error. +``` \ No newline at end of file diff --git a/.changelog/2029.txt b/.changelog/2029.txt new file mode 100644 index 0000000000..c864419eba --- /dev/null +++ b/.changelog/2029.txt @@ -0,0 +1,3 @@ +```release-note:bug +api-gateway: fix ACL issue where when adminPartitions and ACLs are enabled, API Gateway Controller is unable to create a new namespace in Consul +``` \ No newline at end of file diff --git a/.changelog/2068.txt b/.changelog/2068.txt new file mode 100644 index 0000000000..bc17061f51 --- /dev/null +++ b/.changelog/2068.txt @@ -0,0 +1,3 @@ +```release-note:bug +sync-catalog: fix issue where the sync-catalog ACL token were set with an incorrect ENV VAR. +``` \ No newline at end of file diff --git a/.changelog/2078.txt b/.changelog/2078.txt new file mode 100644 index 0000000000..2206de1128 --- /dev/null +++ b/.changelog/2078.txt @@ -0,0 +1,3 @@ +```release-note:improvement +cli: Add `consul-k8s config read` command that returns the helm configuration in yaml format. +``` diff --git a/.changelog/2083.txt b/.changelog/2083.txt new file mode 100644 index 0000000000..23c0c77592 --- /dev/null +++ b/.changelog/2083.txt @@ -0,0 +1,3 @@ +```release-note:bug +api-gateway: fix issue where the API Gateway controller is unable to start up successfully when Vault is configured as the secrets backend +``` diff --git a/.changelog/2098.txt b/.changelog/2098.txt new file mode 100644 index 0000000000..f7c56e65a3 --- /dev/null +++ b/.changelog/2098.txt @@ -0,0 +1,3 @@ +```release-note:feature +sync-catalog: add ability to sync hostname from a Kubernetes Ingress resource to the Consul Catalog during service registration. +``` \ No newline at end of file diff --git a/.changelog/2104.txt b/.changelog/2104.txt new file mode 100644 index 0000000000..59d120f747 --- /dev/null +++ b/.changelog/2104.txt @@ -0,0 +1,12 @@ +```release-note:security +Upgrade to use Go 1.20.4. +This resolves vulnerabilities [CVE-2023-24537](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`go/scanner`), +[CVE-2023-24538](https://github.com/advisories/GHSA-v4m2-x4rp-hv22)(`html/template`), +[CVE-2023-24534](https://github.com/advisories/GHSA-8v5j-pwr7-w5f8)(`net/textproto`) and +[CVE-2023-24536](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`mime/multipart`). +Also, `golang.org/x/net` has been updated to v0.7.0 to resolve CVEs [CVE-2022-41721 +](https://github.com/advisories/GHSA-fxg5-wq6x-vr4w +), [CVE-2022-27664](https://github.com/advisories/GHSA-69cg-p879-7622) and [CVE-2022-41723 +](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h +.) +``` diff --git a/.changelog/2134.txt b/.changelog/2134.txt new file mode 100644 index 0000000000..980e7ba666 --- /dev/null +++ b/.changelog/2134.txt @@ -0,0 +1,3 @@ +```release-note:feature +Add support for consul-telemetry-collector to forward envoy metrics to an otelhttp compatible receiver or HCP +``` \ No newline at end of file diff --git a/.changelog/2143.txt b/.changelog/2143.txt new file mode 100644 index 0000000000..8f58328f3d --- /dev/null +++ b/.changelog/2143.txt @@ -0,0 +1,4 @@ + +```release-note:feature +consul-telemetry-collector: Configure envoy proxy config during registration when consul-telemetry-collector is enabled. +``` diff --git a/.changelog/2156.txt b/.changelog/2156.txt new file mode 100644 index 0000000000..cb812b451a --- /dev/null +++ b/.changelog/2156.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: add support for idleTimeout in the Service Router config +``` diff --git a/.changelog/2160.txt b/.changelog/2160.txt new file mode 100644 index 0000000000..9b970bf3f4 --- /dev/null +++ b/.changelog/2160.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: fix issue with json tags of service defaults fields EnforcingConsecutive5xx, MaxEjectionPercent and BaseEjectionTime. +``` \ No newline at end of file diff --git a/.changelog/2176.txt b/.changelog/2176.txt new file mode 100644 index 0000000000..0aee796433 --- /dev/null +++ b/.changelog/2176.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: fix issue with multiport pods crashlooping due to dataplane port conflicts by ensuring dns redirection is disabled for non-tproxy pods +``` diff --git a/.changelog/2194.txt b/.changelog/2194.txt new file mode 100644 index 0000000000..fb265d9739 --- /dev/null +++ b/.changelog/2194.txt @@ -0,0 +1,3 @@ +```release-note:bug +crd: fix bug on service intentions CRD causing some updates to be ignored. +``` diff --git a/.changelog/2195.txt b/.changelog/2195.txt new file mode 100644 index 0000000000..e7475f88e0 --- /dev/null +++ b/.changelog/2195.txt @@ -0,0 +1,3 @@ +```release-note:improvement +consul-telemetry-collector: add acceptance tests for consul telemetry collector component. +``` diff --git a/.changelog/2204.txt b/.changelog/2204.txt new file mode 100644 index 0000000000..6962f485a0 --- /dev/null +++ b/.changelog/2204.txt @@ -0,0 +1,3 @@ +```release-note:security +Bump Dockerfile base image for RedHat UBI `consul-k8s-control-plane` image to `ubi-minimal:9.2`. +``` diff --git a/.changelog/2205.txt b/.changelog/2205.txt new file mode 100644 index 0000000000..6a66970cfc --- /dev/null +++ b/.changelog/2205.txt @@ -0,0 +1,3 @@ +```release-note:improvement +cli: update cloud preset to enable telemetry collector +``` \ No newline at end of file diff --git a/.changelog/2226.txt b/.changelog/2226.txt new file mode 100644 index 0000000000..fcbb89a54b --- /dev/null +++ b/.changelog/2226.txt @@ -0,0 +1,3 @@ +```release-note:security +Bump `controller-runtime` to address CVEs in dependencies. +``` diff --git a/.changelog/2233.txt b/.changelog/2233.txt new file mode 100644 index 0000000000..bb929501c9 --- /dev/null +++ b/.changelog/2233.txt @@ -0,0 +1,3 @@ +```release-note:feature +Add support for configuring graceful shutdown proxy lifecycle management settings. +``` diff --git a/.changelog/2249.txt b/.changelog/2249.txt new file mode 100644 index 0000000000..9c6a50e098 --- /dev/null +++ b/.changelog/2249.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: Update the default amount of memory used by the connect-inject controller so that its less likely to get OOM killed. +``` diff --git a/.changelog/2262.txt b/.changelog/2262.txt new file mode 100644 index 0000000000..267377ebe9 --- /dev/null +++ b/.changelog/2262.txt @@ -0,0 +1,3 @@ +```release-note:improvement +cli: add consul-telemetry-gateway allow-all intention for -demo +``` \ No newline at end of file diff --git a/.changelog/2266.txt b/.changelog/2266.txt new file mode 100644 index 0000000000..e156f95f8c --- /dev/null +++ b/.changelog/2266.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: Fix casing of the Enforce Consecutive 5xx field on Service Defaults and acceptance test fixtures. +``` \ No newline at end of file diff --git a/.changelog/2284.txt b/.changelog/2284.txt new file mode 100644 index 0000000000..27c51dff07 --- /dev/null +++ b/.changelog/2284.txt @@ -0,0 +1,3 @@ +```release-note:security +Bump Dockerfile base image to `alpine:3.18`. Resolves [CVE-2023-2650](https://github.com/advisories/GHSA-gqxg-9vfr-p9cg) vulnerability in openssl@3.0.8-r4 +``` diff --git a/.changelog/2293.txt b/.changelog/2293.txt new file mode 100644 index 0000000000..ce6d888bcd --- /dev/null +++ b/.changelog/2293.txt @@ -0,0 +1,3 @@ +```release-note:feature +sync-catalog: add ability to support weighted loadbalancing by service annotation `consul.hashicorp.com/service-weight: ` +``` \ No newline at end of file diff --git a/.changelog/2302.txt b/.changelog/2302.txt new file mode 100644 index 0000000000..7bf7e6b0f6 --- /dev/null +++ b/.changelog/2302.txt @@ -0,0 +1,13 @@ +```release-note:improvement +Add support to provide the logLevel flag via helm for multiple low level components. Introduces the following fields +1. `global.acls.logLevel` +2. `global.tls.logLevel` +3. `global.federation.logLevel` +4. `global.gossipEncryption.logLevel` +5. `server.logLevel` +6. `client.logLevel` +7. `meshGateway.logLevel` +8. `ingressGateways.logLevel` +9. `terminatingGateways.logLevel` +10. `telemetryCollector.logLevel` +``` diff --git a/.changelog/2369.txt b/.changelog/2369.txt new file mode 100644 index 0000000000..35643ce272 --- /dev/null +++ b/.changelog/2369.txt @@ -0,0 +1,3 @@ +```release-note:improvement +(Consul Enterprise) Add support to provide inputs via helm for audit log related configuration +``` \ No newline at end of file diff --git a/.changelog/2390.txt b/.changelog/2390.txt new file mode 100644 index 0000000000..a4546bd781 --- /dev/null +++ b/.changelog/2390.txt @@ -0,0 +1,3 @@ +```release-note:security +Update [Go-Discover](https://github.com/hashicorp/go-discover) in the container has been updated to address [CVE-2020-14040](https://github.com/advisories/GHSA-5rcv-m4m3-hfh7) +``` diff --git a/.changelog/2392.txt b/.changelog/2392.txt new file mode 100644 index 0000000000..e268c796ff --- /dev/null +++ b/.changelog/2392.txt @@ -0,0 +1,6 @@ +```release-note:breaking-change +control-plane: All policies managed by consul-k8s will now be updated on upgrade. If you previously edited the policies after install, your changes will be overwritten. +``` +```release-note:bug +control-plane: Always update ACL policies upon upgrade. +``` diff --git a/.changelog/2416.txt b/.changelog/2416.txt new file mode 100644 index 0000000000..e261758542 --- /dev/null +++ b/.changelog/2416.txt @@ -0,0 +1,3 @@ +```release-note:feature +helm: Adds `acls.resources` field which can be configured to override the `resource` settings for the `server-acl-init` and `server-acl-init-cleanup` Jobs. +``` diff --git a/.changelog/2525.txt b/.changelog/2525.txt new file mode 100644 index 0000000000..74a2cd596e --- /dev/null +++ b/.changelog/2525.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: adds values for `securityContext` and `annotations` on TLS and ACL init/cleanup jobs. +``` diff --git a/.changelog/2571.txt b/.changelog/2571.txt new file mode 100644 index 0000000000..91b3f2943b --- /dev/null +++ b/.changelog/2571.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: fix bug in endpoints controller when deregistering services from consul when a node is deleted. +``` diff --git a/.changelog/2572.txt b/.changelog/2572.txt new file mode 100644 index 0000000000..4bc6c4ba50 --- /dev/null +++ b/.changelog/2572.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: set container securityContexts to match the `restricted` Pod Security Standards policy to support running Consul in a namespace with restricted PSA enforcement enabled +``` diff --git a/.changelog/2642.txt b/.changelog/2642.txt new file mode 100644 index 0000000000..5278ed705c --- /dev/null +++ b/.changelog/2642.txt @@ -0,0 +1,4 @@ +```release-note:security +Upgrade to use Go 1.20.6 and `x/net/http` 0.12.0. +This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). +``` diff --git a/.changelog/2652.txt b/.changelog/2652.txt new file mode 100644 index 0000000000..efa290c0e7 --- /dev/null +++ b/.changelog/2652.txt @@ -0,0 +1,3 @@ +```release-note:bug +helm: fix CONSUL_LOGIN_DATACENTER for consul client-daemonset. +``` \ No newline at end of file diff --git a/.changelog/2656.txt b/.changelog/2656.txt new file mode 100644 index 0000000000..07436087d3 --- /dev/null +++ b/.changelog/2656.txt @@ -0,0 +1,3 @@ +```release-note:improvement +control-plane: increase timeout after login for ACL replication to 60 seconds +``` \ No newline at end of file diff --git a/.changelog/2678.txt b/.changelog/2678.txt new file mode 100644 index 0000000000..97e7707c41 --- /dev/null +++ b/.changelog/2678.txt @@ -0,0 +1,3 @@ +```release-note:improvement +helm: do not set container securityContexts by default on OpenShift < 4.11 +``` diff --git a/.changelog/2687.txt b/.changelog/2687.txt new file mode 100644 index 0000000000..5fa4a92b4d --- /dev/null +++ b/.changelog/2687.txt @@ -0,0 +1,3 @@ +```release-note:bug +helm: fix ui ingress manifest formatting, and exclude `ingressClass` when not defined. +``` \ No newline at end of file diff --git a/.changelog/2710.txt b/.changelog/2710.txt new file mode 100644 index 0000000000..1d37b32dfb --- /dev/null +++ b/.changelog/2710.txt @@ -0,0 +1,5 @@ +```release-note:security +Upgrade to use Go 1.20.7 and `x/net` 0.13.0. +This resolves [CVE-2023-29409](https://nvd.nist.gov/vuln/detail/CVE-2023-29409)(`crypto/tls`) +and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978)(`net/html`). +``` diff --git a/.changelog/2755.txt b/.changelog/2755.txt new file mode 100644 index 0000000000..1d8cf20360 --- /dev/null +++ b/.changelog/2755.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: When using transparent proxy or CNI, reduced required permissions by setting privileged to false. Privileged must be true when using OpenShift without CNI. +``` diff --git a/.changelog/2782.txt b/.changelog/2782.txt new file mode 100644 index 0000000000..f01db6bafa --- /dev/null +++ b/.changelog/2782.txt @@ -0,0 +1,3 @@ +```release-note:bug +helm: Update prometheus port and scheme annotations if tls is enabled +``` diff --git a/.changelog/2787.txt b/.changelog/2787.txt new file mode 100644 index 0000000000..2fe921ef23 --- /dev/null +++ b/.changelog/2787.txt @@ -0,0 +1,3 @@ +```release-note:improvement +Add NET_BIND_SERVICE capability to restricted security context used for consul-dataplane +``` diff --git a/.changelog/2808.txt b/.changelog/2808.txt new file mode 100644 index 0000000000..d6d0270e44 --- /dev/null +++ b/.changelog/2808.txt @@ -0,0 +1,3 @@ +```release-note:bug +control-plane: Fix issue where ACL tokens would have an empty pod name that prevented proper token cleanup. +``` diff --git a/.changelog/changelog.tmpl b/.changelog/changelog.tmpl new file mode 100644 index 0000000000..c1de4293b9 --- /dev/null +++ b/.changelog/changelog.tmpl @@ -0,0 +1,57 @@ +{{- if index .NotesByType "breaking-change" -}} +BREAKING CHANGES: + +{{range index .NotesByType "breaking-change" -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.security }} +SECURITY: + +{{range .NotesByType.security -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.feature }} +FEATURES: + +{{range .NotesByType.feature -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + +{{- $improvements := combineTypes .NotesByType.improvement .NotesByType.enhancement -}} +{{- if $improvements }} +IMPROVEMENTS: + +{{range $improvements | sort -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.deprecation }} +DEPRECATIONS: + +{{range .NotesByType.deprecation -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.bug }} +BUG FIXES: + +{{range .NotesByType.bug -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + +{{- if .NotesByType.note }} +NOTES: + +{{range .NotesByType.note -}} +* {{ template "note" . }} +{{ end -}} +{{- end -}} + diff --git a/.changelog/note.tmpl b/.changelog/note.tmpl new file mode 100644 index 0000000000..7588c65fd4 --- /dev/null +++ b/.changelog/note.tmpl @@ -0,0 +1,3 @@ +{{- define "note" -}} +{{.Body}}{{if not (stringHasPrefix .Issue "_")}} [[GH-{{- .Issue -}}](https://github.com/hashicorp/consul-k8s/issues/{{- .Issue -}})]{{end}} +{{- end -}} diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index c371b63bdf..0000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,1393 +0,0 @@ -# Originally from consul-k8s -version: 2.1 -orbs: - slack: circleci/slack@3.4.2 -# reusable 'executor' object for jobs -executors: - go: - docker: - - image: docker.mirror.hashicorp.services/cimg/go:1.19.2 - environment: - TEST_RESULTS: /tmp/test-results # path to where test results are saved - -slack-channel: &slack-channel C0421KHNAV9 #feed-consul-k8s-ci channel ID -control-plane-path: &control-plane-path control-plane -cli-path: &cli-path cli -acceptance-mod-path: &acceptance-mod-path acceptance -acceptance-test-path: &acceptance-test-path acceptance/tests -acceptance-framework-path: &acceptance-framework-path acceptance/framework -helm-gen-path: &helm-gen-path hack/helm-reference-gen -gke-terraform-path: &gke-terraform-path charts/consul/test/terraform/gke -eks-terraform-path: &eks-terraform-path charts/consul/test/terraform/eks -aks-terraform-path: &aks-terraform-path charts/consul/test/terraform/aks -openshift-terraform-path: &openshift-terraform-path charts/consul/test/terraform/openshift -# This image is built from test/docker/Test.dockerfile -consul-helm-test-image: &consul-helm-test-image docker.mirror.hashicorp.services/hashicorpdev/consul-helm-test:0.15.0 -consul-test-image: &consul-test-image hashicorppreview/consul-enterprise:1.15-dev - -######################## -# COMMANDS -######################## -# Commands define a sequence of steps as a map to be executed and reused in jobs -commands: - install-prereqs: - steps: - - run: - name: Install go, gotestsum, kind, kubectl, and helm - command: | - wget https://golang.org/dl/go1.19.2.linux-amd64.tar.gz - sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.19.2.linux-amd64.tar.gz - rm go1.19.2.linux-amd64.tar.gz - echo 'export PATH=$PATH:/usr/local/go/bin' >> $BASH_ENV - - wget https://github.com/gotestyourself/gotestsum/releases/download/v1.8.2/gotestsum_1.8.2_linux_amd64.tar.gz - sudo tar -C /usr/local/bin -xzf gotestsum_1.8.2_linux_amd64.tar.gz - rm gotestsum_1.8.2_linux_amd64.tar.gz - - curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-linux-amd64 - chmod +x ./kind - sudo mv ./kind /usr/local/bin/kind - - curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" - chmod +x ./kubectl - sudo mv ./kubectl /usr/local/bin/kubectl - - wget https://get.helm.sh/helm-v3.9.4-linux-amd64.tar.gz - tar -zxvf helm-v3.9.4-linux-amd64.tar.gz - sudo mv linux-amd64/helm /usr/local/bin/helm - custom-checkout: - description: | - custom-checkout will perform a custom checkout procedure if provided with an alternative git reference, - otherwise it will run the CircleCI defined checkout command. This is needed as the CircleCI defined checkout - command does not support subsequent git actions after being called. - parameters: - git-ref: - type: string - default: "" - steps: - - when: - condition: << parameters.git-ref >> - steps: - - run: - name: Checkout code - command: | - ssh-keyscan github.com >> ~/.ssh/known_hosts - if [ -e '/home/circleci/project/.git' ] ; then - echo 'Fetching into existing repository' - existing_repo='true' - cd '/home/circleci/project' - git remote set-url origin "$CIRCLE_REPOSITORY_URL" || true - else - echo 'Cloning git repository' - existing_repo='false' - mkdir -p '/home/circleci/project' - cd '/home/circleci/project' - git clone --no-checkout "$CIRCLE_REPOSITORY_URL" . - fi - - if [ "$existing_repo" = 'true' ] || [ 'false' = 'true' ]; then - echo 'Fetching from remote repository' - git fetch --force origin - git fetch --force --tags origin - fi - - echo 'Checking out branch/tag' - git checkout --force "<< parameters.git-ref >>" - - unless: - condition: << parameters.git-ref >> - steps: - - checkout - create-kind-clusters: - parameters: - version: - type: string - steps: - - run: - name: Create kind clusters - command: | - kind create cluster --name dc1 --image kindest/node:<< parameters.version >> - kind create cluster --name dc2 --image kindest/node:<< parameters.version >> - create-kind-cni-clusters: - parameters: - version: - type: string - steps: - - run: - name: Create CNI kind clusters - command: | - kind create cluster --config=acceptance/framework/environment/cni-kind/kind.config --name dc1 --image kindest/node:<< parameters.version >> - make kind-cni-calico - kind create cluster --config=acceptance/framework/environment/cni-kind/kind.config --name dc2 --image kindest/node:<< parameters.version >> - make kind-cni-calico - build-cli: - steps: - - run: - name: Build consul-k8s CLI - working_directory: *cli-path - command: | - go build -o ./bin/consul-k8s - sudo cp ./bin/consul-k8s /usr/local/go/bin/ - consul-k8s version - - run-acceptance-tests: - description: | - Runs the Kind acceptance tests using a provided consul-k8s image, or else attempts to use the image referenced by the - branch name and git reference of the current git commit - parameters: - failfast: - type: boolean - default: false - additional-flags: - type: string - consul-k8s-image: - type: string - default: "docker.mirror.hashicorp.services/hashicorpdev/consul-k8s-control-plane:$(git rev-parse --short HEAD)" - go-path: - type: string - default: "/home/circleci/.go_workspace" - steps: - - when: - condition: << parameters.failfast >> - steps: - - run: - name: Run acceptance tests - working_directory: *acceptance-test-path - no_output_timeout: 2h - command: | - # Enterprise tests can't run on fork PRs because they require - # a secret. - if [ -z "$CIRCLE_PR_NUMBER" ]; then - ENABLE_ENTERPRISE=true - fi - - # We have to run the tests for each package separately so that we can - # exit early if any test fails (-failfast only works within a single - # package). - exit_code=0 - pkgs=$(go list ./... | circleci tests split --split-by=timings --timings-type=classname) - echo "Running $(echo $pkgs | wc -w) packages:" - echo $pkgs - for pkg in $pkgs - do - if ! gotestsum --no-summary=all --jsonfile=jsonfile-${pkg////-} -- $pkg -p 1 -timeout 2h -failfast \ - << parameters.additional-flags >> \ - -enable-multi-cluster \ - ${ENABLE_ENTERPRISE:+-enable-enterprise} \ - -debug-directory="$TEST_RESULTS/debug" \ - -consul-k8s-image=<< parameters.consul-k8s-image >> - then - echo "Tests in ${pkg} failed, aborting early" - exit_code=1 - break - fi - done - gotestsum --raw-command --junitfile "$TEST_RESULTS/gotestsum-report.xml" -- cat jsonfile* - exit $exit_code - - - unless: - condition: << parameters.failfast >> - steps: - - run: - name: Run acceptance tests - working_directory: *acceptance-test-path - no_output_timeout: 2h - command: | - # Enterprise tests can't run on fork PRs because they require - # a secret. - if [ -z "$CIRCLE_PR_NUMBER" ]; then - ENABLE_ENTERPRISE=true - fi - - pkgs=$(go list ./... | circleci tests split --split-by=timings --timings-type=classname) - echo "Running $pkgs" - gotestsum --junitfile "$TEST_RESULTS/gotestsum-report.xml" -- $pkgs -p 1 -timeout 2h -failfast \ - << parameters.additional-flags >> \ - ${ENABLE_ENTERPRISE:+-enable-enterprise} \ - -enable-multi-cluster \ - -debug-directory="$TEST_RESULTS/debug" \ - -consul-k8s-image=<< parameters.consul-k8s-image >> - -######################## -# JOBS -######################## -# Jobs are a collection of steps. These are used in the workflows to define -# what gets run in the pipeline -jobs: - go-fmt-and-vet-control-plane: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-k8s-modcache-v2-{{ checksum "control-plane/go.mod" }} - - - run: - name: go mod download - working_directory: *control-plane-path - command: go mod download - - # Save go module cache if the go.mod file has changed - - save_cache: - key: consul-k8s-modcache-v2-{{ checksum "control-plane/go.mod" }} - paths: - - "/home/circleci/go/pkg/mod" - - # check go fmt output because it does not report non-zero when there are fmt changes - - run: - name: check go fmt - working_directory: *control-plane-path - command: | - files=$(go fmt ./...) - if [ -n "$files" ]; then - echo "The following file(s) do not conform to go fmt:" - echo "$files" - exit 1 - fi - - run: cd control-plane && go vet ./... - - lint-control-plane: - executor: go - steps: - - checkout - - run: go get -u github.com/hashicorp/lint-consul-retry && lint-consul-retry - - run: - name: run lint - working_directory: *control-plane-path - command: go run hack/lint-api-new-client/main.go - - test-control-plane: - executor: go - environment: - TEST_RESULTS: /tmp/test-results - parallelism: 1 - steps: - - checkout - - run: mkdir -p $TEST_RESULTS - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-k8s-modcache-v2-{{ checksum "control-plane/go.mod" }} - - # run go tests with gotestsum - - run: - name: run go tests - working_directory: *control-plane-path - command: | - # download and install the consul binary - wget https://releases.hashicorp.com/consul/"${CONSUL_VERSION}"/consul_"${CONSUL_VERSION}"_linux_amd64.zip && \ - unzip consul_"${CONSUL_VERSION}"_linux_amd64.zip -d /home/circleci/bin && - rm consul_"${CONSUL_VERSION}"_linux_amd64.zip - PACKAGE_NAMES=$(go list ./...) - gotestsum --junitfile $TEST_RESULTS/gotestsum-report.xml -- -p 4 $PACKAGE_NAMES - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - test-enterprise-control-plane: - executor: go - environment: - TEST_RESULTS: /tmp/test-results - parallelism: 1 - steps: - - checkout - - run: mkdir -p $TEST_RESULTS - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-k8s-modcache-v2-{{ checksum "control-plane/go.mod" }} - - # run go tests with gotestsum - - run: - name: run enterprise go tests - working_directory: *control-plane-path - command: | - # download and install the consul binary - wget https://releases.hashicorp.com/consul/"${CONSUL_ENT_VERSION}"/consul_"${CONSUL_ENT_VERSION}"_linux_amd64.zip && \ - unzip consul_"${CONSUL_ENT_VERSION}"_linux_amd64.zip -d /home/circleci/bin && - rm consul_"${CONSUL_ENT_VERSION}"_linux_amd64.zip - PACKAGE_NAMES=$(go list ./...) - gotestsum --junitfile $TEST_RESULTS/gotestsum-report.xml -- -tags=enterprise -p 4 $PACKAGE_NAMES - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - build-distro: # defines a parameterized job - description: A job that will build the os/arch distro set by XC_OS and XC_ARCH - parameters: - OS: - description: What OSes to build - default: "" - type: string - ARCH: - description: What architectures to build - default: "" - type: string - executor: go - environment: - GOXPARALLEL: 2 # CircleCI containers are 2 CPU x 4GB RAM - steps: - - checkout - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-k8s-modcache-v2-{{ checksum "control-plane/go.mod" }} - - run: - name: build local - working_directory: *control-plane-path - command: XC_OS="<< parameters.OS >>" XC_ARCH="<< parameters.ARCH >>" ./build-support/scripts/build-local.sh - # persist to downstream job - - persist_to_workspace: - root: . - paths: - - control-plane/pkg/bin - - control-plane/cni/pkg/bin - # save dev build to CircleCI - - store_artifacts: - path: ./control-plane/pkg/bin - - store_artifacts: - path: ./control-plane/cni/pkg/bin - - # upload dev docker image - dev-upload-docker: - machine: - image: ubuntu-2004:202010-01 - environment: - DOCKER_CLI_EXPERIMENTAL: enabled - steps: - - checkout - # get consul-k8s binary - - attach_workspace: - at: . - - run: sudo apt-get update - - run: sudo apt-get install -y qemu-user-static - - run: docker buildx create --use - - run: - name: make ci.dev-docker - working_directory: *control-plane-path - command: make ci.dev-docker - - unit-cli: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-k8s-cli-modcache-v2-{{ checksum "cli/go.mod" }} - - - run: - name: go mod download - working_directory: *cli-path - command: go mod download - - # Save go module cache if the go.mod file has changed - - save_cache: - key: consul-k8s-cli-modcache-v2-{{ checksum "cli/go.mod" }} - paths: - - "/home/circleci/go/pkg/mod" - - - run: mkdir -p $TEST_RESULTS - - - run: - name: Run tests - working_directory: *cli-path - command: | - gotestsum --junitfile $TEST_RESULTS/gotestsum-report.xml ./... -- -p 4 - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - go-fmt-and-vet-acceptance: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: - name: go mod download - working_directory: *acceptance-mod-path - command: go mod download - - # Save go module cache if the go.mod file has changed - - save_cache: - key: consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - paths: - - "/home/circleci/go/pkg/mod" - - # check go fmt output because it does not report non-zero when there are fmt changes - - run: - name: check go fmt - working_directory: *acceptance-mod-path - command: | - files=$(go fmt ./...) - if [ -n "$files" ]; then - echo "The following file(s) do not conform to go fmt:" - echo "$files" - exit 1 - fi - - - run: - name: go vet - working_directory: *acceptance-mod-path - command: go vet ./... - - go-fmt-and-vet-helm-gen: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-helm-gen-modcache-v2-{{ checksum "charts/consul/hack/helm-reference-gen/go.mod" }} - - - run: - name: go mod download - working_directory: *helm-gen-path - command: go mod download - - # Save go module cache if the go.mod file has changed - - save_cache: - key: consul-helm-helm-gen-modcache-v2-{{ checksum "charts/consul/hack/helm-reference-gen/go.mod" }} - paths: - - "/home/circleci/go/pkg/mod" - - # check go fmt output because it does not report non-zero when there are fmt changes - - run: - name: check go fmt - working_directory: *helm-gen-path - command: | - files=$(go fmt ./...) - if [ -n "$files" ]; then - echo "The following file(s) do not conform to go fmt:" - echo "$files" - exit 1 - fi - - - run: - name: go vet - working_directory: *helm-gen-path - command: go vet ./... - - unit-acceptance-framework: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run: - name: Run tests - working_directory: *acceptance-framework-path - command: | - gotestsum --junitfile $TEST_RESULTS/gotestsum-report.xml ./... -- -p 4 - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - unit-helm-gen: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-helm-gen-modcache-v2-{{ checksum "charts/consul/hack/helm-reference-gen/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run: - name: Run tests - working_directory: *helm-gen-path - command: | - gotestsum --junitfile $TEST_RESULTS/gotestsum-report.xml ./... -- -p 4 - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - validate-helm-gen: - executor: go - steps: - - checkout - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-helm-gen-modcache-v2-{{ checksum "charts/consul/hack/helm-reference-gen/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run: - name: Validate helm gen - working_directory: *helm-gen-path - command: | - go run ./... -validate - - unit-test-helm-templates: - docker: - - image: *consul-helm-test-image - - steps: - - checkout - - - run: - name: Run Unit Tests - working_directory: charts/consul - command: bats --jobs 4 ./test/unit - - ########################### - # KIND ACCEPTANCE TEST JOBS - ########################### - acceptance: - environment: - - TEST_RESULTS: /tmp/test-results] - - CONSUL_TEST_IMAGE: *consul-test-image - machine: - image: ubuntu-2004:202010-01 - resource_class: xlarge - parallelism: 6 - steps: - - checkout - - install-prereqs - - create-kind-clusters: - version: "v1.26.0" - - restore_cache: - keys: - - consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - - run: - name: go mod download - working_directory: *acceptance-mod-path - command: go mod download - - save_cache: - key: consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - paths: - - ~/.go_workspace/pkg/mod - - build-cli - - run: mkdir -p $TEST_RESULTS - - run-acceptance-tests: - failfast: true - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -consul-image=$CONSUL_TEST_IMAGE - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - acceptance-tproxy: - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_TEST_IMAGE: *consul-test-image - machine: - image: ubuntu-2004:202010-01 - resource_class: xlarge - parallelism: 6 - steps: - - checkout - - install-prereqs - - create-kind-clusters: - version: "v1.26.0" - - restore_cache: - keys: - - consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - - run: - name: go mod download - working_directory: *acceptance-mod-path - command: go mod download - - save_cache: - key: consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - paths: - - ~/.go_workspace/pkg/mod - - build-cli - - run: mkdir -p $TEST_RESULTS - - run-acceptance-tests: - failfast: true - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - acceptance-tproxy-cni: - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_TEST_IMAGE: *consul-test-image - machine: - image: ubuntu-2004:202010-01 - resource_class: xlarge - parallelism: 3 - steps: - - checkout - - install-prereqs - - create-kind-cni-clusters: - version: "v1.25.3" - - restore_cache: - keys: - - consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - - run: - name: go mod download - working_directory: *acceptance-mod-path - command: go mod download - - save_cache: - key: consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - paths: - - ~/.go_workspace/pkg/mod - - build-cli - - run: mkdir -p $TEST_RESULTS - - run-acceptance-tests: - failfast: true - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -enable-transparent-proxy -enable-cni -consul-image=$CONSUL_TEST_IMAGE - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - ############################## - # CLEANUP CLOUD RESOURCES JOBS - ############################## - cleanup-gcp-resources: - docker: - - image: *consul-helm-test-image - steps: - - run: - name: cleanup leftover resources - command: | - echo "${GOOGLE_CREDENTIALS}" | gcloud auth activate-service-account --key-file=- - clusters=$(gcloud container clusters list --zone us-central1-a --project ${CLOUDSDK_CORE_PROJECT} --format json | jq -r '.[] | select(.name | test("^consul-k8s-\\d+$")) | .name') - for cluster in $clusters; do - echo "Deleting $cluster GKE cluster" - gcloud container clusters delete $cluster --zone us-central1-a --project ${CLOUDSDK_CORE_PROJECT} --quiet - done - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "GKE cleanup failed" - - cleanup-azure-resources: - docker: - - image: *consul-helm-test-image - steps: - - run: - name: cleanup leftover resources - command: | - az login --service-principal -u "$ARM_CLIENT_ID" -p "$ARM_CLIENT_SECRET" --tenant "$ARM_TENANT_ID" > /dev/null - resource_groups=$(az group list -o json | jq -r '.[] | select(.name | test("^consul-k8s-\\d+$")) | .name') - for group in $resource_groups; do - echo "Deleting $group resource group" - az group delete -n $group --yes - done - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "AKS cleanup failed" - - cleanup-eks-resources: - docker: - - image: *consul-helm-test-image - steps: - - checkout - - run: - name: cleanup eks resources - command: | - # Assume the role and set environment variables. - aws sts assume-role --role-arn "$AWS_ROLE_ARN" --role-session-name "consul-helm-$CIRCLE_BUILD_NUM" --duration-seconds 10800 > assume-role.json - export AWS_ACCESS_KEY_ID="$(jq -r .Credentials.AccessKeyId assume-role.json)" - export AWS_SECRET_ACCESS_KEY="$(jq -r .Credentials.SecretAccessKey assume-role.json)" - export AWS_SESSION_TOKEN="$(jq -r .Credentials.SessionToken assume-role.json)" - - make ci.aws-acceptance-test-cleanup - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "EKS cleanup failed" - - ############################# - # CLOUD ACCEPTANCE TEST JOBS - ############################# - acceptance-gke-1-25: - parallelism: 2 - environment: - - TEST_RESULTS: /tmp/test-results - - USE_GKE_GCLOUD_AUTH_PLUGIN: true - - CONSUL_TEST_IMAGE: *consul-test-image - docker: - - image: *consul-helm-test-image - - steps: - - run: - name: Exit if forked PR - command: | - if [ -n "$CIRCLE_PR_NUMBER" ]; then - echo "Skipping acceptance tests for forked PRs; marking step successful." - circleci step halt - fi - - - checkout - - - build-cli - - run: - name: terraform init & apply - working_directory: *gke-terraform-path - command: | - terraform init - echo "${GOOGLE_CREDENTIALS}" | gcloud auth activate-service-account --key-file=- - - # On GKE, we're setting the build number instead of build URL because label values - # cannot contain '/'. - terraform apply \ - -var project=${CLOUDSDK_CORE_PROJECT} \ - -var init_cli=true \ - -var cluster_count=2 \ - -var labels="{\"build_number\": \"$CIRCLE_BUILD_NUM\"}" \ - -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *gke-terraform-path - command: | - terraform destroy -var project=${CLOUDSDK_CORE_PROJECT} -auto-approve - when: always - - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "GKE acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-gke-cni-1-25: - parallelism: 2 - environment: - - TEST_RESULTS: /tmp/test-results - - USE_GKE_GCLOUD_AUTH_PLUGIN: true - - CONSUL_TEST_IMAGE: *consul-test-image - docker: - - image: *consul-helm-test-image - - steps: - - run: - name: Exit if forked PR - command: | - if [ -n "$CIRCLE_PR_NUMBER" ]; then - echo "Skipping acceptance tests for forked PRs; marking step successful." - circleci step halt - fi - - - checkout - - - build-cli - - run: - name: terraform init & apply - working_directory: *gke-terraform-path - command: | - terraform init - echo "${GOOGLE_CREDENTIALS}" | gcloud auth activate-service-account --key-file=- - - # On GKE, we're setting the build number instead of build URL because label values - # cannot contain '/'. - terraform apply \ - -var project=${CLOUDSDK_CORE_PROJECT} \ - -var init_cli=true \ - -var cluster_count=2 \ - -var labels="{\"build_number\": \"$CIRCLE_BUILD_NUM\"}" \ - -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -use-gke -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *gke-terraform-path - command: | - terraform destroy -var project=${CLOUDSDK_CORE_PROJECT} -auto-approve - when: always - - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "GKE CNI acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-aks-1-24: - parallelism: 3 - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_TEST_IMAGE: *consul-test-image - docker: - - image: *consul-helm-test-image - - steps: - - checkout - - - build-cli - - run: - name: terraform init & apply - working_directory: *aks-terraform-path - command: | - terraform init - - terraform apply \ - -var client_id="$ARM_CLIENT_ID" \ - -var client_secret="$ARM_CLIENT_SECRET" \ - -var cluster_count=2 \ - -var tags="{\"build_url\": \"$CIRCLE_BUILD_URL\"}" \ - -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -use-aks -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *aks-terraform-path - command: | - terraform destroy -auto-approve - when: always - - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "AKS acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-aks-cni-1-24: - parallelism: 3 - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_TEST_IMAGE: *consul-test-image - docker: - - image: *consul-helm-test-image - - steps: - - checkout - - - build-cli - - run: - name: terraform init & apply - working_directory: *aks-terraform-path - command: | - terraform init - - terraform apply \ - -var client_id="$ARM_CLIENT_ID" \ - -var client_secret="$ARM_CLIENT_SECRET" \ - -var cluster_count=2 \ - -var tags="{\"build_url\": \"$CIRCLE_BUILD_URL\"}" \ - -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -use-aks -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *aks-terraform-path - command: | - terraform destroy -auto-approve - when: always - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "AKS CNI acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-eks-1-23: - parallelism: 3 - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_TEST_IMAGE: *consul-test-image - docker: - - image: *consul-helm-test-image - - steps: - - checkout - - - build-cli - - run: - name: configure aws - command: | - aws configure --profile helm_user set aws_access_key_id "$AWS_ACCESS_KEY_ID" - aws configure --profile helm_user set aws_secret_access_key "$AWS_SECRET_ACCESS_KEY" - aws configure set role_arn "$AWS_ROLE_ARN" - aws configure set source_profile helm_user - - echo "unset AWS_ACCESS_KEY_ID" >> $BASH_ENV - echo "unset AWS_SECRET_ACCESS_KEY" >> $BASH_ENV - - - run: - name: terraform init & apply - working_directory: *eks-terraform-path - command: | - terraform init - - terraform apply -var cluster_count=2 -var tags="{\"build_url\": \"$CIRCLE_BUILD_URL\"}" -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *eks-terraform-path - command: | - terraform destroy -var cluster_count=2 -auto-approve - when: always - - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "EKS acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-eks-cni-1-23: - parallelism: 3 - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_TEST_IMAGE: *consul-test-image - docker: - - image: *consul-helm-test-image - - steps: - - checkout - - - build-cli - - run: - name: configure aws - command: | - aws configure --profile helm_user set aws_access_key_id "$AWS_ACCESS_KEY_ID" - aws configure --profile helm_user set aws_secret_access_key "$AWS_SECRET_ACCESS_KEY" - aws configure set role_arn "$AWS_ROLE_ARN" - aws configure set source_profile helm_user - - echo "unset AWS_ACCESS_KEY_ID" >> $BASH_ENV - echo "unset AWS_SECRET_ACCESS_KEY" >> $BASH_ENV - - - run: - name: terraform init & apply - working_directory: *eks-terraform-path - command: | - terraform init - - terraform apply -var cluster_count=2 -var tags="{\"build_url\": \"$CIRCLE_BUILD_URL\"}" -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-transparent-proxy -enable-cni -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *eks-terraform-path - command: | - terraform destroy -var cluster_count=2 -auto-approve - when: always - - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "EKS CNI acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-openshift: - environment: - TEST_RESULTS: /tmp/test-results - CONSUL_TEST_IMAGE: *consul-test-image - parallelism: 1 - docker: - - image: *consul-helm-test-image - - steps: - - checkout - - build-cli - - run: - name: terraform init & apply - working_directory: *openshift-terraform-path - command: | - terraform init - az login --service-principal -u "$ARM_CLIENT_ID" -p "$ARM_CLIENT_SECRET" --tenant "$ARM_TENANT_ID" > /dev/null - terraform apply \ - -var cluster_count=2 \ - -var tags="{\"build_url\": \"$CIRCLE_BUILD_URL\"}" \ - -auto-approve - - primary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[0]) - secondary_kubeconfig=$(terraform output -json | jq -r .kubeconfigs.value[1]) - - echo "export primary_kubeconfig=$primary_kubeconfig" >> $BASH_ENV - echo "export secondary_kubeconfig=$secondary_kubeconfig" >> $BASH_ENV - - # Restore go module cache if there is one - - restore_cache: - keys: - - consul-helm-acceptance-modcache-v2-{{ checksum "acceptance/go.mod" }} - - - run: mkdir -p $TEST_RESULTS - - - run-acceptance-tests: - additional-flags: -kubeconfig="$primary_kubeconfig" -secondary-kubeconfig="$secondary_kubeconfig" -enable-openshift -enable-transparent-proxy -consul-image=$CONSUL_TEST_IMAGE - - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - - run: - name: terraform destroy - working_directory: *openshift-terraform-path - command: | - terraform destroy -auto-approve - when: always - - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "OpenShift acceptance tests failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-kind-1-23-consul-compat-nightly-1-12: - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_IMAGE: "docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.12-dev" - - ENVOY_IMAGE: "envoyproxy/envoy:v1.22.2" - - HELM_CHART_VERSION: "0.49.0" - - CONSUL_K8S_IMAGE: "docker.mirror.hashicorp.services/hashicorp/consul-k8s-control-plane:0.49.0" - machine: - image: ubuntu-2004:202010-01 - resource_class: xlarge - steps: - - custom-checkout: - git-ref: "v$HELM_CHART_VERSION" - - install-prereqs - - create-kind-clusters: - version: "v1.23.0" - - restore_cache: - keys: - - consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - - run: - name: go mod download - working_directory: *acceptance-mod-path - command: go mod download - - save_cache: - key: consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - paths: - - ~/.go_workspace/pkg/mod - - build-cli - - run: mkdir -p $TEST_RESULTS - - run-acceptance-tests: - consul-k8s-image: $CONSUL_K8S_IMAGE - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -consul-image=$CONSUL_IMAGE -consul-version="1.12" -envoy-image=$ENVOY_IMAGE -helm-chart-version=$HELM_CHART_VERSION -enable-transparent-proxy - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "Acceptance tests against Kind with Kubernetes v1.25 with Consul 1.12 nightly failed. Check the logs at: ${CIRCLE_BUILD_URL}" - - acceptance-kind-1-23-consul-compat-nightly-1-13: - environment: - - TEST_RESULTS: /tmp/test-results - - CONSUL_IMAGE: "docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.13-dev" - - ENVOY_IMAGE: "envoyproxy/envoy:v1.23.1" - - CONSUL_K8S_IMAGE: "docker.mirror.hashicorp.services/hashicorp/consul-k8s-control-plane:0.49.0" - - HELM_CHART_VERSION: "0.49.0" - machine: - image: ubuntu-2004:202010-01 - resource_class: xlarge - steps: - - custom-checkout: - git-ref: "v$HELM_CHART_VERSION" - - install-prereqs - - create-kind-clusters: - version: "v1.23.0" - - restore_cache: - keys: - - consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - - run: - name: go mod download - working_directory: *acceptance-mod-path - command: go mod download - - save_cache: - key: consul-helm-modcache-v2-{{ checksum "acceptance/go.mod" }} - paths: - - ~/.go_workspace/pkg/mod - - build-cli - - run: mkdir -p $TEST_RESULTS - - run-acceptance-tests: - consul-k8s-image: $CONSUL_K8S_IMAGE - additional-flags: -use-kind -kubecontext="kind-dc1" -secondary-kubecontext="kind-dc2" -consul-image=$CONSUL_IMAGE -consul-version="1.13" -envoy-image=$ENVOY_IMAGE -helm-chart-version=$HELM_CHART_VERSION -enable-transparent-proxy - - store_test_results: - path: /tmp/test-results - - store_artifacts: - path: /tmp/test-results - - slack/status: - channel: *slack-channel - fail_only: true - failure_message: "Acceptance tests against Kind with Kubernetes v1.25 with Consul 1.13 nightly failed. Check the logs at: ${CIRCLE_BUILD_URL}" - -######################## -# WORKFLOWS -######################## -# Workflows are a set of rules for defining a collection of jobs and their run order. -# This is where the pipeline tests and builds are constructed and triggers for running these tests -# are defined -workflows: - version: 2 - test-and-build: - jobs: - # Build this one control-plane binary so that acceptance and acceptance-tproxy will run - # The rest of these CircleCI jobs have been migrated to GitHub Actions. We need to wait until - # there is support for a larger pool of runners before the acceptance tests can - # be moved - # Run acceptance tests using the docker image built for the control plane for this particular - # branch - # This is run on every PR - - build-distro: - OS: "linux" - ARCH: "amd64 arm64" - name: build-distros-linux - - dev-upload-docker: - context: consul-ci - requires: - - build-distros-linux - - acceptance: - context: consul-ci - requires: - - dev-upload-docker - - acceptance-tproxy-cni: - context: consul-ci - requires: - - dev-upload-docker - - acceptance-tproxy: - context: consul-ci - requires: - - dev-upload-docker - - - nightly-cleanup: - triggers: - - schedule: - cron: "0 12 * * *" # Run at 12 pm UTC (5 am PST) - filters: - branches: - only: - - main - jobs: - - cleanup-gcp-resources - - cleanup-azure-resources - - cleanup-eks-resources - - nightly-acceptance-tests-release: - description: | - Tests which run on a release branch nightly. These exist separate from the main - acceptance tests so that they can run at their own cadence, but - contains the same sequence of jobs. - triggers: - - schedule: - cron: "0 0 * * *" # Run at 12 am UTC (5 pm PST) - filters: - branches: - only: - - release/0.49.x - - release/1.0.x - jobs: - - build-distro: - OS: "linux" - ARCH: "amd64 arm64" - name: build-distros-linux - - dev-upload-docker: - requires: - - build-distros-linux - # Disable until we can use UBI images. - # - acceptance-openshift - - acceptance-gke-1-25: - requires: - - dev-upload-docker - - acceptance-gke-cni-1-25: - requires: - - acceptance-gke-1-25 - - acceptance-tproxy: - requires: - - dev-upload-docker - - nightly-acceptance-tests-main: - description: | - Tests which run on the main branch nightly. These exist separate from the release - acceptance tests so that they can run at their own cadence, but - contains the same sequence of jobs. - triggers: - - schedule: - cron: "0 0 * * *" # Run at 12 am UTC (5 pm PST) - filters: - branches: - only: - - main - jobs: - - build-distro: - OS: "linux" - ARCH: "amd64 arm64" - name: build-distros-linux - - dev-upload-docker: - requires: - - build-distros-linux - # Disable until we can use UBI images. - # - acceptance-openshift - - acceptance-gke-1-25: - requires: - - dev-upload-docker - - acceptance-gke-cni-1-25: - requires: - - acceptance-gke-1-25 - - acceptance-eks-1-23: - requires: - - dev-upload-docker - - acceptance-eks-cni-1-23: - requires: - - acceptance-eks-1-23 - - acceptance-aks-1-24: - requires: - - dev-upload-docker - - acceptance-aks-cni-1-24: - requires: - - acceptance-aks-1-24 - - acceptance-tproxy: - requires: - - dev-upload-docker - - nightly-kind-acceptance-tests-consul-compatability: - description: | - Acceptance tests which run nightly to verify the compatibility between - a consul-k8s binary and it's consul version pair. Tests will be conducted - for up to n-2 previous Consul-k8s releases. - triggers: - - schedule: - cron: "0 0 * * *" # Run at 12 am UTC (5 pm PST) - filters: - branches: - only: - - main - jobs: - - acceptance-kind-1-23-consul-compat-nightly-1-12 - - acceptance-kind-1-23-consul-compat-nightly-1-13 diff --git a/.github/workflows/backport-checker.yml b/.github/workflows/backport-checker.yml new file mode 100644 index 0000000000..5bcac5a38e --- /dev/null +++ b/.github/workflows/backport-checker.yml @@ -0,0 +1,32 @@ +# This workflow checks that there is either a 'pr/no-backport' label applied to a PR +# or there is a backport/.txt file associated with a PR for a backport label + +name: Backport Checker + +on: + pull_request: + types: [opened, synchronize, labeled] + # Runs on PRs to main and all release branches + branches: + - main + - release/* + +jobs: + # checks that a backport label is present for a PR + backport-check: + # If there's a `pr/no-backport` label we ignore this check. Also, we ignore PRs created by the bot assigned to `backport-assistant` + if: "! ( contains(github.event.pull_request.labels.*.name, 'pr/no-backport') || github.event.pull_request.user.login == 'hc-github-team-consul-core' )" + runs-on: ubuntu-latest + + steps: + - name: Check for Backport Label + run: | + labels="${{join(github.event.pull_request.labels.*.name, ', ') }}" + if [[ "$labels" =~ .*"backport/".* ]]; then + echo "Found backport label!" + exit 0 + fi + # Fail status check when no backport label was found on the PR + echo "Did not find a backport label matching the pattern 'backport/*' and the 'pr/no-backport' label was not applied. Reference - https://github.com/hashicorp/consul-k8s/pull/1982" + exit 1 + diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 9c241d9ada..31136b5aa3 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -11,10 +11,10 @@ jobs: backport: if: github.event.pull_request.merged runs-on: ubuntu-latest - container: hashicorpdev/backport-assistant:0.2.5 + container: hashicorpdev/backport-assistant:0.3.3 steps: - name: Run Backport Assistant - run: backport-assistant backport -automerge + run: backport-assistant backport -merge-method=squash -gh-automerge env: BACKPORT_LABEL_REGEXP: "backport/(?P\\d+\\.\\d+\\.x)" BACKPORT_TARGET_TEMPLATE: "release/{{.target}}" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9ce46f1704..4fd9ae3cfd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -162,7 +162,7 @@ jobs: if: ${{ matrix.goos == 'linux' && matrix.component == 'cli' && matrix.goarch == 'amd64'}} uses: addnab/docker-run-action@v3 with: - image: registry.access.redhat.com/ubi8/ubi:latest + image: registry.access.redhat.com/ubi9/ubi:latest options: -v ${{ github.workspace }}:/work run: | dnf install -qy openssl diff --git a/.github/workflows/changelog-checker.yml b/.github/workflows/changelog-checker.yml new file mode 100644 index 0000000000..d40bdfbd6a --- /dev/null +++ b/.github/workflows/changelog-checker.yml @@ -0,0 +1,46 @@ +# This workflow checks that there is either a 'pr/no-changelog' label applied to a PR +# or there is a .changelog/.txt file associated with a PR for a changelog entry + +name: Changelog Checker + +on: + pull_request: + types: [opened, synchronize, labeled] + # Runs on PRs to main and all release branches + branches: + - main + - release/* + +jobs: + # checks that a .changelog entry is present for a PR + changelog-check: + # If there's a `pr/no-changelog` label we ignore this check. Also, we ignore PRs created by the bot assigned to `backport-assistant` + if: "! ( contains(github.event.pull_request.labels.*.name, 'pr/no-changelog') || github.event.pull_request.user.login == 'hc-github-team-consul-core' )" + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 # by default the checkout action doesn't checkout all branches + - name: Check for changelog entry in diff + run: | + # check if there is a diff in the .changelog directory + # for PRs against the main branch, the changelog file name should match the PR number + if [ "${{ github.event.pull_request.base.ref }}" = "${{ github.event.repository.default_branch }}" ]; then + enforce_matching_pull_request_number="matching this PR number " + changelog_file_path=".changelog/(_)?${{ github.event.pull_request.number }}.txt" + else + changelog_file_path=".changelog/[_0-9]*.txt" + fi + + changelog_files=$(git --no-pager diff --name-only HEAD "$(git merge-base HEAD "origin/${{ github.event.pull_request.base.ref }}")" | egrep -e "${changelog_file_path}") + + # If we do not find a file in .changelog/, we fail the check + if [ -z "$changelog_files" ]; then + # Fail status check when no .changelog entry was found on the PR + echo "Did not find a .changelog entry ${enforce_matching_pull_request_number}and the 'pr/no-changelog' label was not applied. Reference - https://github.com/hashicorp/consul-k8s/pull/1947" + exit 1 + else + echo "Found .changelog entry in PR!" + fi diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml new file mode 100644 index 0000000000..2af1e46532 --- /dev/null +++ b/.github/workflows/merge.yml @@ -0,0 +1,30 @@ +# Dispatch to the consul-k8s-workflows when a PR is created and on merges to main/release* +name: merge +on: + push: + # Sequence of patterns matched against refs/heads + branches: + # Push events on main branch + - main + # Push events to branches matching refs/heads/release/** + - "release/**" + +# these should be the only settings that you will ever need to change +env: + BRANCH: ${{ github.head_ref || github.ref_name }} + CONTEXT: "merge" + SHA: ${{ github.event.pull_request.head.sha || github.sha }} + +jobs: + test: + name: test + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@v1.2.2 + name: test + with: + workflow: test.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ env.SHA }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.github/workflows/nightly-acceptance.yml b/.github/workflows/nightly-acceptance.yml new file mode 100644 index 0000000000..18a8ba958d --- /dev/null +++ b/.github/workflows/nightly-acceptance.yml @@ -0,0 +1,26 @@ +# Dispatch to the consul-k8s-workflows with a nightly cron +name: nightly-acceptance +on: + schedule: + # * is a special character in YAML so you have to quote this string + # Run nightly at 12AM UTC/8PM EST/5PM PST + - cron: '0 0 * * *' + +# these should be the only settings that you will ever need to change +env: + BRANCH: ${{ github.ref_name }} + CONTEXT: "nightly" + +jobs: + cloud: + name: cloud + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@v1.2.2 + name: cloud + with: + workflow: cloud.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000000..65f924e6d0 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,24 @@ +# Dispatch to the consul-k8s-workflows when a PR is created and on merges to main/release* +name: pr +on: + pull_request: + +# these should be the only settings that you will ever need to change +env: + BRANCH: ${{ github.head_ref || github.ref_name }} + CONTEXT: "pr" + SHA: ${{ github.event.pull_request.head.sha || github.sha }} + +jobs: + test: + name: test + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@v1.2.2 + name: test + with: + workflow: test.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ env.SHA }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.github/workflows/reusable-acceptance.yml b/.github/workflows/reusable-acceptance.yml deleted file mode 100644 index 142754e41a..0000000000 --- a/.github/workflows/reusable-acceptance.yml +++ /dev/null @@ -1,157 +0,0 @@ -name: reusable-acceptance - -on: - workflow_call: - inputs: - name: - required: true - type: string - additional-flags: - required: false - type: string - default: "" - consul-k8s-image: - required: false - type: string - directory: - required: true - type: string - go-version: - required: true - type: string - gotestsum-version: - required: true - type: string - kind-version: - required: false - type: string - default: "v1.24.6" - checkout-ref: - required: false - type: string - default: ${{ github.sha }} - secrets: - CONSUL_ENT_LICENSE: - required: true - VAULT_LICENSE: - required: true - -# Environment variables can only be used at the step level -env: - TEST_RESULTS: /tmp/test-results # path to where test results are saved - CONSUL_ENT_LICENSE: ${{ secrets.CONSUL_ENT_LICENSE }} - VAULT_LICENSE: ${{ secrets.VAULT_LICENSE }} - CONSUL_K8S_IMAGE: ${{ inputs.consul-k8s-image }} - -jobs: - job: - runs-on: [custom, linux, xl] - strategy: - matrix: - include: - - {runner: "0", test-packages: "basic consul-dns metrics"} - - {runner: "1", test-packages: "connect"} - - {runner: "2", test-packages: "controller example"} - - {runner: "3", test-packages: "ingress-gateway"} - - {runner: "4", test-packages: "partitions"} - - {runner: "5", test-packages: "peering"} - - {runner: "6", test-packages: "snapshot-agent vault wan-federation"} - - {runner: "7", test-packages: "cli sync terminating-gateway"} - - fail-fast: false - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - ref: ${{ inputs.checkout-ref }} - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ inputs.go-version }} - - - name: Setup go mod cache - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Install pre-requisites # Install gotestsum, kind, kubectl, and helm - run: | - wget https://github.com/gotestyourself/gotestsum/releases/download/v1.6.4/gotestsum_1.6.4_linux_amd64.tar.gz - sudo tar -C /usr/local/bin -xzf gotestsum_1.6.4_linux_amd64.tar.gz - rm gotestsum_1.6.4_linux_amd64.tar.gz - - curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.15.0/kind-linux-amd64 - chmod +x ./kind - sudo mv ./kind /usr/local/bin/kind - - curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" - chmod +x ./kubectl - sudo mv ./kubectl /usr/local/bin/kubectl - - wget https://get.helm.sh/helm-v3.9.4-linux-amd64.tar.gz - tar -zxvf helm-v3.9.4-linux-amd64.tar.gz - sudo mv linux-amd64/helm /usr/local/bin/helm - - - run: mkdir -p ${{ env.TEST_RESULTS }} - - - name: go mod download - working-directory: ${{ inputs.directory }} - run: go mod download - - - name: Create kind clusters - run: | - kind create cluster --name dc1 --image kindest/node:${{ inputs.kind-version }} - kind create cluster --name dc2 --image kindest/node:${{ inputs.kind-version }} - - - name: Build CLI - run: | - sudo make cli-dev - consul-k8s version - - # We have to run the tests for each package separately so that we can - # exit early if any test fails (-failfast only works within a single - # package). - - name: Run acceptance tests ${{ matrix.runner }} - working-directory: ${{ inputs.directory }} - if: github.repository_owner == 'hashicorp' # This prevents running on forks - run: | - exit_code=0 - echo "Running packages: ${{ matrix.test-packages }}" - for pkg in $(echo ${{ matrix.test-packages }}) - do - fullpkg="github.com/hashicorp/consul-k8s/${{ inputs.directory }}/${pkg}" - echo "Testing package: ${fullpkg}" - if ! gotestsum --jsonfile=jsonfile-${pkg////-} -- ${fullpkg} -p 1 -timeout 2h -failfast \ - ${{ inputs.additional-flags }} \ - -enable-enterprise \ - -enable-multi-cluster \ - -debug-directory=${{ env.TEST_RESULTS }}/debug \ - -consul-k8s-image=${{ inputs.consul-k8s-image }} - then - echo "Tests in ${pkg} failed, aborting early" - exit_code=1 - break - fi - done - gotestsum --raw-command --junitfile "${{ env.TEST_RESULTS }}/gotestsum-report.xml" -- cat jsonfile* - exit $exit_code - - - name: Upload tests - if: always() - uses: actions/upload-artifact@v3 - with: - name: ${{ inputs.name }}-${{ matrix.test-packages }}-gotestsum-report.xml - path: ${{ env.TEST_RESULTS }}/gotestsum-report.xml - - - name: Upload debug (on failure) - if: failure() - uses: actions/upload-artifact@v3 - with: - name: ${{ inputs.name }}-${{ matrix.test-packages }}-debug-info - path: ${{ env.TEST_RESULTS }}/debug diff --git a/.github/workflows/reusable-golangci-lint.yml b/.github/workflows/reusable-golangci-lint.yml deleted file mode 100644 index 30b6a0a3b3..0000000000 --- a/.github/workflows/reusable-golangci-lint.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: golangci-lint - -on: - workflow_call: - inputs: - directory: - required: true - type: string - go-version: - required: true - type: string - args: - required: false - type: string - -jobs: - job: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ inputs.go-version }} - - - name: golangci-lint-${{inputs.directory}} - uses: golangci/golangci-lint-action@v3.4.0 - with: - version: v1.51 - working-directory: ${{inputs.directory}} - args: ${{inputs.args}} diff --git a/.github/workflows/reusable-unit.yml b/.github/workflows/reusable-unit.yml deleted file mode 100644 index fa2b4130f5..0000000000 --- a/.github/workflows/reusable-unit.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: reusable-unit - -on: - workflow_call: - inputs: - directory: - required: true - type: string - go-version: - required: true - type: string - -# Environment variables can only be used at the step level -env: - TEST_RESULTS: /tmp/test-results # path to where test results are saved - GOTESTSUM_VERSION: 1.8.2 - -jobs: - job: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{inputs.go-version}} - - - name: Setup go mod cache - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Install gotestsum - run: | - wget https://github.com/gotestyourself/gotestsum/releases/download/v${{env.GOTESTSUM_VERSION}}/gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - sudo tar -C /usr/local/bin -xzf gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - rm gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - - - run: mkdir -p ${{env.TEST_RESULTS}} - - - name: go mod download - working-directory: ${{inputs.directory}} - run: go mod download - - - name: Run tests - working-directory: ${{inputs.directory}} - run: | - gotestsum --junitfile ${{env.TEST_RESULTS}}/gotestsum-report.xml ./... -- -p 4 - diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 3967d1c816..0000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,399 +0,0 @@ -name: test -on: - push: - -env: - TEST_RESULTS: /tmp/test-results # path to where test results are saved - GOTESTSUM_VERSION: 1.8.2 # You cannot use environment variables with workflows. The gotestsum version is hardcoded in the reusable workflows too. - # We use docker images to copy the consul binary for unit tests. - CONSUL_OSS_DOCKER_IMAGE: hashicorppreview/consul:1.14-dev # Consul's OSS version to use in tests - CONSUL_ENT_DOCKER_IMAGE: hashicorppreview/consul-enterprise:1.14-dev # Consul's enterprise version to use in tests - -jobs: - terraform-fmt-check: - name: "Terraform format check" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup Terraform - uses: hashicorp/setup-terraform@v2 - with: - terraform_version: TERRAFORM_VERSION - terraform_wrapper: false - - name: Run Terraform checks - run: | - make terraform-fmt-check TERRAFORM_DIR="${{ github.workspace }}" - - get-go-version: - name: "Determine Go toolchain version" - runs-on: ubuntu-latest - outputs: - go-version: ${{ steps.get-go-version.outputs.go-version }} - steps: - - uses: actions/checkout@v3 - - name: Determine Go version - id: get-go-version - # We use .go-version as our source of truth for current Go - # version, because "goenv" can react to it automatically. - run: | - echo "Building with Go $(cat .go-version)" - echo "::set-output name=go-version::$(cat .go-version)" - - get-product-version: - runs-on: ubuntu-latest - outputs: - product-version: ${{ steps.get-product-version.outputs.product-version }} - steps: - - uses: actions/checkout@v3 - - name: get product version - id: get-product-version - run: | - make version - echo "::set-output name=product-version::$(make version)" - - validate-helm-gen: - needs: - - get-go-version - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ needs.get-go-version.outputs.go-version }} - - - name: Setup go mod cache - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Validate helm gen - working-directory: hack/helm-reference-gen - run: | - go run ./... -validate - - golangci-lint-helm-gen: - needs: - - get-go-version - uses: hashicorp/consul-k8s/.github/workflows/reusable-golangci-lint.yml@main - with: - directory: hack/helm-reference-gen - go-version: ${{ needs.get-go-version.outputs.go-version }} - #TODO: This is a workaround in order to get pipelines working. godot and staticcheck fail for helm-reference-gen - args: "--no-config --disable-all --enable gofmt,govet" - - unit-helm-gen: - needs: [get-go-version, golangci-lint-helm-gen, validate-helm-gen] - uses: hashicorp/consul-k8s/.github/workflows/reusable-unit.yml@main - with: - directory: hack/helm-reference-gen - go-version: ${{ needs.get-go-version.outputs.go-version }} - - unit-test-helm-templates: - needs: - - unit-helm-gen - runs-on: ubuntu-latest - container: - image: docker.mirror.hashicorp.services/hashicorpdev/consul-helm-test:0.15.0 - options: --user 1001 - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Run Unit Tests - working-directory: charts/consul - run: bats --jobs 4 ./test/unit - - lint-control-plane: - needs: - - get-go-version - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ needs.get-go-version.outputs.go-version }} - - - run: go install github.com/hashicorp/lint-consul-retry@master && lint-consul-retry - - - name: Run lint - working-directory: control-plane - run: go run hack/lint-api-new-client/main.go - - golangci-lint-control-plane: - needs: - - get-go-version - uses: hashicorp/consul-k8s/.github/workflows/reusable-golangci-lint.yml@main - with: - directory: control-plane - go-version: ${{ needs.get-go-version.outputs.go-version }} - - test-control-plane: - needs: [get-go-version, lint-control-plane, golangci-lint-control-plane] - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ needs.get-go-version.outputs.go-version }} - - - name: Setup go mod cache - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Install gotestsum - run: | - wget https://github.com/gotestyourself/gotestsum/releases/download/v${{env.GOTESTSUM_VERSION}}/gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - sudo tar -C /usr/local/bin -xzf gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - rm gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - - - run: mkdir -p ${{env.TEST_RESULTS}} - - run: echo "$HOME/bin" >> $GITHUB_PATH - - - name: Download consul - working-directory: control-plane - run: | - mkdir -p $HOME/bin - container_id=$(docker create ${{env.CONSUL_OSS_DOCKER_IMAGE}}) - docker cp "$container_id:/bin/consul" $HOME/bin/consul - docker rm "$container_id" - - name: Run go tests - working-directory: control-plane - run: | - PACKAGE_NAMES=$(go list ./...) - gotestsum --junitfile ${{env.TEST_RESULTS}}/gotestsum-report.xml -- -p 4 $PACKAGE_NAMES - - test-enterprise-control-plane: - if: github.repository_owner == 'hashicorp' # Do not run on forks as this requires secrets - needs: [get-go-version, lint-control-plane, golangci-lint-control-plane] - runs-on: ubuntu-latest - env: - CONSUL_LICENSE: ${{secrets.CONSUL_LICENSE}} - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ needs.get-go-version.outputs.go-version }} - - - name: Setup go mod cache - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Install gotestsum - run: | - wget https://github.com/gotestyourself/gotestsum/releases/download/v${{env.GOTESTSUM_VERSION}}/gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - sudo tar -C /usr/local/bin -xzf gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - rm gotestsum_${{env.GOTESTSUM_VERSION}}_linux_amd64.tar.gz - - - run: mkdir -p ${{env.TEST_RESULTS}} - - run: echo "$HOME/bin" >> $GITHUB_PATH - - - name: Download consul - working-directory: control-plane - run: | - mkdir -p $HOME/bin - container_id=$(docker create ${{env.CONSUL_ENT_DOCKER_IMAGE}}) - docker cp "$container_id:/bin/consul" $HOME/bin/consul - docker rm "$container_id" - - - name: Run go tests - working-directory: control-plane - run: | - PACKAGE_NAMES=$(go list ./...) - gotestsum --junitfile ${{env.TEST_RESULTS}}/gotestsum-report.xml -- -tags=enterprise -p 4 $PACKAGE_NAMES - - build-distros: - needs: [get-go-version, get-product-version] - runs-on: ubuntu-latest - strategy: - matrix: - include: - # cli - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "cli", pkg_name: "consul-k8s", "bin_name": "consul-k8s" } - # control-plane - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane", pkg_name: "consul-k8s-control-plane", "bin_name": "consul-k8s-control-plane" } - # consul-cni - - {go: "${{ needs.get-go-version.outputs.go-version }}", goos: "linux", goarch: "amd64", component: "control-plane/cni", pkg_name: "consul-cni", "bin_name": "consul-cni" } - - fail-fast: true - - name: Go ${{ matrix.go }} ${{ matrix.goos }} ${{ matrix.goarch }} ${{ matrix.component }} build - steps: - - uses: actions/checkout@v3 - - - name: Setup go - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.go }} - - - name: Build - env: - GOOS: ${{ matrix.goos }} - GOARCH: ${{ matrix.goarch }} - CGO_ENABLED: 0 - working-directory: ${{ matrix.component }} - run: | - mkdir -p dist out - - export GIT_COMMIT=$(git rev-parse --short HEAD) - export GIT_DIRTY=$(test -n "$(git status --porcelain)" && echo "+CHANGES") - export GIT_IMPORT=github.com/hashicorp/consul-k8s/${{ matrix.component }}/version - export GOLDFLAGS="-X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}${GIT_DIRTY} -X ${GIT_IMPORT}.GitDescribe=${{ needs.get-product-version.outputs.product-version }}" - - CGO_ENABLED=0 go build -o dist/${{ matrix.bin_name }} -ldflags "${GOLDFLAGS}" . - zip -r -j out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip dist/ - - - name: Upload built binaries - uses: actions/upload-artifact@v3 - with: - name: ${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip - path: ${{ matrix.component}}/out/${{ matrix.pkg_name }}_${{ needs.get-product-version.outputs.product-version }}_${{ matrix.goos }}_${{ matrix.goarch }}.zip - - golangci-lint-acceptance: - needs: - - get-go-version - uses: hashicorp/consul-k8s/.github/workflows/reusable-golangci-lint.yml@main - with: - directory: acceptance - go-version: ${{ needs.get-go-version.outputs.go-version }} - - unit-acceptance-framework: - needs: [get-go-version, golangci-lint-acceptance] - uses: hashicorp/consul-k8s/.github/workflows/reusable-unit.yml@main - with: - directory: acceptance/framework - go-version: ${{ needs.get-go-version.outputs.go-version }} - - golangci-lint-cli: - needs: - - get-go-version - uses: hashicorp/consul-k8s/.github/workflows/reusable-golangci-lint.yml@main - with: - directory: cli - go-version: ${{ needs.get-go-version.outputs.go-version }} - - unit-cli: - needs: [get-go-version, golangci-lint-cli] - uses: hashicorp/consul-k8s/.github/workflows/reusable-unit.yml@main - with: - directory: cli - go-version: ${{ needs.get-go-version.outputs.go-version }} - - # upload dev docker image - dev-upload-docker: - if: github.repository_owner == 'hashicorp' # Do not run on forks as this requires secrets - needs: [ get-product-version, build-distros ] - runs-on: ubuntu-latest - strategy: - matrix: - arch: ["amd64"] - env: - repo: ${{ github.event.repository.name }} - version: ${{ needs.get-product-version.outputs.product-version }} - steps: - - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 - with: - name: consul-cni_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip - path: control-plane/dist/cni/linux/${{ matrix.arch }} - - uses: actions/download-artifact@v3 - with: - name: consul-k8s-control-plane_${{ needs.get-product-version.outputs.product-version }}_linux_${{ matrix.arch }}.zip - path: control-plane/dist/linux/${{ matrix.arch }} - - name: extract consul-cni zip - env: - ZIP_LOCATION: control-plane/dist/cni/linux/${{ matrix.arch }} - run: | - cd "${ZIP_LOCATION}" - unzip -j *.zip - - name: extract control-plane zip - env: - ZIP_LOCATION: control-plane/dist/linux/${{ matrix.arch }} - run: | - cd "${ZIP_LOCATION}" - unzip -j *.zip - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USER }} - password: ${{ secrets.DOCKER_PASS }} - - name: Docker Build (Action) - uses: docker/build-push-action@v3 - with: - push: true - context: control-plane - platforms: ${{ matrix.arch }} - target: release-default - tags: docker.io/hashicorppreview/${{ env.repo }}-control-plane:${{ env.version }}-pr-${{ github.sha }} - -# Disable GHA acceptance tests until GHA formally supported -# acceptance: -# needs: [ get-product-version, dev-upload-docker, get-go-version ] -# uses: hashicorp/consul-k8s/.github/workflows/reusable-acceptance.yml@main -# with: -# name: acceptance -# directory: acceptance/tests -# go-version: ${{ needs.get-go-version.outputs.go-version }} -# additional-flags: "-use-kind -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.14-dev" -# gotestsum-version: 1.8.2 -# consul-k8s-image: docker.io/hashicorppreview/${{ github.event.repository.name }}-control-plane:${{ needs.get-product-version.outputs.product-version }}-pr-${{ github.sha }} -# secrets: -# CONSUL_ENT_LICENSE: ${{ secrets.CONSUL_ENT_LICENSE }} -# -# acceptance-tproxy: -# needs: [ get-product-version, dev-upload-docker, get-go-version ] -# uses: hashicorp/consul-k8s/.github/workflows/reusable-acceptance.yml@main -# with: -# name: acceptance-tproxy -# directory: acceptance/tests -# go-version: ${{ needs.get-go-version.outputs.go-version }} -# additional-flags: "-use-kind -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -enable-transparent-proxy -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.14-dev" -# gotestsum-version: 1.8.2 -# consul-k8s-image: docker.io/hashicorppreview/${{ github.event.repository.name }}-control-plane:${{ needs.get-product-version.outputs.product-version }}-pr-${{ github.sha }} -# secrets: -# CONSUL_ENT_LICENSE: ${{ secrets.CONSUL_ENT_LICENSE }} -# -# acceptance-cni: -# needs: [ get-product-version, dev-upload-docker, get-go-version ] -# uses: hashicorp/consul-k8s/.github/workflows/reusable-acceptance.yml@main -# with: -# name: acceptance -# directory: acceptance/tests -# go-version: ${{ needs.get-go-version.outputs.go-version }} -# additional-flags: "-use-kind -kubecontext=kind-dc1 -secondary-kubecontext=kind-dc2 -enable-transparent-proxy -enable-cni -consul-image=docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.14-dev" -# gotestsum-version: 1.8.2 -# consul-k8s-image: docker.io/hashicorppreview/${{ github.event.repository.name }}-control-plane:${{ needs.get-product-version.outputs.product-version }}-pr-${{ github.sha }} -# secrets: -# CONSUL_ENT_LICENSE: ${{ secrets.CONSUL_ENT_LICENSE }} - - diff --git a/.github/workflows/weekly-acceptance-0-49-x.yml b/.github/workflows/weekly-acceptance-0-49-x.yml new file mode 100644 index 0000000000..691d0b6236 --- /dev/null +++ b/.github/workflows/weekly-acceptance-0-49-x.yml @@ -0,0 +1,28 @@ +# Dispatch to the consul-k8s-workflows with a weekly cron +# +# A separate file is needed for each release because the cron schedules are different for each release. +name: weekly-acceptance-0-49-x +on: + schedule: + # * is a special character in YAML so you have to quote this string + # Run weekly on Monday at 3AM UTC/11PM EST/8PM PST + - cron: '0 3 * * 1' + +# these should be the only settings that you will ever need to change +env: + BRANCH: "release/0.49.x" + CONTEXT: "weekly" + +jobs: + cloud: + name: cloud + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@v1.2.2 + name: cloud + with: + workflow: cloud.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.github/workflows/weekly-acceptance-1-0-x.yml b/.github/workflows/weekly-acceptance-1-0-x.yml new file mode 100644 index 0000000000..663f779bb5 --- /dev/null +++ b/.github/workflows/weekly-acceptance-1-0-x.yml @@ -0,0 +1,29 @@ +# Dispatch to the consul-k8s-workflows with a weekly cron +# +# A separate file is needed for each release because the cron schedules are different for each release. +name: weekly-acceptance-1-0-x +on: + schedule: + # * is a special character in YAML so you have to quote this string + # Run weekly on Tuesday at 3AM UTC/11PM EST/8PM PST + - cron: '0 3 * * 2' + + +# these should be the only settings that you will ever need to change +env: + BRANCH: "release/1.0.x" + CONTEXT: "weekly" + +jobs: + cloud: + name: cloud + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@v1.2.2 + name: cloud + with: + workflow: cloud.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.github/workflows/weekly-acceptance-1-1-x.yml b/.github/workflows/weekly-acceptance-1-1-x.yml new file mode 100644 index 0000000000..2fc21aba5b --- /dev/null +++ b/.github/workflows/weekly-acceptance-1-1-x.yml @@ -0,0 +1,29 @@ +# Dispatch to the consul-k8s-workflows with a weekly cron +# +# A separate file is needed for each release because the cron schedules are different for each release. +name: weekly-acceptance-1-1-x +on: + schedule: + # * is a special character in YAML so you have to quote this string + # Run weekly on Wednesday at 3AM UTC/11PM EST/8PM PST + - cron: '0 3 * * 3' + + +# these should be the only settings that you will ever need to change +env: + BRANCH: "release/1.1.x" + CONTEXT: "weekly" + +jobs: + cloud: + name: cloud + runs-on: ubuntu-latest + steps: + - uses: benc-uk/workflow-dispatch@v1.2.2 + name: cloud + with: + workflow: cloud.yml + repo: hashicorp/consul-k8s-workflows + ref: main + token: ${{ secrets.ELEVATED_GITHUB_TOKEN }} + inputs: '{ "context":"${{ env.CONTEXT }}", "repository":"${{ github.repository }}", "branch":"${{ env.BRANCH }}", "sha":"${{ github.sha }}", "token":"${{ secrets.ELEVATED_GITHUB_TOKEN }}" }' diff --git a/.go-version b/.go-version index 836ae4eda2..8909929f6e 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.19.2 +1.20.7 diff --git a/.golangci.yml b/.golangci.yml index 143e0297e4..95aa25b0ff 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -31,4 +31,4 @@ linters-settings: simplify: true run: - timeout: 5m + timeout: 10m diff --git a/.release/security-scan.hcl b/.release/security-scan.hcl index 994e169dd9..42576d29b2 100644 --- a/.release/security-scan.hcl +++ b/.release/security-scan.hcl @@ -1,14 +1,13 @@ container { - dependencies = false - alpine_secdb = false - secrets = false + dependencies = true + alpine_secdb = true + secrets = true } binary { - secrets = false + secrets = true go_modules = false - osv = false + osv = true oss_index = false nvd = false -} - +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a625430c0..f6e474d08f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,116 @@ -## UNRELEASED +## 1.1.4 (Aug 10, 2023) + +SECURITY: + +* Upgrade to use Go 1.20.6 and `x/net/http` 0.12.0. +This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). [[GH-2642](https://github.com/hashicorp/consul-k8s/issues/2642)] +* Upgrade to use Go 1.20.7 and `x/net` 0.13.0. +This resolves [CVE-2023-29409](https://nvd.nist.gov/vuln/detail/CVE-2023-29409)(`crypto/tls`) +and [CVE-2023-3978](https://nvd.nist.gov/vuln/detail/CVE-2023-3978)(`net/html`). [[GH-2710](https://github.com/hashicorp/consul-k8s/issues/2710)] + +IMPROVEMENTS: + +* Add support to provide the logLevel flag via helm for multiple low level components. Introduces the following fields +1. `global.acls.logLevel` +2. `global.tls.logLevel` +3. `global.federation.logLevel` +4. `global.gossipEncryption.logLevel` +5. `server.logLevel` +6. `client.logLevel` +7. `meshGateway.logLevel` +8. `ingressGateways.logLevel` +9. `terminatingGateways.logLevel` +10. `telemetryCollector.logLevel` [[GH-2302](https://github.com/hashicorp/consul-k8s/issues/2302)] +* control-plane: increase timeout after login for ACL replication to 60 seconds [[GH-2656](https://github.com/hashicorp/consul-k8s/issues/2656)] +* helm: adds values for `securityContext` and `annotations` on TLS and ACL init/cleanup jobs. [[GH-2525](https://github.com/hashicorp/consul-k8s/issues/2525)] +* helm: do not set container securityContexts by default on OpenShift < 4.11 [[GH-2678](https://github.com/hashicorp/consul-k8s/issues/2678)] +* helm: set container securityContexts to match the `restricted` Pod Security Standards policy to support running Consul in a namespace with restricted PSA enforcement enabled [[GH-2572](https://github.com/hashicorp/consul-k8s/issues/2572)] + +BUG FIXES: + +* control-plane: fix bug in endpoints controller when deregistering services from consul when a node is deleted. [[GH-2571](https://github.com/hashicorp/consul-k8s/issues/2571)] +* helm: fix CONSUL_LOGIN_DATACENTER for consul client-daemonset. [[GH-2652](https://github.com/hashicorp/consul-k8s/issues/2652)] +* helm: fix ui ingress manifest formatting, and exclude `ingressClass` when not defined. [[GH-2687](https://github.com/hashicorp/consul-k8s/issues/2687)] + +## 1.1.3 (June 28, 2023) +BREAKING CHANGES: + +* control-plane: All policies managed by consul-k8s will now be updated on upgrade. If you previously edited the policies after install, your changes will be overwritten. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] + +SECURITY: + +* Bump Dockerfile base image to `alpine:3.18`. Resolves [CVE-2023-2650](https://github.com/advisories/GHSA-gqxg-9vfr-p9cg) vulnerability in openssl@3.0.8-r4 [[GH-2284](https://github.com/hashicorp/consul-k8s/issues/2284)] +* Update [Go-Discover](https://github.com/hashicorp/go-discover) in the container has been updated to address [CVE-2020-14040](https://github.com/advisories/GHSA-5rcv-m4m3-hfh7) [[GH-2390](https://github.com/hashicorp/consul-k8s/issues/2390)] + +FEATURES: + +* Add support for configuring graceful shutdown proxy lifecycle management settings. [[GH-2233](https://github.com/hashicorp/consul-k8s/issues/2233)] +* helm: Adds `acls.resources` field which can be configured to override the `resource` settings for the `server-acl-init` and `server-acl-init-cleanup` Jobs. [[GH-2416](https://github.com/hashicorp/consul-k8s/issues/2416)] +* sync-catalog: add ability to support weighted loadbalancing by service annotation `consul.hashicorp.com/service-weight: ` [[GH-2293](https://github.com/hashicorp/consul-k8s/issues/2293)] + +IMPROVEMENTS: + +* (Consul Enterprise) Add support to provide inputs via helm for audit log related configuration [[GH-2369](https://github.com/hashicorp/consul-k8s/issues/2369)] +* helm: Update the default amount of memory used by the connect-inject controller so that its less likely to get OOM killed. [[GH-2249](https://github.com/hashicorp/consul-k8s/issues/2249)] + +BUG FIXES: + +* control-plane: Always update ACL policies upon upgrade. [[GH-2392](https://github.com/hashicorp/consul-k8s/issues/2392)] +* control-plane: Fix casing of the Enforce Consecutive 5xx field on Service Defaults and acceptance test fixtures. [[GH-2266](https://github.com/hashicorp/consul-k8s/issues/2266)] + +## 1.1.2 (June 5, 2023) + +SECURITY: + +* Bump Dockerfile base image for RedHat UBI `consul-k8s-control-plane` image to `ubi-minimal:9.2`. [[GH-2204](https://github.com/hashicorp/consul-k8s/issues/2204)] +* Bump `controller-runtime` to address CVEs in dependencies. [[GH-2226](https://github.com/hashicorp/consul-k8s/issues/2226)] +* Upgrade to use Go 1.20.4. +This resolves vulnerabilities [CVE-2023-24537](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`go/scanner`), +[CVE-2023-24538](https://github.com/advisories/GHSA-v4m2-x4rp-hv22)(`html/template`), +[CVE-2023-24534](https://github.com/advisories/GHSA-8v5j-pwr7-w5f8)(`net/textproto`) and +[CVE-2023-24536](https://github.com/advisories/GHSA-9f7g-gqwh-jpf5)(`mime/multipart`). +Also, `golang.org/x/net` has been updated to v0.7.0 to resolve CVEs [CVE-2022-41721 +](https://github.com/advisories/GHSA-fxg5-wq6x-vr4w +), [CVE-2022-27664](https://github.com/advisories/GHSA-69cg-p879-7622) and [CVE-2022-41723 +](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h +.) [[GH-2104](https://github.com/hashicorp/consul-k8s/issues/2104)] + +FEATURES: + +* Add support for consul-telemetry-collector to forward envoy metrics to an otelhttp compatible receiver or HCP [[GH-2134](https://github.com/hashicorp/consul-k8s/issues/2134)] +* consul-telemetry-collector: Configure envoy proxy config during registration when consul-telemetry-collector is enabled. [[GH-2143](https://github.com/hashicorp/consul-k8s/issues/2143)] +* sync-catalog: add ability to sync hostname from a Kubernetes Ingress resource to the Consul Catalog during service registration. [[GH-2098](https://github.com/hashicorp/consul-k8s/issues/2098)] + +IMPROVEMENTS: + +* cli: Add `consul-k8s config read` command that returns the helm configuration in yaml format. [[GH-2078](https://github.com/hashicorp/consul-k8s/issues/2078)] +* cli: add consul-telemetry-gateway allow-all intention for -demo [[GH-2262](https://github.com/hashicorp/consul-k8s/issues/2262)] +* cli: update cloud preset to enable telemetry collector [[GH-2205](https://github.com/hashicorp/consul-k8s/issues/2205)] +* consul-telemetry-collector: add acceptance tests for consul telemetry collector component [[GH-2195](https://github.com/hashicorp/consul-k8s/issues/2195)] + +BUG FIXES: + +* crd: fix bug on service intentions CRD causing some updates to be ignored. [[GH-2194](https://github.com/hashicorp/consul-k8s/issues/2083)] +* api-gateway: fix issue where the API Gateway controller is unable to start up successfully when Vault is configured as the secrets backend [[GH-2083](https://github.com/hashicorp/consul-k8s/issues/2083)] +* control-plane: add support for idleTimeout in the Service Router config [[GH-2156](https://github.com/hashicorp/consul-k8s/issues/2156)] +* control-plane: fix issue with json tags of service defaults fields EnforcingConsecutive5xx, MaxEjectionPercent and BaseEjectionTime. [[GH-2160](https://github.com/hashicorp/consul-k8s/issues/2160)] +* control-plane: fix issue with multiport pods crashlooping due to dataplane port conflicts by ensuring dns redirection is disabled for non-tproxy pods [[GH-2176](https://github.com/hashicorp/consul-k8s/issues/2176)] +* helm: add missing `$HOST_IP` environment variable to to mesh gateway deployments. [[GH-1808](https://github.com/hashicorp/consul-k8s/issues/1808)] +* sync-catalog: fix issue where the sync-catalog ACL token were set with an incorrect ENV VAR. [[GH-2068](https://github.com/hashicorp/consul-k8s/issues/2068)] + +## 1.1.1 (March 31, 2023) + +IMPROVEMENTS: + +* helm: Set default `limits.cpu` resource setting to `null` for `consul-connect-inject-init` container to speed up registration times when onboarding services onto the mesh during the init container lifecycle. [[GH-2008](https://github.com/hashicorp/consul-k8s/issues/2008)] +* helm: When the `global.acls.bootstrapToken` field is set and the content of the secret is empty, the bootstrap ACL token is written to that secret after bootstrapping ACLs. This applies to both the Vault and Consul secrets backends. [[GH-1920](https://github.com/hashicorp/consul-k8s/issues/1920)] + +BUG FIXES: + +* api-gateway: fix ACL issue where when adminPartitions and ACLs are enabled, API Gateway Controller is unable to create a new namespace in Consul [[GH-2029](https://github.com/hashicorp/consul-k8s/issues/2029)] +* api-gateway: fix issue where specifying an external server SNI name while using client nodes resulted in a TLS verification error. [[GH-2013](https://github.com/hashicorp/consul-k8s/issues/2013)] + +## 1.1.0 (February 27, 2023) BREAKING CHANGES: * Helm: @@ -17,6 +129,7 @@ BREAKING CHANGES: IMPROVEMENTS: * Helm: + * CNI: Add `connectInject.cni.namespace` stanza which allows the CNI plugin resources to be deployed in a namespace other than the namespace that Consul is installed. [[GH-1756](https://github.com/hashicorp/consul-k8s/pull/1756)] * Kubernetes v1.26 is now supported. Minimum tested version of Kubernetes is now v1.23. [[GH-1852](https://github.com/hashicorp/consul-k8s/pull/1852)] * Add a `global.extraLabels` stanza to allow setting global Kubernetes labels for all components deployed by the `consul-k8s` Helm chart. [[GH-1778](https://github.com/hashicorp/consul-k8s/pull/1778)] * Add the `accessLogs` field to the `ProxyDefaults` CRD. [[GH-1816](https://github.com/hashicorp/consul-k8s/pull/1816)] @@ -24,17 +137,25 @@ IMPROVEMENTS: * Add the `balanceInboundConnections` field to the `ServiceDefaults` CRD. [[GH-1823]](https://github.com/hashicorp/consul-k8s/pull/1823) * Add the `upstreamConfig.overrides[].peer` field to the `ServiceDefaults` CRD. [[GH-1853]](https://github.com/hashicorp/consul-k8s/pull/1853) * Control-Plane + * Update minimum go version for project to 1.20 [[GH-1908](https://github.com/hashicorp/consul-k8s/pull/1908)] * Add support for the annotation `consul.hashicorp.com/use-proxy-health-check`. When this annotation is used by a service, it configures a readiness endpoint on Consul Dataplane and queries it instead of the proxy's inbound port which forwards requests to the application. [[GH-1824](https://github.com/hashicorp/consul-k8s/pull/1824)], [[GH-1841](https://github.com/hashicorp/consul-k8s/pull/1841)] * Add health check for synced services based on the status of the Kubernetes readiness probe on synced pod. [[GH-1821](https://github.com/hashicorp/consul-k8s/pull/1821)] * Remove extraneous `gnupg` dependency from `consul-k8s-control-plane` since it is no longer needed for validating binary artifacts prior to release. [[GH-1882](https://github.com/hashicorp/consul-k8s/pull/1882)] * Server ACL Init always appends both, the secrets from the serviceAccount's secretRefs and the one created by the Helm chart, to support Openshift secret handling. [[GH-1770](https://github.com/hashicorp/consul-k8s/pull/1770)] + * Update alpine to 3.17 in the Docker image. [[GH-1934](https://github.com/hashicorp/consul-k8s/pull/1934)] * CLI: + * Update minimum go version for project to 1.20 [[GH-1908](https://github.com/hashicorp/consul-k8s/pull/1908)] * Add `consul-k8s proxy log podname` command for displaying and modifying Envoy log levels for a given Pod. [GH-1844](https://github.com/hashicorp/consul-k8s/pull/1844), [GH-1849](https://github.com/hashicorp/consul-k8s/pull/1849), [GH-1864](https://github.com/hashicorp/consul-k8s/pull/1864) - BUG FIXES: * Control Plane * Don't incorrectly diff intention config entries when upgrading from Consul pre-1.12 to 1.12+ [[GH-1804](https://github.com/hashicorp/consul-k8s/pull/1804)] + * Add discover binary to control-plane image [[GH-1749](https://github.com/hashicorp/consul-k8s/pull/1749)] +* Helm: + * Don't pass in a CA file to the API Gateway controller when `externalServers.useSystemRoots` is `true`. [[GH-1743](https://github.com/hashicorp/consul-k8s/pull/1743)] + * Use the correct autogenerated cert for the API Gateway Controller when connecting to servers versus clients. [[GH-1753](https://github.com/hashicorp/consul-k8s/pull/1753)] +* Security: + * Upgrade to use Go 1.20.1 This resolves vulnerabilities [CVE-2022-41724](https://go.dev/issue/58001) in `crypto/tls` and [CVE-2022-41723](https://go.dev/issue/57855) in `net/http`. [[GH-1908](https://github.com/hashicorp/consul-k8s/pull/1908)] ## 1.0.3 (January 30, 2023) @@ -117,6 +238,7 @@ BREAKING CHANGES: * `client.enabled` now defaults to `false`. Setting it to `true` will deploy client agents, however, none of the consul-k8s components will use clients for their operation. * `global.imageEnvoy` is no longer used for sidecar proxies, as well as mesh, terminating, and ingress gateways. * `externalServers.grpcPort` default is now `8502` instead of `8503`. + * `externalServers.hosts` no longer supports [cloud auto-join](https://developer.hashicorp.com/consul/docs/install/cloud-auto-join) strings directly. Instead, include an [`exec=`](https://github.com/hashicorp/go-netaddrs#command-line-tool-usage) string in the `externalServers.hosts` list to invoke the `discover` CLI. For example, the following string invokes the `discover` CLI with a cloud auto-join string: `exec=discover -q addrs provider=aws region=us-west-2 tag_key=consul-server tag_value=true`. The `discover` CLI is included in the official `hashicorp/consul-dataplane` images by default. * `meshGateway.service.enabled` value is removed. Mesh gateways now will always have a Kubernetes service as this is required to register them as a service with Consul. * `meshGateway.initCopyConsulContainer`, `ingressGateways.initCopyConsulContainer`, `terminatingGateways.initCopyConsulContainer` values are removed. * `connectInject.enabled` now defaults to `true`. [[GH-1551](https://github.com/hashicorp/consul-k8s/pull/1551)] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 510d4c3b3f..6ad56baacc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,6 +24,7 @@ 1. [Writing Acceptance tests](#writing-acceptance-tests) 1. [Using the Acceptance Test Framework to Debug](#using-acceptance-test-framework-to-debug) 1. [Helm Reference Docs](#helm-reference-docs) +1. [Adding a Changelog Entry](#adding-a-changelog-entry) ## Contributing 101 @@ -641,8 +642,7 @@ you may use the following command: go test ./... -p 1 -timeout 20m \ -enable-multi-cluster \ - -kubecontext= \ - -secondary-kubecontext= + -kube-contexts=",, etc.>" Below is the list of available flags: @@ -666,20 +666,14 @@ Below is the list of available flags: This applies only to tests that enable connectInject. -enterprise-license The enterprise license for Consul. --kubeconfig string - The path to a kubeconfig file. If this is blank, the default kubeconfig path (~/.kube/config) will be used. --kubecontext string - The name of the Kubernetes context to use. If this is blank, the context set as the current context will be used by default. --namespace string - The Kubernetes namespace to use for tests. (default "default") +-kubeconfigs string + The comma separated list of Kubernetes configs to use (eg. "~/.kube/config,~/.kube/config2"). The first in the list will be treated as the primary config, followed by the secondary, etc. If the list is empty, or items are blank, then the default kubeconfig path (~/.kube/config) will be used. +-kube-contexts string + The comma separated list of Kubernetes contexts to use (eg. "kind-dc1,kind-dc2"). The first in the list will be treated as the primary context, followed by the secondary, etc. If the list is empty, or items are blank, then the current context will be used. +-kube-namespaces string + The comma separated list of Kubernetes namespaces to use (eg. "consul,consul-secondary"). The first in the list will be treated as the primary namespace, followed by the secondary, etc. If the list is empty, or fields are blank, then the current namespace will be used. -no-cleanup-on-failure If true, the tests will not cleanup Kubernetes resources they create when they finish running.Note this flag must be run with -failfast flag, otherwise subsequent tests will fail. --secondary-kubeconfig string - The path to a kubeconfig file of the secondary k8s cluster. If this is blank, the default kubeconfig path (~/.kube/config) will be used. --secondary-kubecontext string - The name of the Kubernetes context for the secondary cluster to use. If this is blank, the context set as the current context will be used by default. --secondary-namespace string - The Kubernetes namespace to use in the secondary k8s cluster. (default "default") ``` **Note:** There is a Terraform configuration in the @@ -1214,3 +1208,43 @@ So that the documentation can look like: ```markdown - `ports` ((#v-ingressgateways-defaults-service-ports)) (`array: [{port: 8080, port: 8443}]`) - Port docs ``` + +## Adding a Changelog Entry + +Any change that a Consul-K8s user might need to know about should have a changelog entry. + +What doesn't need a changelog entry? +- Typos/fixes, unless they are in a public-facing API +- Code changes we are certain no Consul-K8s users will need to know about + +To include a [changelog entry](../.changelog) in a PR, commit a text file +named `.changelog/.txt`, where `` is the number associated with the open +PR in GitHub. The text file should describe the changes in the following format: + +```` +```release-note: +: +``` +```` + +Valid values for `` include: +- `feature`: for the addition of a new feature +- `improvement`: for an improvement (not a bug fix) to an existing feature +- `bug`: for a bug fix +- `security`: for any Common Vulnerabilities and Exposures (CVE) resolutions +- `breaking-change`: for any change that is not fully backwards-compatible +- `deprecation`: for functionality which is now marked for removal in a future release + +`` is meant to categorize the functionality affected by the change. +Some common values are: +- `cli`: related to the command-line interface and its commands +- `control-plane`: related to control-plane functionality +- `helm`: related to the charts module and any files, yaml, go, etc. therein + +There may be cases where a `code area` doesn't make sense (i.e. addressing a Go CVE). In these +cases it is okay not to provide a `code area`. + +For more examples, look in the [`.changelog/`](../.changelog) folder for existing changelog entries. + +If a PR deserves multiple changelog entries, just add multiple entries separated by a newline +in the format described above to the `.changelog/.txt` file. diff --git a/Makefile b/Makefile index 6ad59a2a91..44a0a6a7f2 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,10 @@ VERSION = $(shell ./control-plane/build-support/scripts/version.sh control-plane/version/version.go) +CONSUL_IMAGE_VERSION = $(shell ./control-plane/build-support/scripts/consul-version.sh charts/consul/values.yaml) +CONSUL_ENTERPRISE_IMAGE_VERSION = $(shell ./control-plane/build-support/scripts/consul-enterprise-version.sh charts/consul/values.yaml) +CONSUL_DATAPLANE_IMAGE_VERSION = $(shell ./control-plane/build-support/scripts/consul-dataplane-version.sh charts/consul/values.yaml) +KIND_VERSION= $(shell ./control-plane/build-support/scripts/read-yaml-config.sh acceptance/ci-inputs/kind-inputs.yaml .kindVersion) +KIND_NODE_IMAGE= $(shell ./control-plane/build-support/scripts/read-yaml-config.sh acceptance/ci-inputs/kind-inputs.yaml .kindNodeImage) +KUBECTL_VERSION= $(shell ./control-plane/build-support/scripts/read-yaml-config.sh acceptance/ci-inputs/kind-inputs.yaml .kubectlVersion) # ===========> Helm Targets @@ -66,15 +72,6 @@ cni-plugin-lint: ctrl-generate: get-controller-gen ## Run CRD code generation. cd control-plane; $(CONTROLLER_GEN) object:headerFile="build-support/controller/boilerplate.go.txt" paths="./..." -# Helper target for doing local cni acceptance testing -kind-cni: - kind delete cluster --name dc1 - kind delete cluster --name dc2 - kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc1 --image kindest/node:v1.23.6 - make kind-cni-calico - kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc2 --image kindest/node:v1.23.6 - make kind-cni-calico - # Perform a terraform fmt check but don't change anything terraform-fmt-check: @$(CURDIR)/control-plane/build-support/scripts/terraformfmtcheck.sh $(TERRAFORM_DIR) @@ -85,14 +82,16 @@ terraform-fmt: @terraform fmt -recursive .PHONY: terraform-fmt +# Check for hashicorppreview containers +check-preview-containers: + @source $(CURDIR)/control-plane/build-support/scripts/check-hashicorppreview.sh -# ===========> CLI Targets +# ===========> CLI Targets cli-dev: @echo "==> Installing consul-k8s CLI tool for ${GOOS}/${GOARCH}" @cd cli; go build -o ./bin/consul-k8s; cp ./bin/consul-k8s ${GOPATH}/bin/ - cli-lint: ## Run linter in the control-plane directory. cd cli; golangci-lint run -c ../.golangci.yml @@ -110,7 +109,31 @@ kind-cni-calico: # Sleeps are needed as installs can happen too quickly for Kind to handle it @sleep 30 kubectl create -f $(CURDIR)/acceptance/framework/environment/cni-kind/custom-resources.yaml - @sleep 20 + @sleep 20 + +# Helper target for doing local cni acceptance testing +kind-cni: + kind delete cluster --name dc1 + kind delete cluster --name dc2 + kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc1 --image $(KIND_NODE_IMAGE) + make kind-cni-calico + kind create cluster --config=$(CURDIR)/acceptance/framework/environment/cni-kind/kind.config --name dc2 --image $(KIND_NODE_IMAGE) + make kind-cni-calico + +# Helper target for doing local acceptance testing +kind: + kind delete cluster --name dc1 + kind delete cluster --name dc2 + kind create cluster --name dc1 --image $(KIND_NODE_IMAGE) + kind create cluster --name dc2 --image $(KIND_NODE_IMAGE) + + +# Helper target for loading local dev images (run with `DEV_IMAGE=...` to load non-k8s images) +kind-load: + kind load docker-image --name dc1 $(DEV_IMAGE) + kind load docker-image --name dc2 $(DEV_IMAGE) + kind load docker-image --name dc3 $(DEV_IMAGE) + kind load docker-image --name dc4 $(DEV_IMAGE) # ===========> Shared Targets @@ -147,31 +170,76 @@ ci.aws-acceptance-test-cleanup: ## Deletes AWS resources left behind after faile version: @echo $(VERSION) +consul-version: + @echo $(CONSUL_IMAGE_VERSION) + +consul-enterprise-version: + @echo $(CONSUL_ENTERPRISE_IMAGE_VERSION) + +consul-dataplane-version: + @echo $(CONSUL_DATAPLANE_IMAGE_VERSION) + +kind-version: + @echo $(KIND_VERSION) + +kind-node-image: + @echo $(KIND_NODE_IMAGE) + +kubectl-version: + @echo $(KUBECTL_VERSION) + +kind-test-packages: + @./control-plane/build-support/scripts/set_test_package_matrix.sh "acceptance/ci-inputs/kind_acceptance_test_packages.yaml" + +gke-test-packages: + @./control-plane/build-support/scripts/set_test_package_matrix.sh "acceptance/ci-inputs/gke_acceptance_test_packages.yaml" + +eks-test-packages: + @./control-plane/build-support/scripts/set_test_package_matrix.sh "acceptance/ci-inputs/eks_acceptance_test_packages.yaml" + +aks-test-packages: + @./control-plane/build-support/scripts/set_test_package_matrix.sh "acceptance/ci-inputs/aks_acceptance_test_packages.yaml" + # ===========> Release Targets +check-env: + @printenv | grep "CONSUL_K8S" -prepare-release: ## Sets the versions, updates changelog to prepare this repository to release -ifndef RELEASE_VERSION - $(error RELEASE_VERSION is required) +prepare-release-script: ## Sets the versions, updates changelog to prepare this repository to release +ifndef CONSUL_K8S_RELEASE_VERSION + $(error CONSUL_K8S_RELEASE_VERSION is required) +endif +ifndef CONSUL_K8S_RELEASE_DATE + $(error CONSUL_K8S_RELEASE_DATE is required, use format , (ex. October 4, 2022)) +endif +ifndef CONSUL_K8S_LAST_RELEASE_GIT_TAG + $(error CONSUL_K8S_LAST_RELEASE_GIT_TAG is required) endif -ifndef RELEASE_DATE - $(error RELEASE_DATE is required, use format , (ex. October 4, 2022)) +ifndef CONSUL_K8S_CONSUL_VERSION + $(error CONSUL_K8S_CONSUL_VERSION is required) endif - source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_release $(CURDIR) $(RELEASE_VERSION) "$(RELEASE_DATE)" $(PRERELEASE_VERSION) + @source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_release $(CURDIR) $(CONSUL_K8S_RELEASE_VERSION) "$(CONSUL_K8S_RELEASE_DATE)" $(CONSUL_K8S_LAST_RELEASE_GIT_TAG) $(CONSUL_K8S_CONSUL_VERSION) $(CONSUL_K8S_CONSUL_DATAPLANE_VERSION) $(CONSUL_K8S_PRERELEASE_VERSION); \ + +prepare-release: prepare-release-script check-preview-containers prepare-dev: -ifndef RELEASE_VERSION - $(error RELEASE_VERSION is required) +ifndef CONSUL_K8S_RELEASE_VERSION + $(error CONSUL_K8S_RELEASE_VERSION is required) +endif +ifndef CONSUL_K8S_RELEASE_DATE + $(error CONSUL_K8S_RELEASE_DATE is required, use format , (ex. October 4, 2022)) endif -ifndef RELEASE_DATE - $(error RELEASE_DATE is required, use format , (ex. October 4, 2022)) +ifndef CONSUL_K8S_NEXT_RELEASE_VERSION + $(error CONSUL_K8S_NEXT_RELEASE_VERSION is required) endif -ifndef NEXT_RELEASE_VERSION - $(error NEXT_RELEASE_VERSION is required) +ifndef CONSUL_K8S_NEXT_CONSUL_VERSION + $(error CONSUL_K8S_NEXT_CONSUL_VERSION is required) endif - source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_dev $(CURDIR) $(RELEASE_VERSION) "$(RELEASE_DATE)" $(NEXT_RELEASE_VERSION) $(PRERELEASE_VERSION) +ifndef CONSUL_K8S_NEXT_CONSUL_DATAPLANE_VERSION + $(error CONSUL_K8S_NEXT_CONSUL_DATAPLANE_VERSION is required) +endif + source $(CURDIR)/control-plane/build-support/scripts/functions.sh; prepare_dev $(CURDIR) $(CONSUL_K8S_RELEASE_VERSION) "$(CONSUL_K8S_RELEASE_DATE)" "" $(CONSUL_K8S_NEXT_RELEASE_VERSION) $(CONSUL_K8S_NEXT_CONSUL_VERSION) $(CONSUL_K8S_NEXT_CONSUL_DATAPLANE_VERSION) # ===========> Makefile config - .DEFAULT_GOAL := help .PHONY: gen-helm-docs copy-crds-to-chart bats-tests help ci.aws-acceptance-test-cleanup version cli-dev prepare-dev prepare-release SHELL = bash diff --git a/acceptance/ci-inputs/aks_acceptance_test_packages.yaml b/acceptance/ci-inputs/aks_acceptance_test_packages.yaml new file mode 100644 index 0000000000..cef04a3205 --- /dev/null +++ b/acceptance/ci-inputs/aks_acceptance_test_packages.yaml @@ -0,0 +1,3 @@ +- {runner: 0, test-packages: "connect peering snapshot-agent wan-federation"} +- {runner: 1, test-packages: "consul-dns example partitions metrics sync"} +- {runner: 2, test-packages: "basic cli config-entries api-gateway ingress-gateway terminating-gateway vault"} \ No newline at end of file diff --git a/acceptance/ci-inputs/eks_acceptance_test_packages.yaml b/acceptance/ci-inputs/eks_acceptance_test_packages.yaml new file mode 100644 index 0000000000..cef04a3205 --- /dev/null +++ b/acceptance/ci-inputs/eks_acceptance_test_packages.yaml @@ -0,0 +1,3 @@ +- {runner: 0, test-packages: "connect peering snapshot-agent wan-federation"} +- {runner: 1, test-packages: "consul-dns example partitions metrics sync"} +- {runner: 2, test-packages: "basic cli config-entries api-gateway ingress-gateway terminating-gateway vault"} \ No newline at end of file diff --git a/acceptance/ci-inputs/gke_acceptance_test_packages.yaml b/acceptance/ci-inputs/gke_acceptance_test_packages.yaml new file mode 100644 index 0000000000..cef04a3205 --- /dev/null +++ b/acceptance/ci-inputs/gke_acceptance_test_packages.yaml @@ -0,0 +1,3 @@ +- {runner: 0, test-packages: "connect peering snapshot-agent wan-federation"} +- {runner: 1, test-packages: "consul-dns example partitions metrics sync"} +- {runner: 2, test-packages: "basic cli config-entries api-gateway ingress-gateway terminating-gateway vault"} \ No newline at end of file diff --git a/acceptance/ci-inputs/kind-inputs.yaml b/acceptance/ci-inputs/kind-inputs.yaml new file mode 100644 index 0000000000..e729e60b35 --- /dev/null +++ b/acceptance/ci-inputs/kind-inputs.yaml @@ -0,0 +1,3 @@ +kindVersion: v0.19.0 +kindNodeImage: kindest/node:v1.26.4 +kubectlVersion: v1.27.1 diff --git a/acceptance/ci-inputs/kind_acceptance_test_packages.yaml b/acceptance/ci-inputs/kind_acceptance_test_packages.yaml new file mode 100644 index 0000000000..74991abd76 --- /dev/null +++ b/acceptance/ci-inputs/kind_acceptance_test_packages.yaml @@ -0,0 +1,6 @@ +- {runner: 0, test-packages: "partitions"} +- {runner: 1, test-packages: "peering"} +- {runner: 2, test-packages: "connect snapshot-agent wan-federation"} +- {runner: 3, test-packages: "cli vault metrics"} +- {runner: 4, test-packages: "api-gateway ingress-gateway sync example consul-dns"} +- {runner: 5, test-packages: "config-entries terminating-gateway basic"} \ No newline at end of file diff --git a/acceptance/framework/config/config.go b/acceptance/framework/config/config.go index 8e131c9c59..44364617be 100644 --- a/acceptance/framework/config/config.go +++ b/acceptance/framework/config/config.go @@ -2,10 +2,12 @@ package config import ( "fmt" + "math" "os" "path/filepath" "strconv" "strings" + "testing" "github.com/hashicorp/go-version" "gopkg.in/yaml.v2" @@ -19,16 +21,48 @@ const ( LicenseSecretKey = "key" ) -// TestConfig holds configuration for the test suite. -type TestConfig struct { - Kubeconfig string +type KubeTestConfig struct { + KubeConfig string KubeContext string KubeNamespace string +} + +// NewKubeTestConfigList takes lists of kubernetes configs, contexts and namespaces and constructs KubeTestConfig +// We validate ahead of time that the lists are either 0 or the same length as we expect that if the length of a list +// is greater than 0, then the indexes should match. For example: []kubeContexts{"ctx1", "ctx2"} indexes 0, 1 match with []kubeNamespaces{"ns1", "ns2"}. +func NewKubeTestConfigList(kubeConfigs, kubeContexts, kubeNamespaces []string) []KubeTestConfig { + // Grab the longest length. + l := math.Max(float64(len(kubeConfigs)), + math.Max(float64(len(kubeContexts)), float64(len(kubeNamespaces)))) + + // If all are empty, then return a single empty entry + if l == 0 { + return []KubeTestConfig{{}} + } + + // Add each non-zero length list to the new structs, we should have + // n structs where n == l. + out := make([]KubeTestConfig, int(l)) + for i := range out { + kenv := KubeTestConfig{} + if len(kubeConfigs) != 0 { + kenv.KubeConfig = kubeConfigs[i] + } + if len(kubeContexts) != 0 { + kenv.KubeContext = kubeContexts[i] + } + if len(kubeNamespaces) != 0 { + kenv.KubeNamespace = kubeNamespaces[i] + } + out[i] = kenv + } + return out +} - EnableMultiCluster bool - SecondaryKubeconfig string - SecondaryKubeContext string - SecondaryKubeNamespace string +// TestConfig holds configuration for the test suite. +type TestConfig struct { + KubeEnvs []KubeTestConfig + EnableMultiCluster bool EnableEnterprise bool EnterpriseLicense string @@ -37,22 +71,29 @@ type TestConfig struct { EnablePodSecurityPolicies bool - EnableCNI bool + EnableCNI bool + EnableRestrictedPSAEnforcement bool EnableTransparentProxy bool DisablePeering bool - HelmChartVersion string - ConsulImage string - ConsulK8SImage string - ConsulVersion *version.Version - EnvoyImage string + HelmChartVersion string + ConsulImage string + ConsulK8SImage string + ConsulDataplaneImage string + ConsulVersion *version.Version + ConsulDataplaneVersion *version.Version + EnvoyImage string + ConsulCollectorImage string + + HCPResourceID string NoCleanupOnFailure bool DebugDirectory string UseAKS bool + UseEKS bool UseGKE bool UseKind bool @@ -89,10 +130,22 @@ func (t *TestConfig) HelmValuesFromConfig() (map[string]string, error) { if t.EnableCNI { setIfNotEmpty(helmValues, "connectInject.cni.enabled", "true") + setIfNotEmpty(helmValues, "connectInject.cni.logLevel", "debug") // GKE is currently the only cloud provider that uses a different CNI bin dir. if t.UseGKE { setIfNotEmpty(helmValues, "connectInject.cni.cniBinDir", "/home/kubernetes/bin") } + if t.EnableOpenshift { + setIfNotEmpty(helmValues, "connectInject.cni.multus", "true") + setIfNotEmpty(helmValues, "connectInject.cni.cniBinDir", "/var/lib/cni/bin") + setIfNotEmpty(helmValues, "connectInject.cni.cniNetDir", "/etc/kubernetes/cni/net.d") + } + + if t.EnableRestrictedPSAEnforcement { + // The CNI requires privilege, so when restricted PSA enforcement is enabled on the Consul + // namespace it must be run in a different privileged namespace. + setIfNotEmpty(helmValues, "connectInject.cni.namespace", "kube-system") + } } setIfNotEmpty(helmValues, "connectInject.transparentProxy.defaultEnabled", strconv.FormatBool(t.EnableTransparentProxy)) @@ -100,10 +153,28 @@ func (t *TestConfig) HelmValuesFromConfig() (map[string]string, error) { setIfNotEmpty(helmValues, "global.image", t.ConsulImage) setIfNotEmpty(helmValues, "global.imageK8S", t.ConsulK8SImage) setIfNotEmpty(helmValues, "global.imageEnvoy", t.EnvoyImage) + setIfNotEmpty(helmValues, "global.imageConsulDataplane", t.ConsulDataplaneImage) return helmValues, nil } +// IsExpectedClusterCount check that we have at least the required number of clusters to +// run a test. +func (t *TestConfig) IsExpectedClusterCount(count int) bool { + return len(t.KubeEnvs) >= count +} + +// GetPrimaryKubeEnv returns the primary Kubernetes environment. +func (t *TestConfig) GetPrimaryKubeEnv() KubeTestConfig { + // Return the first in the list as this is always the primary + // kube environment. If empty return an empty kubeEnv + if len(t.KubeEnvs) < 1 { + return KubeTestConfig{} + } else { + return t.KubeEnvs[0] + } +} + type values struct { Global globalValues `yaml:"global"` } @@ -139,21 +210,22 @@ func (t *TestConfig) entImage() (string, error) { } // Otherwise, assume that we have an image tag with a version in it. - consulImageSplits := strings.Split(v.Global.Image, ":") - if len(consulImageSplits) != 2 { - return "", fmt.Errorf("could not determine consul version from global.image: %s", v.Global.Image) - } - consulImageVersion := consulImageSplits[1] - - var preRelease string - // Handle versions like 1.9.0-rc1. - if strings.Contains(consulImageVersion, "-") { - split := strings.Split(consulImageVersion, "-") - consulImageVersion = split[0] - preRelease = fmt.Sprintf("-%s", split[1]) + // Use the same Docker repository and tagging scheme, but replace 'consul' with 'consul-enterprise'. + imageTag := strings.Replace(v.Global.Image, "/consul:", "/consul-enterprise:", 1) + + // We currently add an '-ent' suffix to release versions of enterprise images (nightly previews + // do not include this suffix). + if strings.HasPrefix(imageTag, "hashicorp/consul-enterprise:") { + imageTag = fmt.Sprintf("%s-ent", imageTag) } - return fmt.Sprintf("hashicorp/consul-enterprise:%s%s-ent", consulImageVersion, preRelease), nil + return imageTag, nil +} + +func (c *TestConfig) SkipWhenOpenshiftAndCNI(t *testing.T) { + if c.EnableOpenshift && c.EnableCNI { + t.Skip("skipping because -enable-cni and -enable-openshift are set and this test doesn't deploy apps correctly") + } } // setIfNotEmpty sets key to val in map m if value is not empty. diff --git a/acceptance/framework/config/config_test.go b/acceptance/framework/config/config_test.go index 7733d815db..124bfe65f5 100644 --- a/acceptance/framework/config/config_test.go +++ b/acceptance/framework/config/config_test.go @@ -113,6 +113,7 @@ func TestConfig_HelmValuesFromConfig(t *testing.T) { }, map[string]string{ "connectInject.cni.enabled": "true", + "connectInject.cni.logLevel": "debug", "connectInject.transparentProxy.defaultEnabled": "false", }, }, @@ -133,25 +134,34 @@ func TestConfig_HelmValuesFromConfig_EntImage(t *testing.T) { expErr string }{ { - consulImage: "hashicorp/consul:1.9.0", - expImage: "hashicorp/consul-enterprise:1.9.0-ent", + consulImage: "hashicorp/consul:1.15.3", + expImage: "hashicorp/consul-enterprise:1.15.3-ent", }, { - consulImage: "hashicorp/consul:1.8.5-rc1", - expImage: "hashicorp/consul-enterprise:1.8.5-rc1-ent", + consulImage: "hashicorp/consul:1.16.0-rc1", + expImage: "hashicorp/consul-enterprise:1.16.0-rc1-ent", }, { - consulImage: "hashicorp/consul:1.7.0-beta3", - expImage: "hashicorp/consul-enterprise:1.7.0-beta3-ent", - }, - { - consulImage: "invalid", - expErr: "could not determine consul version from global.image: invalid", + consulImage: "hashicorp/consul:1.14.0-beta1", + expImage: "hashicorp/consul-enterprise:1.14.0-beta1-ent", }, { consulImage: "hashicorp/consul@sha256:oioi2452345kjhlkh", expImage: "hashicorp/consul@sha256:oioi2452345kjhlkh", }, + // Nightly tags differ from release tags ('-ent' suffix is omitted) + { + consulImage: "docker.mirror.hashicorp.services/hashicorppreview/consul:1.17-dev", + expImage: "docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.17-dev", + }, + { + consulImage: "docker.mirror.hashicorp.services/hashicorppreview/consul:1.17-dev-ubi", + expImage: "docker.mirror.hashicorp.services/hashicorppreview/consul-enterprise:1.17-dev-ubi", + }, + { + consulImage: "docker.mirror.hashicorp.services/hashicorppreview/consul@sha256:oioi2452345kjhlkh", + expImage: "docker.mirror.hashicorp.services/hashicorppreview/consul@sha256:oioi2452345kjhlkh", + }, } for _, tt := range tests { t.Run(tt.consulImage, func(t *testing.T) { @@ -178,3 +188,106 @@ func TestConfig_HelmValuesFromConfig_EntImage(t *testing.T) { }) } } + +func Test_KubeEnvListFromStringList(t *testing.T) { + tests := []struct { + name string + kubeContexts []string + KubeConfigs []string + kubeNamespaces []string + expKubeEnvList []KubeTestConfig + }{ + { + name: "empty-lists", + kubeContexts: []string{}, + KubeConfigs: []string{}, + kubeNamespaces: []string{}, + expKubeEnvList: []KubeTestConfig{{}}, + }, + { + name: "kubeContext set", + kubeContexts: []string{"ctx1", "ctx2"}, + KubeConfigs: []string{}, + kubeNamespaces: []string{}, + expKubeEnvList: []KubeTestConfig{{KubeContext: "ctx1"}, {KubeContext: "ctx2"}}, + }, + { + name: "kubeNamespace set", + kubeContexts: []string{}, + KubeConfigs: []string{"/path/config1", "/path/config2"}, + kubeNamespaces: []string{}, + expKubeEnvList: []KubeTestConfig{{KubeConfig: "/path/config1"}, {KubeConfig: "/path/config2"}}, + }, + { + name: "kubeConfigs set", + kubeContexts: []string{}, + KubeConfigs: []string{}, + kubeNamespaces: []string{"ns1", "ns2"}, + expKubeEnvList: []KubeTestConfig{{KubeNamespace: "ns1"}, {KubeNamespace: "ns2"}}, + }, + { + name: "multiple everything", + kubeContexts: []string{"ctx1", "ctx2"}, + KubeConfigs: []string{"/path/config1", "/path/config2"}, + kubeNamespaces: []string{"ns1", "ns2"}, + expKubeEnvList: []KubeTestConfig{{KubeContext: "ctx1", KubeNamespace: "ns1", KubeConfig: "/path/config1"}, {KubeContext: "ctx2", KubeNamespace: "ns2", KubeConfig: "/path/config2"}}, + }, + { + name: "multiple context and configs", + kubeContexts: []string{"ctx1", "ctx2"}, + KubeConfigs: []string{"/path/config1", "/path/config2"}, + kubeNamespaces: []string{}, + expKubeEnvList: []KubeTestConfig{{KubeContext: "ctx1", KubeConfig: "/path/config1"}, {KubeContext: "ctx2", KubeConfig: "/path/config2"}}, + }, + { + name: "multiple namespace and configs", + kubeContexts: []string{}, + KubeConfigs: []string{"/path/config1", "/path/config2"}, + kubeNamespaces: []string{"ns1", "ns2"}, + expKubeEnvList: []KubeTestConfig{{KubeNamespace: "ns1", KubeConfig: "/path/config1"}, {KubeNamespace: "ns2", KubeConfig: "/path/config2"}}, + }, + { + name: "multiple context and namespace", + kubeContexts: []string{"ctx1", "ctx2"}, + KubeConfigs: []string{}, + kubeNamespaces: []string{"ns1", "ns2"}, + expKubeEnvList: []KubeTestConfig{{KubeContext: "ctx1", KubeNamespace: "ns1"}, {KubeContext: "ctx2", KubeNamespace: "ns2"}}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := NewKubeTestConfigList(tt.KubeConfigs, tt.kubeContexts, tt.kubeNamespaces) + require.Equal(t, tt.expKubeEnvList, actual) + }) + } +} + +func Test_GetPrimaryKubeEnv(t *testing.T) { + tests := []struct { + name string + kubeEnvList []KubeTestConfig + expPrimaryKubeEnv KubeTestConfig + }{ + { + name: "context config multiple namespace single", + kubeEnvList: []KubeTestConfig{{KubeContext: "ctx1", KubeNamespace: "ns1", KubeConfig: "/path/config1"}, {KubeContext: "ctx2", KubeConfig: "/path/config2"}}, + expPrimaryKubeEnv: KubeTestConfig{KubeContext: "ctx1", KubeNamespace: "ns1", KubeConfig: "/path/config1"}, + }, + { + name: "context config multiple namespace single", + kubeEnvList: []KubeTestConfig{}, + expPrimaryKubeEnv: KubeTestConfig{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := TestConfig{ + KubeEnvs: tt.kubeEnvList, + } + actual := cfg.GetPrimaryKubeEnv() + require.Equal(t, tt.expPrimaryKubeEnv, actual) + }) + } +} diff --git a/acceptance/framework/connhelper/connect_helper.go b/acceptance/framework/connhelper/connect_helper.go index c5d677ba6f..0ec1dafd31 100644 --- a/acceptance/framework/connhelper/connect_helper.go +++ b/acceptance/framework/connhelper/connect_helper.go @@ -1,11 +1,16 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package connhelper import ( "context" "strconv" + "strings" "testing" "time" + terratestK8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/config" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" "github.com/hashicorp/consul-k8s/acceptance/framework/environment" @@ -41,14 +46,19 @@ type ConnectHelper struct { // ReleaseName is the name of the Consul cluster. ReleaseName string + // Ctx is used to deploy Consul Ctx environment.TestContext + // UseAppNamespace is used top optionally deploy applications into a separate namespace. + // If unset, the namespace associated with Ctx is used. + UseAppNamespace bool + Cfg *config.TestConfig // consulCluster is the cluster to use for the test. consulCluster consul.Cluster - // consulClient is the client used to test service mesh connectivity. - consulClient *api.Client + // ConsulClient is the client used to test service mesh connectivity. + ConsulClient *api.Client } // Setup creates a new cluster using the New*Cluster function and assigns it @@ -66,19 +76,27 @@ func (c *ConnectHelper) Setup(t *testing.T) { func (c *ConnectHelper) Install(t *testing.T) { logger.Log(t, "Installing Consul cluster") c.consulCluster.Create(t) - c.consulClient, _ = c.consulCluster.SetupConsulClient(t, c.Secure) + c.ConsulClient, _ = c.consulCluster.SetupConsulClient(t, c.Secure) } // Upgrade uses the existing Consul cluster and upgrades it using Helm values // set by the Secure, AutoEncrypt, and HelmValues fields. func (c *ConnectHelper) Upgrade(t *testing.T) { require.NotNil(t, c.consulCluster, "consulCluster must be set before calling Upgrade (Call Install first).") - require.NotNil(t, c.consulClient, "consulClient must be set before calling Upgrade (Call Install first).") + require.NotNil(t, c.ConsulClient, "ConsulClient must be set before calling Upgrade (Call Install first).") logger.Log(t, "upgrading Consul cluster") c.consulCluster.Upgrade(t, c.helmValues()) } +func (c *ConnectHelper) KubectlOptsForApp(t *testing.T) *terratestK8s.KubectlOptions { + opts := c.Ctx.KubectlOptions(t) + if !c.UseAppNamespace { + return opts + } + return c.Ctx.KubectlOptionsForNamespace(opts.Namespace + "-apps") +} + // DeployClientAndServer deploys a client and server pod to the Kubernetes // cluster which will be used to test service mesh connectivity. If the Secure // flag is true, a pre-check is done to ensure that the ACL tokens for the test @@ -91,9 +109,9 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) { // deployments because golang will execute them in reverse order // (i.e. the last registered cleanup function will be executed first). t.Cleanup(func() { - retrier := &retry.Timer{Timeout: 30 * time.Second, Wait: 100 * time.Millisecond} + retrier := &retry.Timer{Timeout: 60 * time.Second, Wait: 100 * time.Millisecond} retry.RunWith(retrier, t, func(r *retry.R) { - tokens, _, err := c.consulClient.ACL().TokenList(nil) + tokens, _, err := c.ConsulClient.ACL().TokenList(nil) require.NoError(r, err) for _, token := range tokens { require.NotContains(r, token.Description, StaticServerName) @@ -105,33 +123,92 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) { logger.Log(t, "creating static-server and static-client deployments") - k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-server-inject") - if c.Cfg.EnableTransparentProxy { - k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + c.SetupAppNamespace(t) + + opts := c.KubectlOptsForApp(t) + if c.Cfg.EnableCNI && c.Cfg.EnableOpenshift { + // On OpenShift with the CNI, we need to create a network attachment definition in the namespace + // where the applications are running, and the app deployment configs need to reference that network + // attachment definition. + + // TODO: A base fixture is the wrong place for these files + k8s.KubectlApply(t, opts, "../fixtures/bases/openshift/") + helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, func() { + k8s.KubectlDelete(t, opts, "../fixtures/bases/openshift/") + }) + + k8s.DeployKustomize(t, opts, c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-server-openshift") + if c.Cfg.EnableTransparentProxy { + k8s.DeployKustomize(t, opts, c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-openshift-tproxy") + } else { + k8s.DeployKustomize(t, opts, c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-openshift-inject") + } } else { - k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + if c.Cfg.EnableTransparentProxy { + k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-tproxy") + } else { + k8s.DeployKustomize(t, c.Ctx.KubectlOptions(t), c.Cfg.NoCleanupOnFailure, c.Cfg.DebugDirectory, "../fixtures/cases/static-client-inject") + } } // Check that both static-server and static-client have been injected and // now have 2 containers. - for _, labelSelector := range []string{"app=static-server", "app=static-client"} { - podList, err := c.Ctx.KubernetesClient(t).CoreV1().Pods(c.Ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{ - LabelSelector: labelSelector, + retry.RunWith( + &retry.Timer{Timeout: 30 * time.Second, Wait: 100 * time.Millisecond}, t, + func(r *retry.R) { + for _, labelSelector := range []string{"app=static-server", "app=static-client"} { + podList, err := c.Ctx.KubernetesClient(t).CoreV1(). + Pods(opts.Namespace). + List(context.Background(), metav1.ListOptions{ + LabelSelector: labelSelector, + FieldSelector: `status.phase=Running`, + }) + require.NoError(r, err) + require.Len(r, podList.Items, 1) + require.Len(r, podList.Items[0].Spec.Containers, 2) + } }) - require.NoError(t, err) - require.Len(t, podList.Items, 1) - require.Len(t, podList.Items[0].Spec.Containers, 2) +} + +// SetupAppNamespace creates a namespace where applications are deployed. This +// does nothing if UseAppNamespace is not set. The app namespace is relevant +// when testing with restricted PSA enforcement enabled. +func (c *ConnectHelper) SetupAppNamespace(t *testing.T) { + if !c.UseAppNamespace { + return + } + opts := c.KubectlOptsForApp(t) + // If we are deploying apps in another namespace, create the namespace. + + _, err := k8s.RunKubectlAndGetOutputE(t, opts, "create", "ns", opts.Namespace) + if err != nil && strings.Contains(err.Error(), "AlreadyExists") { + return } + require.NoError(t, err) + helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, func() { + k8s.RunKubectl(t, opts, "delete", "ns", opts.Namespace) + }) + + if c.Cfg.EnableRestrictedPSAEnforcement { + // Allow anything to run in the app namespace. + k8s.RunKubectl(t, opts, "label", "--overwrite", "ns", opts.Namespace, + "pod-security.kubernetes.io/enforce=privileged", + "pod-security.kubernetes.io/enforce-version=v1.24", + ) + } + } // TestConnectionFailureWithoutIntention ensures the connection to the static // server fails when no intentions are configured. func (c *ConnectHelper) TestConnectionFailureWithoutIntention(t *testing.T) { logger.Log(t, "checking that the connection is not successful because there's no intention") + opts := c.KubectlOptsForApp(t) if c.Cfg.EnableTransparentProxy { - k8s.CheckStaticServerConnectionFailing(t, c.Ctx.KubectlOptions(t), StaticClientName, "http://static-server") + k8s.CheckStaticServerConnectionFailing(t, opts, StaticClientName, "http://static-server") } else { - k8s.CheckStaticServerConnectionFailing(t, c.Ctx.KubectlOptions(t), StaticClientName, "http://localhost:1234") + k8s.CheckStaticServerConnectionFailing(t, opts, StaticClientName, "http://localhost:1234") } } @@ -139,7 +216,7 @@ func (c *ConnectHelper) TestConnectionFailureWithoutIntention(t *testing.T) { // the static-client pod. func (c *ConnectHelper) CreateIntention(t *testing.T) { logger.Log(t, "creating intention") - _, _, err := c.consulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ + _, _, err := c.ConsulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ Kind: api.ServiceIntentions, Name: StaticServerName, Sources: []*api.SourceIntention{ @@ -156,11 +233,12 @@ func (c *ConnectHelper) CreateIntention(t *testing.T) { // static-client pod once the intention is set. func (c *ConnectHelper) TestConnectionSuccess(t *testing.T) { logger.Log(t, "checking that connection is successful") + opts := c.KubectlOptsForApp(t) if c.Cfg.EnableTransparentProxy { // todo: add an assertion that the traffic is going through the proxy - k8s.CheckStaticServerConnectionSuccessful(t, c.Ctx.KubectlOptions(t), StaticClientName, "http://static-server") + k8s.CheckStaticServerConnectionSuccessful(t, opts, StaticClientName, "http://static-server") } else { - k8s.CheckStaticServerConnectionSuccessful(t, c.Ctx.KubectlOptions(t), StaticClientName, "http://localhost:1234") + k8s.CheckStaticServerConnectionSuccessful(t, opts, StaticClientName, "http://localhost:1234") } } @@ -171,8 +249,10 @@ func (c *ConnectHelper) TestConnectionFailureWhenUnhealthy(t *testing.T) { // Test that kubernetes readiness status is synced to Consul. // Create a file called "unhealthy" at "/tmp/" so that the readiness probe // of the static-server pod fails. + opts := c.KubectlOptsForApp(t) + logger.Log(t, "testing k8s -> consul health checks sync by making the static-server unhealthy") - k8s.RunKubectl(t, c.Ctx.KubectlOptions(t), "exec", "deploy/"+StaticServerName, "--", "touch", "/tmp/unhealthy") + k8s.RunKubectl(t, opts, "exec", "deploy/"+StaticServerName, "--", "touch", "/tmp/unhealthy") // The readiness probe should take a moment to be reflected in Consul, // CheckStaticServerConnection will retry until Consul marks the service @@ -184,20 +264,20 @@ func (c *ConnectHelper) TestConnectionFailureWhenUnhealthy(t *testing.T) { // other tests. logger.Log(t, "checking that connection is unsuccessful") if c.Cfg.EnableTransparentProxy { - k8s.CheckStaticServerConnectionMultipleFailureMessages(t, c.Ctx.KubectlOptions(t), StaticClientName, false, []string{ + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, opts, StaticClientName, false, []string{ "curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", "curl: (7) Failed to connect to static-server port 80: Connection refused", }, "", "http://static-server") } else { - k8s.CheckStaticServerConnectionMultipleFailureMessages(t, c.Ctx.KubectlOptions(t), StaticClientName, false, []string{ + k8s.CheckStaticServerConnectionMultipleFailureMessages(t, opts, StaticClientName, false, []string{ "curl: (56) Recv failure: Connection reset by peer", "curl: (52) Empty reply from server", }, "", "http://localhost:1234") } // Return the static-server to a "healthy state". - k8s.RunKubectl(t, c.Ctx.KubectlOptions(t), "exec", "deploy/"+StaticServerName, "--", "rm", "/tmp/unhealthy") + k8s.RunKubectl(t, opts, "exec", "deploy/"+StaticServerName, "--", "rm", "/tmp/unhealthy") } // helmValues uses the Secure and AutoEncrypt fields to set values for the Helm diff --git a/acceptance/framework/consul/cli_cluster.go b/acceptance/framework/consul/cli_cluster.go index 271f8f2eae..91952c80d1 100644 --- a/acceptance/framework/consul/cli_cluster.go +++ b/acceptance/framework/consul/cli_cluster.go @@ -94,16 +94,17 @@ func NewCLICluster( cli, err := cli.NewCLI() require.NoError(t, err) + require.Greater(t, len(cfg.KubeEnvs), 0) return &CLICluster{ ctx: ctx, helmOptions: hopts, kubectlOptions: kopts, - namespace: cfg.KubeNamespace, + namespace: cfg.GetPrimaryKubeEnv().KubeNamespace, values: values, releaseName: releaseName, kubernetesClient: ctx.KubernetesClient(t), - kubeConfig: cfg.Kubeconfig, - kubeContext: cfg.KubeContext, + kubeConfig: cfg.GetPrimaryKubeEnv().KubeConfig, + kubeContext: cfg.GetPrimaryKubeEnv().KubeContext, noCleanupOnFailure: cfg.NoCleanupOnFailure, debugDirectory: cfg.DebugDirectory, logger: logger, diff --git a/acceptance/framework/consul/helm_cluster_test.go b/acceptance/framework/consul/helm_cluster_test.go index af70812f9a..1f7b8b7d58 100644 --- a/acceptance/framework/consul/helm_cluster_test.go +++ b/acceptance/framework/consul/helm_cluster_test.go @@ -5,6 +5,7 @@ import ( "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/config" + "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/stretchr/testify/require" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" @@ -75,6 +76,11 @@ func (c *ctx) Name() string { func (c *ctx) KubectlOptions(_ *testing.T) *k8s.KubectlOptions { return &k8s.KubectlOptions{} } +func (c *ctx) KubectlOptionsForNamespace(ns string) *k8s.KubectlOptions { + return &k8s.KubectlOptions{} +} func (c *ctx) KubernetesClient(_ *testing.T) kubernetes.Interface { return fake.NewSimpleClientset() } + +var _ environment.TestContext = (*ctx)(nil) diff --git a/acceptance/framework/environment/environment.go b/acceptance/framework/environment/environment.go index 15121b97e3..96245cfde3 100644 --- a/acceptance/framework/environment/environment.go +++ b/acceptance/framework/environment/environment.go @@ -12,69 +12,60 @@ import ( ) const ( - DefaultContextName = "default" - SecondaryContextName = "secondary" + DefaultContextIndex = 0 ) // TestEnvironment represents the infrastructure environment of the test, // such as the kubernetes cluster(s) the test is running against. type TestEnvironment interface { DefaultContext(t *testing.T) TestContext - Context(t *testing.T, name string) TestContext + Context(t *testing.T, index int) TestContext } // TestContext represents a specific context a test needs, // for example, information about a specific Kubernetes cluster. type TestContext interface { KubectlOptions(t *testing.T) *k8s.KubectlOptions + // TODO: I don't love this. + KubectlOptionsForNamespace(ns string) *k8s.KubectlOptions KubernetesClient(t *testing.T) kubernetes.Interface } type KubernetesEnvironment struct { - contexts map[string]*kubernetesContext + contexts []*kubernetesContext } func NewKubernetesEnvironmentFromConfig(config *config.TestConfig) *KubernetesEnvironment { - defaultContext := NewContext(config.KubeNamespace, config.Kubeconfig, config.KubeContext) + // First kubeEnv is the default + defaultContext := NewContext(config.GetPrimaryKubeEnv().KubeNamespace, config.GetPrimaryKubeEnv().KubeConfig, config.GetPrimaryKubeEnv().KubeContext) // Create a kubernetes environment with default context. kenv := &KubernetesEnvironment{ - contexts: map[string]*kubernetesContext{ - DefaultContextName: defaultContext, + contexts: []*kubernetesContext{ + defaultContext, }, } - // Add secondary context if multi cluster tests are enabled. + // Add additional contexts if multi cluster tests are enabled. if config.EnableMultiCluster { - kenv.contexts[SecondaryContextName] = NewContext(config.SecondaryKubeNamespace, config.SecondaryKubeconfig, config.SecondaryKubeContext) - } - - return kenv -} - -func NewKubernetesEnvironmentFromContext(context *kubernetesContext) *KubernetesEnvironment { - // Create a kubernetes environment with default context. - kenv := &KubernetesEnvironment{ - contexts: map[string]*kubernetesContext{ - DefaultContextName: context, - }, + for _, v := range config.KubeEnvs[1:] { + kenv.contexts = append(kenv.contexts, NewContext(v.KubeNamespace, v.KubeConfig, v.KubeContext)) + } } return kenv } -func (k *KubernetesEnvironment) Context(t *testing.T, name string) TestContext { - ctx, ok := k.contexts[name] - require.Truef(t, ok, fmt.Sprintf("requested context %s not found", name)) - - return ctx +func (k *KubernetesEnvironment) Context(t *testing.T, index int) TestContext { + lenContexts := len(k.contexts) + require.Greater(t, lenContexts, index, fmt.Sprintf("context list does not contain an index %d, length is %d", index, lenContexts)) + return k.contexts[index] } func (k *KubernetesEnvironment) DefaultContext(t *testing.T) TestContext { - ctx, ok := k.contexts[DefaultContextName] - require.Truef(t, ok, "default context not found") - - return ctx + lenContexts := len(k.contexts) + require.Greater(t, lenContexts, DefaultContextIndex, fmt.Sprintf("context list does not contain an index %d, length is %d", DefaultContextIndex, lenContexts)) + return k.contexts[DefaultContextIndex] } type kubernetesContext struct { @@ -137,6 +128,14 @@ func (k kubernetesContext) KubectlOptions(t *testing.T) *k8s.KubectlOptions { return k.options } +func (k kubernetesContext) KubectlOptionsForNamespace(ns string) *k8s.KubectlOptions { + return &k8s.KubectlOptions{ + ContextName: k.kubeContextName, + ConfigPath: k.pathToKubeConfig, + Namespace: ns, + } +} + // KubernetesClientFromOptions takes KubectlOptions and returns Kubernetes API client. func KubernetesClientFromOptions(t *testing.T, options *k8s.KubectlOptions) kubernetes.Interface { configPath, err := options.GetConfigPath(t) diff --git a/acceptance/framework/flags/flags.go b/acceptance/framework/flags/flags.go index 5d90d74f9e..3e9b733047 100644 --- a/acceptance/framework/flags/flags.go +++ b/acceptance/framework/flags/flags.go @@ -1,9 +1,13 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package flags import ( "errors" "flag" "os" + "strings" "sync" "github.com/hashicorp/consul-k8s/acceptance/framework/config" @@ -11,14 +15,10 @@ import ( ) type TestFlags struct { - flagKubeconfig string - flagKubecontext string - flagNamespace string - - flagEnableMultiCluster bool - flagSecondaryKubeconfig string - flagSecondaryKubecontext string - flagSecondaryNamespace string + flagKubeconfigs listFlag + flagKubecontexts listFlag + flagKubeNamespaces listFlag + flagEnableMultiCluster bool flagEnableEnterprise bool flagEnterpriseLicense string @@ -27,21 +27,25 @@ type TestFlags struct { flagEnablePodSecurityPolicies bool - flagEnableCNI bool + flagEnableCNI bool + flagEnableRestrictedPSAEnforcement bool flagEnableTransparentProxy bool - flagHelmChartVersion string - flagConsulImage string - flagConsulK8sImage string - flagConsulVersion string - flagEnvoyImage string + flagHelmChartVersion string + flagConsulImage string + flagConsulK8sImage string + flagConsulDataplaneImage string + flagConsulVersion string + flagConsulDataplaneVersion string + flagEnvoyImage string flagNoCleanupOnFailure bool flagDebugDirectory string flagUseAKS bool + flagUseEKS bool flagUseGKE bool flagUseKind bool @@ -57,27 +61,36 @@ func NewTestFlags() *TestFlags { return t } -func (t *TestFlags) init() { - flag.StringVar(&t.flagKubeconfig, "kubeconfig", "", "The path to a kubeconfig file. If this is blank, "+ - "the default kubeconfig path (~/.kube/config) will be used.") - flag.StringVar(&t.flagKubecontext, "kubecontext", "", "The name of the Kubernetes context to use. If this is blank, "+ - "the context set as the current context will be used by default.") - flag.StringVar(&t.flagNamespace, "namespace", "", "The Kubernetes namespace to use for tests.") +type listFlag []string + +// String() returns a comma separated list in the form "var1,var2,var3". +func (f *listFlag) String() string { + return strings.Join(*f, ",") +} +func (f *listFlag) Set(value string) error { + *f = strings.Split(value, ",") + return nil +} + +func (t *TestFlags) init() { flag.StringVar(&t.flagConsulImage, "consul-image", "", "The Consul image to use for all tests.") flag.StringVar(&t.flagConsulK8sImage, "consul-k8s-image", "", "The consul-k8s image to use for all tests.") + flag.StringVar(&t.flagConsulDataplaneImage, "consul-dataplane-image", "", "The consul-dataplane image to use for all tests.") flag.StringVar(&t.flagConsulVersion, "consul-version", "", "The consul version used for all tests.") + flag.StringVar(&t.flagConsulDataplaneVersion, "consul-dataplane-version", "", "The consul-dataplane version used for all tests.") flag.StringVar(&t.flagHelmChartVersion, "helm-chart-version", config.HelmChartPath, "The helm chart used for all tests.") flag.StringVar(&t.flagEnvoyImage, "envoy-image", "", "The Envoy image to use for all tests.") + flag.Var(&t.flagKubeconfigs, "kubeconfigs", "The list of paths to a kubeconfig files. If this is blank, "+ + "the default kubeconfig path (~/.kube/config) will be used.") + flag.Var(&t.flagKubecontexts, "kube-contexts", "The list of names of the Kubernetes contexts to use. If this is blank, "+ + "the context set as the current context will be used by default.") + flag.Var(&t.flagKubeNamespaces, "kube-namespaces", "The list of Kubernetes namespaces to use for tests.") + flag.BoolVar(&t.flagEnableMultiCluster, "enable-multi-cluster", false, "If true, the tests that require multiple Kubernetes clusters will be run. "+ - "At least one of -secondary-kubeconfig or -secondary-kubecontext is required when this flag is used.") - flag.StringVar(&t.flagSecondaryKubeconfig, "secondary-kubeconfig", "", "The path to a kubeconfig file of the secondary k8s cluster. "+ - "If this is blank, the default kubeconfig path (~/.kube/config) will be used.") - flag.StringVar(&t.flagSecondaryKubecontext, "secondary-kubecontext", "", "The name of the Kubernetes context for the secondary cluster to use. "+ - "If this is blank, the context set as the current context will be used by default.") - flag.StringVar(&t.flagSecondaryNamespace, "secondary-namespace", "", "The Kubernetes namespace to use in the secondary k8s cluster.") + "The lists -kubeconfig or -kube-context must contain more than one entry when this flag is used.") flag.BoolVar(&t.flagEnableEnterprise, "enable-enterprise", false, "If true, the test suite will run tests for enterprise features. "+ @@ -94,6 +107,13 @@ func (t *TestFlags) init() { flag.BoolVar(&t.flagEnableCNI, "enable-cni", false, "If true, the test suite will run tests with consul-cni plugin enabled. "+ "In general, this will only run against tests that are mesh related (connect, mesh-gateway, peering, etc") + flag.BoolVar(&t.flagEnableRestrictedPSAEnforcement, "enable-restricted-psa-enforcement", false, + "If true, this indicates that Consul is being run in a namespace with restricted PSA enforcement enabled. "+ + "The tests do not configure Consul's namespace with PSA enforcement enabled. This must configured before tests are run. "+ + "The CNI and test applications need more privilege than is allowed in a restricted namespace. "+ + "When set, the CNI will be deployed into the kube-system namespace, and in supported test cases, applications "+ + "are deployed, by default, into a namespace named '-apps' instead of being deployed into the "+ + "Consul namespace.") flag.BoolVar(&t.flagEnableTransparentProxy, "enable-transparent-proxy", false, "If true, the test suite will run tests with transparent proxy enabled. "+ @@ -108,6 +128,8 @@ func (t *TestFlags) init() { flag.BoolVar(&t.flagUseAKS, "use-aks", false, "If true, the tests will assume they are running against an AKS cluster(s).") + flag.BoolVar(&t.flagUseEKS, "use-eks", false, + "If true, the tests will assume they are running against an EKS cluster(s).") flag.BoolVar(&t.flagUseGKE, "use-gke", false, "If true, the tests will assume they are running against a GKE cluster(s).") flag.BoolVar(&t.flagUseKind, "use-kind", false, @@ -123,14 +145,33 @@ func (t *TestFlags) init() { func (t *TestFlags) Validate() error { if t.flagEnableMultiCluster { - if t.flagSecondaryKubecontext == "" && t.flagSecondaryKubeconfig == "" { - return errors.New("at least one of -secondary-kubecontext or -secondary-kubeconfig flags must be provided if -enable-multi-cluster is set") + if len(t.flagKubecontexts) <= 1 && len(t.flagKubeconfigs) <= 1 { + return errors.New("at least two contexts must be included in -kube-contexts or -kubeconfigs if -enable-multi-cluster is set") + } + } + + if len(t.flagKubecontexts) != 0 && len(t.flagKubeconfigs) != 0 { + if len(t.flagKubecontexts) != len(t.flagKubeconfigs) { + return errors.New("-kube-contexts and -kubeconfigs are both set but are not of equal length") + } + } + + if len(t.flagKubecontexts) != 0 && len(t.flagKubeNamespaces) != 0 { + if len(t.flagKubecontexts) != len(t.flagKubeNamespaces) { + return errors.New("-kube-contexts and -kube-namespaces are both set but are not of equal length") + } + } + + if len(t.flagKubeNamespaces) != 0 && len(t.flagKubeconfigs) != 0 { + if len(t.flagKubeNamespaces) != len(t.flagKubeconfigs) { + return errors.New("-kube-namespaces and -kubeconfigs are both set but are not of equal length") } } if t.flagEnableEnterprise && t.flagEnterpriseLicense == "" { return errors.New("-enable-enterprise provided without setting env var CONSUL_ENT_LICENSE with consul license") } + return nil } @@ -139,40 +180,43 @@ func (t *TestFlags) TestConfigFromFlags() *config.TestConfig { // if the Version is empty consulVersion will be nil consulVersion, _ := version.NewVersion(t.flagConsulVersion) + consulDataplaneVersion, _ := version.NewVersion(t.flagConsulDataplaneVersion) + //vaultserverVersion, _ := version.NewVersion(t.flagVaultServerVersion) + kubeEnvs := config.NewKubeTestConfigList(t.flagKubeconfigs, t.flagKubecontexts, t.flagKubeNamespaces) - return &config.TestConfig{ - Kubeconfig: t.flagKubeconfig, - KubeContext: t.flagKubecontext, - KubeNamespace: t.flagNamespace, - - EnableMultiCluster: t.flagEnableMultiCluster, - SecondaryKubeconfig: t.flagSecondaryKubeconfig, - SecondaryKubeContext: t.flagSecondaryKubecontext, - SecondaryKubeNamespace: t.flagSecondaryNamespace, - + c := &config.TestConfig{ EnableEnterprise: t.flagEnableEnterprise, EnterpriseLicense: t.flagEnterpriseLicense, + KubeEnvs: kubeEnvs, + EnableMultiCluster: t.flagEnableMultiCluster, + EnableOpenshift: t.flagEnableOpenshift, EnablePodSecurityPolicies: t.flagEnablePodSecurityPolicies, - EnableCNI: t.flagEnableCNI, + EnableCNI: t.flagEnableCNI, + EnableRestrictedPSAEnforcement: t.flagEnableRestrictedPSAEnforcement, EnableTransparentProxy: t.flagEnableTransparentProxy, DisablePeering: t.flagDisablePeering, - HelmChartVersion: t.flagHelmChartVersion, - ConsulImage: t.flagConsulImage, - ConsulK8SImage: t.flagConsulK8sImage, - ConsulVersion: consulVersion, - EnvoyImage: t.flagEnvoyImage, + HelmChartVersion: t.flagHelmChartVersion, + ConsulImage: t.flagConsulImage, + ConsulK8SImage: t.flagConsulK8sImage, + ConsulDataplaneImage: t.flagConsulDataplaneImage, + ConsulVersion: consulVersion, + ConsulDataplaneVersion: consulDataplaneVersion, + EnvoyImage: t.flagEnvoyImage, NoCleanupOnFailure: t.flagNoCleanupOnFailure, DebugDirectory: tempDir, UseAKS: t.flagUseAKS, + UseEKS: t.flagUseEKS, UseGKE: t.flagUseGKE, UseKind: t.flagUseKind, } + + return c } diff --git a/acceptance/framework/flags/flags_test.go b/acceptance/framework/flags/flags_test.go index f66317a048..05e9ab19c0 100644 --- a/acceptance/framework/flags/flags_test.go +++ b/acceptance/framework/flags/flags_test.go @@ -8,9 +8,10 @@ import ( func TestFlags_validate(t *testing.T) { type fields struct { - flagEnableMultiCluster bool - flagSecondaryKubeconfig string - flagSecondaryKubecontext string + flagEnableMultiCluster bool + flagKubeConfigs listFlag + flagKubeContexts listFlag + flagNamespaces listFlag flagEnableEnt bool flagEntLicense string @@ -23,20 +24,16 @@ func TestFlags_validate(t *testing.T) { }{ { "no error by default", - fields{ - flagEnableMultiCluster: false, - flagSecondaryKubeconfig: "", - flagSecondaryKubecontext: "", - }, + fields{}, false, "", }, { "enable multi cluster: no error when multi cluster is disabled", fields{ - flagEnableMultiCluster: false, - flagSecondaryKubeconfig: "", - flagSecondaryKubecontext: "", + flagEnableMultiCluster: false, + flagKubeConfigs: listFlag{}, + flagKubeContexts: listFlag{}, }, false, "", @@ -44,19 +41,19 @@ func TestFlags_validate(t *testing.T) { { "enable multi cluster: errors when both secondary kubeconfig and kubecontext are empty", fields{ - flagEnableMultiCluster: true, - flagSecondaryKubeconfig: "", - flagSecondaryKubecontext: "", + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{}, + flagKubeContexts: listFlag{}, }, true, - "at least one of -secondary-kubecontext or -secondary-kubeconfig flags must be provided if -enable-multi-cluster is set", + "at least two contexts must be included in -kube-contexts or -kubeconfigs if -enable-multi-cluster is set", }, { "enable multi cluster: no error when secondary kubeconfig but not kubecontext is provided", fields{ - flagEnableMultiCluster: true, - flagSecondaryKubeconfig: "foo", - flagSecondaryKubecontext: "", + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{"foo", "bar"}, + flagKubeContexts: listFlag{}, }, false, "", @@ -64,9 +61,9 @@ func TestFlags_validate(t *testing.T) { { "enable multi cluster: no error when secondary kubecontext but not kubeconfig is provided", fields{ - flagEnableMultiCluster: true, - flagSecondaryKubeconfig: "", - flagSecondaryKubecontext: "foo", + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{}, + flagKubeContexts: listFlag{"foo", "bar"}, }, false, "", @@ -74,13 +71,54 @@ func TestFlags_validate(t *testing.T) { { "enable multi cluster: no error when both secondary kubecontext and kubeconfig are provided", fields{ - flagEnableMultiCluster: true, - flagSecondaryKubeconfig: "foo", - flagSecondaryKubecontext: "bar", + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{"foo", "bar"}, + flagKubeContexts: listFlag{"foo", "bar"}, + }, + false, + "", + }, + { + "enable multi cluster: no error when all of secondary kubecontext, kubeconfigs and namespaces are provided", + fields{ + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{"foo", "bar"}, + flagKubeContexts: listFlag{"foo", "bar"}, + flagNamespaces: listFlag{"foo", "bar"}, }, false, "", }, + { + "enable multi cluster: error when the list of kubeconfigs and kubecontexts do not match", + fields{ + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{"foo", "bar"}, + flagKubeContexts: listFlag{"foo"}, + }, + true, + "-kube-contexts and -kubeconfigs are both set but are not of equal length", + }, + { + "enable multi cluster: error when the list of kubeconfigs and namespaces do not match", + fields{ + flagEnableMultiCluster: true, + flagKubeConfigs: listFlag{"foo", "bar"}, + flagNamespaces: listFlag{"foo"}, + }, + true, + "-kube-namespaces and -kubeconfigs are both set but are not of equal length", + }, + { + "enable multi cluster: error when the list of kubecontexts and namespaces do not match", + fields{ + flagEnableMultiCluster: true, + flagKubeContexts: listFlag{"foo", "bar"}, + flagNamespaces: listFlag{"foo"}, + }, + true, + "-kube-contexts and -kube-namespaces are both set but are not of equal length", + }, { "enterprise license: error when only -enable-enterprise is true but env CONSUL_ENT_LICENSE is not provided", fields{ @@ -102,11 +140,12 @@ func TestFlags_validate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tf := &TestFlags{ - flagEnableMultiCluster: tt.fields.flagEnableMultiCluster, - flagSecondaryKubeconfig: tt.fields.flagSecondaryKubeconfig, - flagSecondaryKubecontext: tt.fields.flagSecondaryKubecontext, - flagEnableEnterprise: tt.fields.flagEnableEnt, - flagEnterpriseLicense: tt.fields.flagEntLicense, + flagEnableMultiCluster: tt.fields.flagEnableMultiCluster, + flagKubeconfigs: tt.fields.flagKubeConfigs, + flagKubecontexts: tt.fields.flagKubeContexts, + flagKubeNamespaces: tt.fields.flagNamespaces, + flagEnableEnterprise: tt.fields.flagEnableEnt, + flagEnterpriseLicense: tt.fields.flagEntLicense, } err := tf.Validate() if tt.wantErr { diff --git a/acceptance/framework/k8s/helpers.go b/acceptance/framework/k8s/helpers.go index 8d3da64612..7c3205928f 100644 --- a/acceptance/framework/k8s/helpers.go +++ b/acceptance/framework/k8s/helpers.go @@ -136,6 +136,7 @@ func CopySecret(t *testing.T, sourceContext, destContext environment.TestContext secret.ResourceVersion = "" require.NoError(r, err) }) + secret.Namespace = destContext.KubectlOptions(t).Namespace _, err = destContext.KubernetesClient(t).CoreV1().Secrets(destContext.KubectlOptions(t).Namespace).Create(context.Background(), secret, metav1.CreateOptions{}) require.NoError(t, err) } diff --git a/acceptance/framework/vault/helpers.go b/acceptance/framework/vault/helpers.go index 4726e246ae..2280aee8b2 100644 --- a/acceptance/framework/vault/helpers.go +++ b/acceptance/framework/vault/helpers.go @@ -167,6 +167,20 @@ func (config *KV2Secret) SaveSecretAndAddReadPolicy(t *testing.T, vaultClient *v path "%s" { capabilities = ["read"] }`, config.Path) + config.saveSecretAndAddPolicy(t, vaultClient, policy) +} + +// SaveSecretAndAddUpdatePolicy will create an update policy for the PolicyName +// on the KV2Secret and then will save the secret in the KV2 store. +func (config *KV2Secret) SaveSecretAndAddUpdatePolicy(t *testing.T, vaultClient *vapi.Client) { + policy := fmt.Sprintf(` + path "%s" { + capabilities = ["read", "update"] + }`, config.Path) + config.saveSecretAndAddPolicy(t, vaultClient, policy) +} + +func (config *KV2Secret) saveSecretAndAddPolicy(t *testing.T, vaultClient *vapi.Client, policy string) { // Create the Vault Policy for the secret. logger.Log(t, "Creating policy") err := vaultClient.Sys().PutPolicy(config.PolicyName, policy) diff --git a/acceptance/framework/vault/vault_cluster.go b/acceptance/framework/vault/vault_cluster.go index c8105fa806..dd6cbe4a68 100644 --- a/acceptance/framework/vault/vault_cluster.go +++ b/acceptance/framework/vault/vault_cluster.go @@ -379,12 +379,12 @@ func (v *VaultCluster) initAndUnseal(t *testing.T) { v.logger.Logf(t, "initializing and unsealing Vault") namespace := v.helmOptions.KubectlOptions.Namespace - retrier := &retry.Timer{Timeout: 2 * time.Minute, Wait: 1 * time.Second} + retrier := &retry.Timer{Timeout: 4 * time.Minute, Wait: 1 * time.Second} retry.RunWith(retrier, t, func(r *retry.R) { // Wait for vault server pod to be running so that we can create Vault client without errors. serverPod, err := v.kubernetesClient.CoreV1().Pods(namespace).Get(context.Background(), fmt.Sprintf("%s-vault-0", v.releaseName), metav1.GetOptions{}) require.NoError(r, err) - require.Equal(r, serverPod.Status.Phase, corev1.PodRunning) + require.Equal(r, corev1.PodRunning, serverPod.Status.Phase) // Set up the client so that we can make API calls to initialize and unseal. v.vaultClient = v.SetupVaultClient(t) diff --git a/acceptance/go.mod b/acceptance/go.mod index 55acec04ba..c912b6c35a 100644 --- a/acceptance/go.mod +++ b/acceptance/go.mod @@ -1,14 +1,15 @@ module github.com/hashicorp/consul-k8s/acceptance -go 1.19 +go 1.20 require ( github.com/gruntwork-io/terratest v0.31.2 github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3 - github.com/hashicorp/consul/api v1.16.0 - github.com/hashicorp/consul/sdk v0.12.0 + github.com/hashicorp/consul/api v1.20.0 + github.com/hashicorp/consul/sdk v0.13.1 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.6.0 + github.com/hashicorp/serf v0.10.1 github.com/hashicorp/vault/api v1.2.0 github.com/stretchr/testify v1.7.2 gopkg.in/yaml.v2 v2.4.0 @@ -53,7 +54,6 @@ require ( github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/serf v0.10.1 // indirect github.com/hashicorp/vault/sdk v0.2.1 // indirect github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/imdario/mergo v0.3.12 // indirect @@ -82,12 +82,12 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/urfave/cli v1.22.2 // indirect go.uber.org/atomic v1.7.0 // indirect - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/net v0.13.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect - golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/term v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect diff --git a/acceptance/go.sum b/acceptance/go.sum index 2b154c10f7..1a9e13a090 100644 --- a/acceptance/go.sum +++ b/acceptance/go.sum @@ -364,10 +364,10 @@ github.com/gruntwork-io/terratest v0.31.2 h1:xvYHA80MUq5kx670dM18HInewOrrQrAN+Xb github.com/gruntwork-io/terratest v0.31.2/go.mod h1:EEgJie28gX/4AD71IFqgMj6e99KP5mi81hEtzmDjxTo= github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3 h1:4wROIZB8Y4cN/wPILChc2zQ/q00z1VyJitdgyLbITdU= github.com/hashicorp/consul-k8s/control-plane v0.0.0-20221117191905-0b1cc2b631e3/go.mod h1:j9Db/whkzvNC+KP2GftY0HxxleLm9swxXjlu3tYaOAw= -github.com/hashicorp/consul/api v1.16.0 h1:Vf/QVFIwz+PdHR4T4lSwYzLULtbHVq0BheXCUAKP50M= -github.com/hashicorp/consul/api v1.16.0/go.mod h1:GJI1Sif0Wc/iYyqg7EXHJV37IPush6eJTewvYdF9uO8= -github.com/hashicorp/consul/sdk v0.12.0 h1:qsNQToBEs9v5MUWOv/JhiOu4wPeq9VdK7Jcgf7shOrU= -github.com/hashicorp/consul/sdk v0.12.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= +github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= +github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= +github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= +github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= 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= @@ -715,8 +715,8 @@ golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/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-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.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= 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= @@ -803,9 +803,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b 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-20210520170846-37e1c6afe023/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 h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= @@ -902,15 +901,15 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/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-20210927094055-39ccf1dd6fa6/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-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -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.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -920,8 +919,8 @@ 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.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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= diff --git a/acceptance/tests/api-gateway/example_test.go b/acceptance/tests/api-gateway/example_test.go new file mode 100644 index 0000000000..b324ac31fe --- /dev/null +++ b/acceptance/tests/api-gateway/example_test.go @@ -0,0 +1,64 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Rename package to your test package. +package example + +import ( + "context" + "testing" + + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestExample(t *testing.T) { + // Get test configuration. + cfg := suite.Config() + + // Get the default context. + ctx := suite.Environment().DefaultContext(t) + + // Create Helm values for the Helm install. + helmValues := map[string]string{ + "exampleFeature.enabled": "true", + } + + // Generate a random name for this test. + releaseName := helpers.RandomName() + + // Create a new Consul cluster object. + consulCluster := consul.NewHelmCluster(t, helmValues, ctx, cfg, releaseName) + + // Create the Consul cluster with Helm. + consulCluster.Create(t) + + // Make test assertions. + + // To run kubectl commands, you need to get KubectlOptions from the test context. + // There are a number of kubectl commands available in the helpers/kubectl.go file. + // For example, to call 'kubectl apply' from the test write the following: + k8s.KubectlApply(t, ctx.KubectlOptions(t), "path/to/config") + + // Clean up any Kubernetes resources you have created + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + k8s.KubectlDelete(t, ctx.KubectlOptions(t), "path/to/config") + }) + + // Similarly, you can obtain Kubernetes client from your test context. + // You can use it to, for example, read all services in a namespace: + k8sClient := ctx.KubernetesClient(t) + services, err := k8sClient.CoreV1().Services(ctx.KubectlOptions(t).Namespace).List(context.Background(), metav1.ListOptions{}) + require.NoError(t, err) + require.NotNil(t, services.Items) + + // To make Consul API calls, you can get the Consul client from the consulCluster object, + // indicating whether the client needs to be secure or not (i.e. whether TLS and ACLs are enabled on the Consul cluster): + consulClient, _ := consulCluster.SetupConsulClient(t, true) + consulServices, _, err := consulClient.Catalog().Services(nil) + require.NoError(t, err) + require.NotNil(t, consulServices) +} diff --git a/acceptance/tests/api-gateway/main_test.go b/acceptance/tests/api-gateway/main_test.go new file mode 100644 index 0000000000..f92fff8a59 --- /dev/null +++ b/acceptance/tests/api-gateway/main_test.go @@ -0,0 +1,37 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// Rename package to your test package. +package example + +import ( + "testing" + + testsuite "github.com/hashicorp/consul-k8s/acceptance/framework/suite" +) + +var suite testsuite.Suite + +func TestMain(m *testing.M) { + // First, uncomment the line below to create a new suite so that all flags are parsed. + /* + suite = framework.NewSuite(m) + */ + + // If the test suite needs to run only when certain test flags are passed, + // you need to handle that in the TestMain function. + // Uncomment and modify example code below if that is the case. + /* + if suite.Config().EnableExampleFeature { + os.Exit(suite.Run()) + } else { + fmt.Println("Skipping example feature tests because -enable-example-feature is not set") + os.Exit(0) + } + */ + + // If the test suite should run in every case, uncomment the line below. + /* + os.Exit(suite.Run()) + */ +} diff --git a/acceptance/tests/cli/cli_install_test.go b/acceptance/tests/cli/cli_install_test.go index d45093dd59..36f5f583a9 100644 --- a/acceptance/tests/cli/cli_install_test.go +++ b/acceptance/tests/cli/cli_install_test.go @@ -71,7 +71,7 @@ func TestInstall(t *testing.T) { retry.RunWith(retrier, t, func(r *retry.R) { for podName := range list { out, err := cli.Run(t, ctx.KubectlOptions(t), "proxy", "read", podName) - require.NoError(t, err) + require.NoError(r, err) output := string(out) logger.Log(t, output) @@ -109,7 +109,7 @@ func TestInstall(t *testing.T) { proxyOut, err := cli.Run(t, ctx.KubectlOptions(t), "troubleshoot", "proxy", "-pod", clientPodName, "-upstream-ip", serverIP) require.NoError(t, err) - require.Regexp(t, "upstream resources are valid", string(proxyOut)) + require.Regexp(t, "Upstream resources are valid", string(proxyOut)) logger.Log(t, string(proxyOut)) } else { // With tproxy disabled and explicit upstreams we need the envoy-id of the server @@ -117,7 +117,7 @@ func TestInstall(t *testing.T) { proxyOut, err := cli.Run(t, ctx.KubectlOptions(t), "troubleshoot", "proxy", "-pod", clientPodName, "-upstream-envoy-id", "static-server") require.NoError(t, err) - require.Regexp(t, "upstream resources are valid", string(proxyOut)) + require.Regexp(t, "Upstream resources are valid", string(proxyOut)) logger.Log(t, string(proxyOut)) } diff --git a/acceptance/tests/cloud/basic_test.go b/acceptance/tests/cloud/basic_test.go new file mode 100644 index 0000000000..8278309ff3 --- /dev/null +++ b/acceptance/tests/cloud/basic_test.go @@ -0,0 +1,233 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cloud + +import ( + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "strings" + "testing" + "time" + + terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/environment" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/serf/testutil/retry" + "github.com/stretchr/testify/require" +) + +type TokenResponse struct { + Token string `json:"token"` +} + +var ( + resourceSecretName = "resource-sec-name" + resourceSecretKey = "resource-sec-key" + resourceSecretKeyValue = "organization/11eb1a35-aac0-f7c7-8fe1-0242ac110008/project/11eb1a35-ab64-d576-8fe1-0242ac110008/hashicorp.consul.global-network-manager.cluster/TEST" + + clientIDSecretName = "clientid-sec-name" + clientIDSecretKey = "clientid-sec-key" + clientIDSecretKeyValue = "clientid" + + clientSecretName = "client-sec-name" + clientSecretKey = "client-sec-key" + clientSecretKeyValue = "client-secret" + + apiHostSecretName = "apihost-sec-name" + apiHostSecretKey = "apihost-sec-key" + apiHostSecretKeyValue = "fake-server:443" + + authUrlSecretName = "authurl-sec-name" + authUrlSecretKey = "authurl-sec-key" + authUrlSecretKeyValue = "https://fake-server:443" + + scadaAddressSecretName = "scadaaddress-sec-name" + scadaAddressSecretKey = "scadaaddress-sec-key" + scadaAddressSecretKeyValue = "fake-server:443" +) + +// The fake-server has a requestToken endpoint to retrieve the token. +func requestToken(endpoint string) (string, error) { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + + client := &http.Client{Transport: tr} + url := fmt.Sprintf("https://%s/token", endpoint) + req, err := http.NewRequest("GET", url, nil) + if err != nil { + fmt.Println("Error creating request:", err) + return "", errors.New("error creating request") + } + + // Perform the request + resp, err := client.Do(req) + if err != nil { + fmt.Println("Error sending request:", err) + return "", errors.New("error making request") + } + defer resp.Body.Close() + + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error reading response:", err) + return "", errors.New("error reading body") + } + + var tokenResponse TokenResponse + err = json.Unmarshal(body, &tokenResponse) + if err != nil { + fmt.Println("Error parsing response:", err) + return "", errors.New("error parsing body") + } + + return tokenResponse.Token, nil + +} + +func TestBasicCloud(t *testing.T) { + ctx := suite.Environment().DefaultContext(t) + + kubectlOptions := ctx.KubectlOptions(t) + ns := kubectlOptions.Namespace + k8sClient := environment.KubernetesClientFromOptions(t, kubectlOptions) + + cfg := suite.Config() + + if cfg.HCPResourceID != "" { + resourceSecretKeyValue = cfg.HCPResourceID + } + consul.CreateK8sSecret(t, k8sClient, cfg, ns, resourceSecretName, resourceSecretKey, resourceSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, clientIDSecretName, clientIDSecretKey, clientIDSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, clientSecretName, clientSecretKey, clientSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, apiHostSecretName, apiHostSecretKey, apiHostSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, authUrlSecretName, authUrlSecretKey, authUrlSecretKeyValue) + consul.CreateK8sSecret(t, k8sClient, cfg, ns, scadaAddressSecretName, scadaAddressSecretKey, scadaAddressSecretKeyValue) + + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/cloud/hcp-mock") + podName, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "get", "pod", "-l", "app=fake-server", "-o", `jsonpath="{.items[0].metadata.name}"`) + podName = strings.ReplaceAll(podName, "\"", "") + if err != nil { + logger.Log(t, "error finding pod name") + return + } + logger.Log(t, "fake-server pod name:"+podName) + localPort := terratestk8s.GetAvailablePort(t) + tunnel := terratestk8s.NewTunnelWithLogger( + ctx.KubectlOptions(t), + terratestk8s.ResourceTypePod, + podName, + localPort, + 443, + logger.TestLogger{}) + + // Retry creating the port forward since it can fail occasionally. + retry.RunWith(&retry.Counter{Wait: 5 * time.Second, Count: 60}, t, func(r *retry.R) { + // NOTE: It's okay to pass in `t` to ForwardPortE despite being in a retry + // because we're using ForwardPortE (not ForwardPort) so the `t` won't + // get used to fail the test, just for logging. + require.NoError(r, tunnel.ForwardPortE(t)) + }) + + logger.Log(t, "fake-server addr:"+tunnel.Endpoint()) + consulToken, err := requestToken(tunnel.Endpoint()) + if err != nil { + logger.Log(t, "error finding consul token") + return + } + tunnel.Close() + logger.Log(t, "consul test token :"+consulToken) + + releaseName := helpers.RandomName() + + helmValues := map[string]string{ + "global.cloud.enabled": "true", + "global.cloud.resourceId.secretName": resourceSecretName, + "global.cloud.resourceId.secretKey": resourceSecretKey, + + "global.cloud.clientId.secretName": clientIDSecretName, + "global.cloud.clientId.secretKey": clientIDSecretKey, + + "global.cloud.clientSecret.secretName": clientSecretName, + "global.cloud.clientSecret.secretKey": clientSecretKey, + + "global.cloud.apiHost.secretName": apiHostSecretName, + "global.cloud.apiHost.secretKey": apiHostSecretKey, + + "global.cloud.authUrl.secretName": authUrlSecretName, + "global.cloud.authUrl.secretKey": authUrlSecretKey, + + "global.cloud.scadaAddress.secretName": scadaAddressSecretName, + "global.cloud.scadaAddress.secretKey": scadaAddressSecretKey, + "connectInject.default": "true", + + // TODO: Follow up with this bug + "global.acls.manageSystemACLs": "false", + "global.gossipEncryption.autoGenerate": "false", + "global.tls.enabled": "true", + "global.tls.enableAutoEncrypt": "true", + // TODO: Take this out + + "telemetryCollector.enabled": "true", + "telemetryCollector.image": cfg.ConsulCollectorImage, + "telemetryCollector.cloud.clientId.secretName": clientIDSecretName, + "telemetryCollector.cloud.clientId.secretKey": clientIDSecretKey, + + "telemetryCollector.cloud.clientSecret.secretName": clientSecretName, + "telemetryCollector.cloud.clientSecret.secretKey": clientSecretKey, + // Either we set the global.trustedCAs (make sure it's idented exactly) or we + // set TLS to insecure + + "telemetryCollector.extraEnvironmentVars.HCP_API_TLS": "insecure", + "telemetryCollector.extraEnvironmentVars.HCP_AUTH_TLS": "insecure", + "telemetryCollector.extraEnvironmentVars.HCP_SCADA_TLS": "insecure", + "telemetryCollector.extraEnvironmentVars.OTLP_EXPORTER_TLS": "insecure", + + "server.extraEnvironmentVars.HCP_API_TLS": "insecure", + "server.extraEnvironmentVars.HCP_AUTH_TLS": "insecure", + "server.extraEnvironmentVars.HCP_SCADA_TLS": "insecure", + + // This is pregenerated CA used for testing. It can be replaced at any time and isn't + // meant for anything other than testing + // "global.trustedCAs[0]": `-----BEGIN CERTIFICATE----- + // MIICrjCCAZYCCQD5LxMcnMY8rDANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDDA5m + // YWtlLXNlcnZlci1jYTAeFw0yMzA1MTkxMjIwMzhaFw0zMzA1MTYxMjIwMzhaMBkx + // FzAVBgNVBAMMDmZha2Utc2VydmVyLWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A + // MIIBCgKCAQEAwhbiII7sMultedFzQVhVZz5Ti+9lWrpZb8y0ZR6NaNvoxDPX151t + // Adh5NegSeH/+351iDBGZHhmKECtBuk8FJgk88O7y8A7Yg+/lyeZd0SJTEeiYUe7d + // sSaBTYSmixyn6s15Y5MVp9gM7t2YXrocRkFxDtdhLMWf0zwzJEwDouFMMiFZw5II + // yDbI6UfwKyB8C8ln10+TcczbheaOMQ1jGn35YWAG/LEdutU6DO2Y/GZYQ41nyLF1 + // klqh34USQPVQSQW7R7GiDxyhh1fGaDF6RAzH4RerzQSNvvTHmBXIGurB/Hnu1n3p + // CwWeatWMU5POy1es73S/EPM0NpWD5RabSwIDAQABMA0GCSqGSIb3DQEBCwUAA4IB + // AQBayoTltSW55PvKVp9cmqGOBMlkIMKPd6Ny4bCb/3UF+3bzQmIblh3O3kEt7WoY + // fA9vp+6cSRGVqgBfR2bi40RrerLNA79yywIZjfBMteNuRoul5VeD+mLyFCo4197r + // Atl2TEx2kl2V8rjCsEBcTqKqetVOMLYEZ2tbCeUt1A/K7OzaJfHgelEYcsVt68Q9 + // /BLoo2UXfOpRrcsx7u7s5HPVbG3bx+1MvGJZ2C3i0B6agnkGDzEpoM4KZGxEefB9 + // DOHIJfie9d9BQD52nZh3SGHz0b3vfJ430XrQmaNZ26fuIEyIYrpvyAhBXckj2iTD + // 1TXpqr/1D7EUbddktyhXTK9e + // -----END CERTIFICATE-----`, + } + if cfg.ConsulImage != "" { + helmValues["global.image"] = cfg.ConsulImage + } + if cfg.ConsulCollectorImage != "" { + helmValues["telemetryCollector.image"] = cfg.ConsulCollectorImage + } + + consulCluster := consul.NewHelmCluster(t, helmValues, suite.Environment().DefaultContext(t), suite.Config(), releaseName) + consulCluster.Create(t) + + logger.Log(t, "creating static-server deployment") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/bases/static-server") + // time.Sleep(1 * time.Hour) + // TODO: add in test assertions here +} diff --git a/acceptance/tests/cloud/main_test.go b/acceptance/tests/cloud/main_test.go new file mode 100644 index 0000000000..85d1867933 --- /dev/null +++ b/acceptance/tests/cloud/main_test.go @@ -0,0 +1,18 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package cloud + +import ( + "os" + "testing" + + testsuite "github.com/hashicorp/consul-k8s/acceptance/framework/suite" +) + +var suite testsuite.Suite + +func TestMain(m *testing.M) { + suite = testsuite.NewSuite(m) + os.Exit(suite.Run()) +} diff --git a/acceptance/tests/connect/connect_external_servers_test.go b/acceptance/tests/connect/connect_external_servers_test.go index 5132b66e7a..56d7b16bc0 100644 --- a/acceptance/tests/connect/connect_external_servers_test.go +++ b/acceptance/tests/connect/connect_external_servers_test.go @@ -27,6 +27,8 @@ func TestConnectInject_ExternalServers(t *testing.T) { caseName := fmt.Sprintf("secure: %t", secure) t.Run(caseName, func(t *testing.T) { cfg := suite.Config() + cfg.SkipWhenOpenshiftAndCNI(t) + ctx := suite.Environment().DefaultContext(t) serverHelmValues := map[string]string{ diff --git a/acceptance/tests/connect/connect_inject_namespaces_test.go b/acceptance/tests/connect/connect_inject_namespaces_test.go index db48465bda..dbfc4725e4 100644 --- a/acceptance/tests/connect/connect_inject_namespaces_test.go +++ b/acceptance/tests/connect/connect_inject_namespaces_test.go @@ -31,6 +31,7 @@ func TestConnectInjectNamespaces(t *testing.T) { if !cfg.EnableEnterprise { t.Skipf("skipping this test because -enable-enterprise is not set") } + cfg.SkipWhenOpenshiftAndCNI(t) cases := []struct { name string @@ -243,6 +244,7 @@ func TestConnectInjectNamespaces_CleanupController(t *testing.T) { if !cfg.EnableEnterprise { t.Skipf("skipping this test because -enable-enterprise is not set") } + cfg.SkipWhenOpenshiftAndCNI(t) consulDestNS := "consul-dest" cases := []struct { diff --git a/acceptance/tests/connect/connect_inject_test.go b/acceptance/tests/connect/connect_inject_test.go index 2e75846884..2909f8e5d0 100644 --- a/acceptance/tests/connect/connect_inject_test.go +++ b/acceptance/tests/connect/connect_inject_test.go @@ -35,11 +35,12 @@ func TestConnectInject(t *testing.T) { releaseName := helpers.RandomName() connHelper := connhelper.ConnectHelper{ - ClusterKind: consul.Helm, - Secure: c.secure, - ReleaseName: releaseName, - Ctx: ctx, - Cfg: cfg, + ClusterKind: consul.Helm, + Secure: c.secure, + ReleaseName: releaseName, + Ctx: ctx, + UseAppNamespace: cfg.EnableRestrictedPSAEnforcement, + Cfg: cfg, } connHelper.Setup(t) @@ -63,6 +64,9 @@ func TestConnectInject_CleanupKilledPods(t *testing.T) { name := fmt.Sprintf("secure: %t", secure) t.Run(name, func(t *testing.T) { cfg := suite.Config() + + cfg.SkipWhenOpenshiftAndCNI(t) + ctx := suite.Environment().DefaultContext(t) helmValues := map[string]string{ @@ -116,6 +120,18 @@ func TestConnectInject_CleanupKilledPods(t *testing.T) { } } }) + // Ensure the token is cleaned up + if secure { + retry.Run(t, func(r *retry.R) { + tokens, _, err := consulClient.ACL().TokenList(nil) + require.NoError(r, err) + for _, t := range tokens { + if strings.Contains(t.Description, podName) { + r.Errorf("Found a token that was supposed to be deleted for pod %v", podName) + } + } + }) + } }) } } @@ -131,15 +147,14 @@ func TestConnectInject_MultiportServices(t *testing.T) { name := fmt.Sprintf("secure: %t", secure) t.Run(name, func(t *testing.T) { cfg := suite.Config() - ctx := suite.Environment().DefaultContext(t) + cfg.SkipWhenOpenshiftAndCNI(t) - // Multi port apps don't work with transparent proxy. - if cfg.EnableTransparentProxy { - t.Skipf("skipping this test because transparent proxy is enabled") - } + ctx := suite.Environment().DefaultContext(t) helmValues := map[string]string{ "connectInject.enabled": "true", + // Enable DNS so we can test that DNS redirection _isn't_ set in the pod. + "dns.enabled": "true", "global.tls.enabled": strconv.FormatBool(secure), "global.acls.manageSystemACLs": strconv.FormatBool(secure), diff --git a/acceptance/tests/connect/connect_proxy_lifecycle_test.go b/acceptance/tests/connect/connect_proxy_lifecycle_test.go new file mode 100644 index 0000000000..ae70a0fdeb --- /dev/null +++ b/acceptance/tests/connect/connect_proxy_lifecycle_test.go @@ -0,0 +1,208 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package connect + +import ( + "context" + "fmt" + "strconv" + "strings" + "testing" + "time" + + "github.com/gruntwork-io/terratest/modules/k8s" + "github.com/hashicorp/consul-k8s/acceptance/framework/connhelper" + "github.com/hashicorp/consul-k8s/acceptance/framework/consul" + "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" + "github.com/hashicorp/consul-k8s/acceptance/framework/logger" + "github.com/hashicorp/consul/sdk/testutil/retry" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type LifecycleShutdownConfig struct { + secure bool + helmValues map[string]string +} + +const ( + helmDrainListenersKey = "connectInject.sidecarProxy.lifecycle.defaultEnableShutdownDrainListeners" + helmGracePeriodSecondsKey = "connectInject.sidecarProxy.lifecycle.defaultShutdownGracePeriodSeconds" +) + +// Test the endpoints controller cleans up force-killed pods. +func TestConnectInject_ProxyLifecycleShutdown(t *testing.T) { + t.Skipf("skiping this test, will be re-added in a future commit") + cfg := suite.Config() + cfg.SkipWhenOpenshiftAndCNI(t) + + for _, testCfg := range []LifecycleShutdownConfig{ + {secure: false, helmValues: map[string]string{ + helmDrainListenersKey: "true", + helmGracePeriodSecondsKey: "15", + }}, + {secure: true, helmValues: map[string]string{ + helmDrainListenersKey: "true", + helmGracePeriodSecondsKey: "15", + }}, + {secure: false, helmValues: map[string]string{ + helmDrainListenersKey: "false", + helmGracePeriodSecondsKey: "15", + }}, + {secure: true, helmValues: map[string]string{ + helmDrainListenersKey: "false", + helmGracePeriodSecondsKey: "15", + }}, + {secure: false, helmValues: map[string]string{ + helmDrainListenersKey: "false", + helmGracePeriodSecondsKey: "0", + }}, + {secure: true, helmValues: map[string]string{ + helmDrainListenersKey: "false", + helmGracePeriodSecondsKey: "0", + }}, + } { + // Determine if listeners should be expected to drain inbound connections + var drainListenersEnabled bool + var err error + val, ok := testCfg.helmValues[helmDrainListenersKey] + if ok { + drainListenersEnabled, err = strconv.ParseBool(val) + require.NoError(t, err) + } + + // Determine expected shutdown grace period + var gracePeriodSeconds int64 + val, ok = testCfg.helmValues[helmGracePeriodSecondsKey] + if ok { + gracePeriodSeconds, err = strconv.ParseInt(val, 10, 64) + require.NoError(t, err) + } else { + // Half of the helm default to speed tests up + gracePeriodSeconds = 15 + } + + name := fmt.Sprintf("secure: %t, drainListeners: %t, gracePeriodSeconds: %d", testCfg.secure, drainListenersEnabled, gracePeriodSeconds) + t.Run(name, func(t *testing.T) { + ctx := suite.Environment().DefaultContext(t) + releaseName := helpers.RandomName() + + connHelper := connhelper.ConnectHelper{ + ClusterKind: consul.Helm, + Secure: testCfg.secure, + ReleaseName: releaseName, + Ctx: ctx, + Cfg: cfg, + HelmValues: testCfg.helmValues, + } + + connHelper.Setup(t) + connHelper.Install(t) + connHelper.DeployClientAndServer(t) + + // TODO: should this move into connhelper.DeployClientAndServer? + logger.Log(t, "waiting for static-client and static-server to be registered with Consul") + retry.Run(t, func(r *retry.R) { + for _, name := range []string{ + "static-client", + "static-client-sidecar-proxy", + "static-server", + "static-server-sidecar-proxy", + } { + logger.Logf(t, "checking for %s service in Consul catalog", name) + instances, _, err := connHelper.ConsulClient.Catalog().Service(name, "", nil) + r.Check(err) + + if len(instances) != 1 { + r.Errorf("expected 1 instance of %s", name) + } + } + }) + + if testCfg.secure { + connHelper.TestConnectionFailureWithoutIntention(t) + connHelper.CreateIntention(t) + } + + connHelper.TestConnectionSuccess(t) + + // Get static-client pod name + ns := ctx.KubectlOptions(t).Namespace + pods, err := ctx.KubernetesClient(t).CoreV1().Pods(ns).List( + context.Background(), + metav1.ListOptions{ + LabelSelector: "app=static-client", + }, + ) + require.NoError(t, err) + require.Len(t, pods.Items, 1) + clientPodName := pods.Items[0].Name + + var terminationGracePeriod int64 = 60 + logger.Logf(t, "killing the %q pod with %dseconds termination grace period", clientPodName, terminationGracePeriod) + err = ctx.KubernetesClient(t).CoreV1().Pods(ns).Delete(context.Background(), clientPodName, metav1.DeleteOptions{GracePeriodSeconds: &terminationGracePeriod}) + require.NoError(t, err) + + // Exec into terminating pod, not just any static-client pod + args := []string{"exec", clientPodName, "-c", connhelper.StaticClientName, "--", "curl", "-vvvsSf"} + + if cfg.EnableTransparentProxy { + args = append(args, "http://static-server") + } else { + args = append(args, "http://localhost:1234") + } + + if gracePeriodSeconds > 0 { + // Ensure outbound requests are still successful during grace + // period. + retry.RunWith(&retry.Timer{Timeout: time.Duration(gracePeriodSeconds) * time.Second, Wait: 2 * time.Second}, t, func(r *retry.R) { + output, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), args...) + require.NoError(r, err) + require.Condition(r, func() bool { + exists := false + if strings.Contains(output, "curl: (7) Failed to connect") { + exists = true + } + return !exists + }) + }) + + // If listener draining is enabled, ensure inbound + // requests are rejected during grace period. + // connHelper.TestConnectionSuccess(t) + } else { + // Ensure outbound requests fail because proxy has terminated + retry.RunWith(&retry.Timer{Timeout: time.Duration(terminationGracePeriod) * time.Second, Wait: 2 * time.Second}, t, func(r *retry.R) { + output, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), args...) + require.Error(r, err) + require.Condition(r, func() bool { + exists := false + if strings.Contains(output, "curl: (7) Failed to connect") { + exists = true + } + return exists + }) + }) + } + + logger.Log(t, "ensuring pod is deregistered after termination") + retry.Run(t, func(r *retry.R) { + for _, name := range []string{ + "static-client", + "static-client-sidecar-proxy", + } { + logger.Logf(t, "checking for %s service in Consul catalog", name) + instances, _, err := connHelper.ConsulClient.Catalog().Service(name, "", nil) + r.Check(err) + + for _, instance := range instances { + if strings.Contains(instance.ServiceID, clientPodName) { + r.Errorf("%s is still registered", instance.ServiceID) + } + } + } + }) + }) + } +} diff --git a/acceptance/tests/fixtures/bases/cloud/hcp-mock/deployment.yaml b/acceptance/tests/fixtures/bases/cloud/hcp-mock/deployment.yaml new file mode 100644 index 0000000000..7278557cdb --- /dev/null +++ b/acceptance/tests/fixtures/bases/cloud/hcp-mock/deployment.yaml @@ -0,0 +1,29 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: fake-server +spec: + replicas: 1 + selector: + matchLabels: + app: fake-server + template: + metadata: + name: fake-server + labels: + app: fake-server + spec: + containers: + - name: fake-server + # TODO: move this to a hashicorp mirror + image: docker.io/chaapppie/fakeserver:latest + ports: + - containerPort: 443 + name: https + - containerPort: 8080 + name: http + serviceAccountName: fake-server + terminationGracePeriodSeconds: 0 # so deletion is quick diff --git a/acceptance/tests/fixtures/bases/cloud/hcp-mock/kustomization.yaml b/acceptance/tests/fixtures/bases/cloud/hcp-mock/kustomization.yaml new file mode 100644 index 0000000000..dc9c951ab2 --- /dev/null +++ b/acceptance/tests/fixtures/bases/cloud/hcp-mock/kustomization.yaml @@ -0,0 +1,10 @@ + + +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - deployment.yaml + - service.yaml + - serviceaccount.yaml + diff --git a/acceptance/tests/fixtures/bases/cloud/hcp-mock/service.yaml b/acceptance/tests/fixtures/bases/cloud/hcp-mock/service.yaml new file mode 100644 index 0000000000..0cc6f1b9ce --- /dev/null +++ b/acceptance/tests/fixtures/bases/cloud/hcp-mock/service.yaml @@ -0,0 +1,17 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: v1 +kind: Service +metadata: + name: fake-server +spec: + selector: + app: fake-server + ports: + - name: https + port: 443 + targetPort: 443 + - name: http + port: 8080 + targetPort: 8080 diff --git a/acceptance/tests/fixtures/bases/cloud/hcp-mock/serviceaccount.yaml b/acceptance/tests/fixtures/bases/cloud/hcp-mock/serviceaccount.yaml new file mode 100644 index 0000000000..f52d9640cd --- /dev/null +++ b/acceptance/tests/fixtures/bases/cloud/hcp-mock/serviceaccount.yaml @@ -0,0 +1,7 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: fake-server diff --git a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml index 413eb68d22..11fa6f37ea 100644 --- a/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml +++ b/acceptance/tests/fixtures/bases/crds-oss/servicedefaults.yaml @@ -18,7 +18,9 @@ spec: passiveHealthCheck: interval: 1s maxFailures: 10 - enforcing_consecutive_5xx: 60 + enforcingConsecutive5xx: 60 + maxEjectionPercent: 100 + baseEjectionTime: 20s - name: "bar" limits: maxConnections: 5 @@ -36,4 +38,4 @@ spec: required: false arguments: payloadPassthrough: false - arn: arn:aws:lambda:us-east-1:111111111111:function:lambda-1234 \ No newline at end of file + arn: arn:aws:lambda:us-east-1:111111111111:function:lambda-1234 diff --git a/acceptance/tests/fixtures/bases/ingress/ingress.yaml b/acceptance/tests/fixtures/bases/ingress/ingress.yaml new file mode 100644 index 0000000000..7632a187d6 --- /dev/null +++ b/acceptance/tests/fixtures/bases/ingress/ingress.yaml @@ -0,0 +1,23 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: test-ingress + annotations: + kubernetes.io/ingress.class: "alb" + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/target-type: ip +spec: + rules: + - http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: static-server + port: + number: 80 + host: test.acceptance.com \ No newline at end of file diff --git a/acceptance/tests/fixtures/bases/ingress/kustomization.yaml b/acceptance/tests/fixtures/bases/ingress/kustomization.yaml new file mode 100644 index 0000000000..09fd1b7d0b --- /dev/null +++ b/acceptance/tests/fixtures/bases/ingress/kustomization.yaml @@ -0,0 +1,5 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ingress.yaml diff --git a/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml b/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml new file mode 100644 index 0000000000..4b3f7948ee --- /dev/null +++ b/acceptance/tests/fixtures/bases/openshift/network-attachment.yaml @@ -0,0 +1,17 @@ +apiVersion: "k8s.cni.cncf.io/v1" +kind: NetworkAttachmentDefinition +metadata: + name: consul-cni +spec: + config: '{ + "cniVersion": "0.3.1", + "type": "consul-cni", + "cni_bin_dir": "/var/lib/cni/bin", + "cni_net_dir": "/etc/kubernetes/cni/net.d", + "kubeconfig": "ZZZ-consul-cni-kubeconfig", + "log_level": "debug", + "multus": true, + "name": "consul-cni", + "type": "consul-cni" + }' + diff --git a/acceptance/tests/fixtures/bases/static-server/deployment.yaml b/acceptance/tests/fixtures/bases/static-server/deployment.yaml index 1c724b0d37..2a9f3a8adb 100644 --- a/acceptance/tests/fixtures/bases/static-server/deployment.yaml +++ b/acceptance/tests/fixtures/bases/static-server/deployment.yaml @@ -15,7 +15,9 @@ spec: spec: containers: - name: static-server - image: docker.mirror.hashicorp.services/hashicorp/http-echo:latest + # Using alpine vs latest as there is a build issue with M1s. Also other tests in multiport-app reference + # alpine so standardizing this. + image: docker.mirror.hashicorp.services/hashicorp/http-echo:alpine args: - -text="hello world" - -listen=:8080 diff --git a/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml new file mode 100644 index 0000000000..4d4a53b87f --- /dev/null +++ b/acceptance/tests/fixtures/cases/static-client-openshift-inject/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-openshift-inject/patch.yaml b/acceptance/tests/fixtures/cases/static-client-openshift-inject/patch.yaml new file mode 100644 index 0000000000..8cc6d10411 --- /dev/null +++ b/acceptance/tests/fixtures/cases/static-client-openshift-inject/patch.yaml @@ -0,0 +1,14 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-client +spec: + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "true" + "consul.hashicorp.com/connect-service-upstreams": "static-server:1234" + "k8s.v1.cni.cncf.io/networks": '[{ "name":"consul-cni" }]' diff --git a/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml b/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml new file mode 100644 index 0000000000..4d4a53b87f --- /dev/null +++ b/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../bases/static-client + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/patch.yaml b/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/patch.yaml new file mode 100644 index 0000000000..3b9c91fcc0 --- /dev/null +++ b/acceptance/tests/fixtures/cases/static-client-openshift-tproxy/patch.yaml @@ -0,0 +1,18 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +# When using the CNI on OpenShift, we need to specify the +# network attachment definition for the pods to use. This assumes +# that one named 'consul-cni' was created by the acceptance tests. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-client +spec: + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "true" + "k8s.v1.cni.cncf.io/networks": '[{ "name":"consul-cni" }]' + diff --git a/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml b/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml new file mode 100644 index 0000000000..bc50c78adf --- /dev/null +++ b/acceptance/tests/fixtures/cases/static-server-openshift/kustomization.yaml @@ -0,0 +1,8 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resources: + - ../../bases/static-server + +patchesStrategicMerge: + - patch.yaml \ No newline at end of file diff --git a/acceptance/tests/fixtures/cases/static-server-openshift/patch.yaml b/acceptance/tests/fixtures/cases/static-server-openshift/patch.yaml new file mode 100644 index 0000000000..8e2ed857f3 --- /dev/null +++ b/acceptance/tests/fixtures/cases/static-server-openshift/patch.yaml @@ -0,0 +1,42 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: static-server +spec: + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "true" + "k8s.v1.cni.cncf.io/networks": '[{ "name":"consul-cni" }]' + spec: + containers: + - name: static-server + image: docker.mirror.hashicorp.services/kschoche/http-echo:latest + args: + - -text="hello world" + - -listen=:8080 + ports: + - containerPort: 8080 + name: http + livenessProbe: + httpGet: + port: 8080 + initialDelaySeconds: 1 + failureThreshold: 1 + periodSeconds: 1 + startupProbe: + httpGet: + port: 8080 + initialDelaySeconds: 1 + failureThreshold: 30 + periodSeconds: 1 + readinessProbe: + exec: + command: ['sh', '-c', 'test ! -f /tmp/unhealthy'] + initialDelaySeconds: 1 + failureThreshold: 1 + periodSeconds: 1 + serviceAccountName: static-server diff --git a/acceptance/tests/partitions/main_test.go b/acceptance/tests/partitions/main_test.go index b2758a572c..2052a1b74e 100644 --- a/acceptance/tests/partitions/main_test.go +++ b/acceptance/tests/partitions/main_test.go @@ -13,10 +13,12 @@ var suite testsuite.Suite func TestMain(m *testing.M) { suite = testsuite.NewSuite(m) - if suite.Config().EnableMultiCluster { + expectedNumberOfClusters := 2 + if suite.Config().EnableMultiCluster && suite.Config().IsExpectedClusterCount(expectedNumberOfClusters) { os.Exit(suite.Run()) } else { - fmt.Println("Skipping partitions tests because -enable-multi-cluster is not set") + fmt.Println(fmt.Sprintf("Skipping partitions tests because either -enable-multi-cluster is "+ + "not set or the number of clusters did not match the expected count of %d", expectedNumberOfClusters)) os.Exit(0) } } diff --git a/acceptance/tests/partitions/partitions_connect_test.go b/acceptance/tests/partitions/partitions_connect_test.go index f205ac67e2..ae2f0688f6 100644 --- a/acceptance/tests/partitions/partitions_connect_test.go +++ b/acceptance/tests/partitions/partitions_connect_test.go @@ -8,7 +8,6 @@ import ( terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -82,7 +81,7 @@ func TestPartitions_Connect(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { defaultPartitionClusterContext := env.DefaultContext(t) - secondaryPartitionClusterContext := env.Context(t, environment.SecondaryContextName) + secondaryPartitionClusterContext := env.Context(t, 1) commonHelmValues := map[string]string{ "global.adminPartitions.enabled": "true", diff --git a/acceptance/tests/partitions/partitions_sync_test.go b/acceptance/tests/partitions/partitions_sync_test.go index e29ef18c78..8d761d0765 100644 --- a/acceptance/tests/partitions/partitions_sync_test.go +++ b/acceptance/tests/partitions/partitions_sync_test.go @@ -8,7 +8,6 @@ import ( terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -79,7 +78,7 @@ func TestPartitions_Sync(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { primaryClusterContext := env.DefaultContext(t) - secondaryClusterContext := env.Context(t, environment.SecondaryContextName) + secondaryClusterContext := env.Context(t, 1) commonHelmValues := map[string]string{ "global.adminPartitions.enabled": "true", diff --git a/acceptance/tests/peering/main_test.go b/acceptance/tests/peering/main_test.go index 12bb35afd5..df9812171c 100644 --- a/acceptance/tests/peering/main_test.go +++ b/acceptance/tests/peering/main_test.go @@ -13,10 +13,12 @@ var suite testsuite.Suite func TestMain(m *testing.M) { suite = testsuite.NewSuite(m) - if suite.Config().EnableMultiCluster && !suite.Config().DisablePeering { + expectedNumberOfClusters := 2 + if suite.Config().EnableMultiCluster && suite.Config().IsExpectedClusterCount(expectedNumberOfClusters) && !suite.Config().DisablePeering { os.Exit(suite.Run()) } else { - fmt.Println("Skipping peering tests because either -enable-multi-cluster is not set or -disable-peering is set") + fmt.Println(fmt.Sprintf("Skipping peerings tests because either -enable-multi-cluster is "+ + "not set, -disable-peering is set, or the number of clusters did not match the expected count of %d", expectedNumberOfClusters)) os.Exit(0) } } diff --git a/acceptance/tests/peering/peering_connect_namespaces_test.go b/acceptance/tests/peering/peering_connect_namespaces_test.go index 3e243b4781..b52d2793d0 100644 --- a/acceptance/tests/peering/peering_connect_namespaces_test.go +++ b/acceptance/tests/peering/peering_connect_namespaces_test.go @@ -9,7 +9,6 @@ import ( terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -90,7 +89,7 @@ func TestPeering_ConnectNamespaces(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { staticServerPeerClusterContext := env.DefaultContext(t) - staticClientPeerClusterContext := env.Context(t, environment.SecondaryContextName) + staticClientPeerClusterContext := env.Context(t, 1) commonHelmValues := map[string]string{ "global.peering.enabled": "true", diff --git a/acceptance/tests/peering/peering_connect_test.go b/acceptance/tests/peering/peering_connect_test.go index 0761854497..470ada08a1 100644 --- a/acceptance/tests/peering/peering_connect_test.go +++ b/acceptance/tests/peering/peering_connect_test.go @@ -9,7 +9,6 @@ import ( terratestk8s "github.com/gruntwork-io/terratest/modules/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -50,7 +49,7 @@ func TestPeering_Connect(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { staticServerPeerClusterContext := env.DefaultContext(t) - staticClientPeerClusterContext := env.Context(t, environment.SecondaryContextName) + staticClientPeerClusterContext := env.Context(t, 1) commonHelmValues := map[string]string{ "global.peering.enabled": "true", diff --git a/acceptance/tests/sync/sync_catalog_test.go b/acceptance/tests/sync/sync_catalog_test.go index c4f873fcbd..3e3880b618 100644 --- a/acceptance/tests/sync/sync_catalog_test.go +++ b/acceptance/tests/sync/sync_catalog_test.go @@ -76,3 +76,84 @@ func TestSyncCatalog(t *testing.T) { }) } } + +// Test that sync catalog works in both the default installation and +// the secure installation when TLS and ACLs are enabled with an Ingress resource. +// The test will create a test service and a pod and will +// wait for the service to be synced *to* consul. +func TestSyncCatalogWithIngress(t *testing.T) { + cfg := suite.Config() + if cfg.EnableCNI { + t.Skipf("skipping because -enable-cni is set and sync catalog is already tested with regular tproxy") + } + if !cfg.UseEKS { + t.Skipf("skipping because -use-eks is not set and the ingress test only runs on EKS") + } + + cases := map[string]struct { + secure bool + }{ + "non-secure": {secure: false}, + "secure": {secure: true}, + } + + for name, c := range cases { + t.Run(name, func(t *testing.T) { + ctx := suite.Environment().DefaultContext(t) + helmValues := map[string]string{ + "syncCatalog.enabled": "true", + "syncCatalog.ingres.enabled": "true", + "global.tls.enabled": strconv.FormatBool(c.secure), + "global.acls.manageSystemACLs": strconv.FormatBool(c.secure), + } + + releaseName := helpers.RandomName() + consulCluster := consul.NewHelmCluster(t, helmValues, ctx, suite.Config(), releaseName) + + logger.Log(t, "creating ingress resource") + retry.Run(t, func(r *retry.R) { + // Retry the kubectl apply because we've seen sporadic + // "connection refused" errors where the mutating webhook + // endpoint fails initially. + out, err := k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "apply", "-k", "../fixtures/bases/ingress") + require.NoError(r, err, out) + helpers.Cleanup(t, cfg.NoCleanupOnFailure, func() { + // Ignore errors here because if the test ran as expected + // the custom resources will have been deleted. + k8s.RunKubectlAndGetOutputE(t, ctx.KubectlOptions(t), "delete", "-k", "../fixtures/bases/ingress") + }) + }) + + consulCluster.Create(t) + + logger.Log(t, "creating a static-server with a service") + k8s.DeployKustomize(t, ctx.KubectlOptions(t), suite.Config().NoCleanupOnFailure, suite.Config().DebugDirectory, "../fixtures/bases/static-server") + + consulClient, _ := consulCluster.SetupConsulClient(t, c.secure) + + logger.Log(t, "checking that the service has been synced to Consul") + var services map[string][]string + syncedServiceName := fmt.Sprintf("static-server-%s", ctx.KubectlOptions(t).Namespace) + counter := &retry.Counter{Count: 10, Wait: 5 * time.Second} + retry.RunWith(counter, t, func(r *retry.R) { + var err error + services, _, err = consulClient.Catalog().Services(nil) + require.NoError(r, err) + if _, ok := services[syncedServiceName]; !ok { + r.Errorf("service '%s' is not in Consul's list of services %s", syncedServiceName, services) + } + }) + + service, _, err := consulClient.Catalog().Service(syncedServiceName, "", nil) + require.NoError(t, err) + require.Len(t, service, 1) + require.Equal(t, "test.acceptance.com", service[0].Address) + require.Equal(t, []string{"k8s"}, service[0].ServiceTags) + filter := fmt.Sprintf("ServiceID == %q", service[0].ServiceID) + healthChecks, _, err := consulClient.Health().Checks(syncedServiceName, &api.QueryOptions{Filter: filter}) + require.NoError(t, err) + require.Len(t, healthChecks, 1) + require.Equal(t, api.HealthPassing, healthChecks[0].Status) + }) + } +} diff --git a/acceptance/tests/vault/main_test.go b/acceptance/tests/vault/main_test.go index 1d3a5a5842..6b38cad022 100644 --- a/acceptance/tests/vault/main_test.go +++ b/acceptance/tests/vault/main_test.go @@ -1,6 +1,7 @@ package vault import ( + "fmt" "os" "testing" @@ -11,5 +12,13 @@ var suite testsuite.Suite func TestMain(m *testing.M) { suite = testsuite.NewSuite(m) - os.Exit(suite.Run()) + + expectedNumberOfClusters := 2 + if suite.Config().EnableMultiCluster && suite.Config().IsExpectedClusterCount(expectedNumberOfClusters) { + os.Exit(suite.Run()) + } else { + fmt.Println(fmt.Sprintf("Skipping vault tests because either -enable-multi-cluster is "+ + "not set or the number of clusters did not match the expected count of %d", expectedNumberOfClusters)) + os.Exit(0) + } } diff --git a/acceptance/tests/vault/vault_partitions_test.go b/acceptance/tests/vault/vault_partitions_test.go index 0fff6726dc..609147b676 100644 --- a/acceptance/tests/vault/vault_partitions_test.go +++ b/acceptance/tests/vault/vault_partitions_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" @@ -23,7 +22,7 @@ func TestVault_Partitions(t *testing.T) { env := suite.Environment() cfg := suite.Config() serverClusterCtx := env.DefaultContext(t) - clientClusterCtx := env.Context(t, environment.SecondaryContextName) + clientClusterCtx := env.Context(t, 1) ns := serverClusterCtx.KubectlOptions(t).Namespace const secondaryPartition = "secondary" diff --git a/acceptance/tests/vault/vault_test.go b/acceptance/tests/vault/vault_test.go index cf0c926b22..4d43d8bb5b 100644 --- a/acceptance/tests/vault/vault_test.go +++ b/acceptance/tests/vault/vault_test.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/consul-k8s/acceptance/framework/logger" "github.com/hashicorp/consul-k8s/acceptance/framework/portforward" "github.com/hashicorp/consul-k8s/acceptance/framework/vault" + "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/hashicorp/go-uuid" "github.com/hashicorp/go-version" "github.com/stretchr/testify/require" @@ -31,6 +32,29 @@ const ( // TestVault installs Vault, bootstraps it with secrets, policies, and Kube Auth Method. // It then configures Consul to use vault as the backend and checks that it works. func TestVault(t *testing.T) { + cases := map[string]struct { + autoBootstrap bool + }{ + "manual ACL bootstrap": {}, + "automatic ACL bootstrap": { + autoBootstrap: true, + }, + } + for name, c := range cases { + c := c + t.Run(name, func(t *testing.T) { + testVault(t, c.autoBootstrap) + }) + } +} + +// testVault is the implementation for TestVault: +// +// - testAutoBootstrap = false. Test when ACL bootstrapping has already occurred. +// The test pre-populates a Vault secret with the bootstrap token. +// - testAutoBootstrap = true. Test that server-acl-init automatically ACL bootstraps +// consul and writes the bootstrap token to Vault. +func testVault(t *testing.T, testAutoBootstrap bool) { cfg := suite.Config() ctx := suite.Environment().DefaultContext(t) kubectlOptions := ctx.KubectlOptions(t) @@ -123,16 +147,22 @@ func TestVault(t *testing.T) { licenseSecret.SaveSecretAndAddReadPolicy(t, vaultClient) } - // Bootstrap Token - bootstrapToken, err := uuid.GenerateUUID() - require.NoError(t, err) bootstrapTokenSecret := &vault.KV2Secret{ Path: "consul/data/secret/bootstrap", Key: "token", - Value: bootstrapToken, + Value: "", PolicyName: "bootstrap", } - bootstrapTokenSecret.SaveSecretAndAddReadPolicy(t, vaultClient) + if testAutoBootstrap { + bootstrapTokenSecret.SaveSecretAndAddUpdatePolicy(t, vaultClient) + } else { + id, err := uuid.GenerateUUID() + require.NoError(t, err) + bootstrapTokenSecret.Value = id + bootstrapTokenSecret.SaveSecretAndAddReadPolicy(t, vaultClient) + } + + bootstrapToken := bootstrapTokenSecret.Value // ------------------------- // Additional Auth Roles @@ -265,6 +295,26 @@ func TestVault(t *testing.T) { logger.Logf(t, "Wait %d seconds for certificates to rotate....", expirationInSeconds) time.Sleep(time.Duration(expirationInSeconds) * time.Second) + if testAutoBootstrap { + logger.Logf(t, "Validating the ACL bootstrap token was stored in Vault.") + timer := &retry.Timer{Timeout: 10 * time.Second, Wait: 1 * time.Second} + retry.RunWith(timer, t, func(r *retry.R) { + secret, err := vaultClient.Logical().Read("consul/data/secret/bootstrap") + require.NoError(r, err) + + data, ok := secret.Data["data"].(map[string]interface{}) + require.True(r, ok) + require.NotNil(r, data) + + tok, ok := data["token"].(string) + require.True(r, ok) + require.NotEmpty(r, tok) + + // Set bootstrapToken for subsequent validations. + bootstrapToken = tok + }) + } + // Validate that the gossip encryption key is set correctly. logger.Log(t, "Validating the gossip key has been set correctly.") consulCluster.ACLToken = bootstrapToken diff --git a/acceptance/tests/vault/vault_wan_fed_test.go b/acceptance/tests/vault/vault_wan_fed_test.go index 0a08810463..a65702907f 100644 --- a/acceptance/tests/vault/vault_wan_fed_test.go +++ b/acceptance/tests/vault/vault_wan_fed_test.go @@ -41,7 +41,7 @@ func TestVault_WANFederationViaGateways(t *testing.T) { } primaryCtx := suite.Environment().DefaultContext(t) - secondaryCtx := suite.Environment().Context(t, environment.SecondaryContextName) + secondaryCtx := suite.Environment().Context(t, 1) ns := primaryCtx.KubectlOptions(t).Namespace diff --git a/acceptance/tests/wan-federation/main_test.go b/acceptance/tests/wan-federation/main_test.go index 197a3181e8..4f6b0a7b54 100644 --- a/acceptance/tests/wan-federation/main_test.go +++ b/acceptance/tests/wan-federation/main_test.go @@ -13,10 +13,12 @@ var suite testsuite.Suite func TestMain(m *testing.M) { suite = testsuite.NewSuite(m) - if suite.Config().EnableMultiCluster { + expectedNumberOfClusters := 2 + if suite.Config().EnableMultiCluster && suite.Config().IsExpectedClusterCount(expectedNumberOfClusters) { os.Exit(suite.Run()) } else { - fmt.Println("Skipping wan federation tests because -enable-multi-cluster is not set") + fmt.Println(fmt.Sprintf("Skipping wan-federation tests because either -enable-multi-cluster is "+ + "not set or the number of clusters did not match the expected count of %d", expectedNumberOfClusters)) os.Exit(0) } } diff --git a/acceptance/tests/wan-federation/wan_federation_test.go b/acceptance/tests/wan-federation/wan_federation_test.go index 1b23633077..d347695faa 100644 --- a/acceptance/tests/wan-federation/wan_federation_test.go +++ b/acceptance/tests/wan-federation/wan_federation_test.go @@ -6,12 +6,11 @@ import ( "strconv" "testing" + "github.com/hashicorp/consul-k8s/acceptance/framework/connhelper" "github.com/hashicorp/consul-k8s/acceptance/framework/consul" - "github.com/hashicorp/consul-k8s/acceptance/framework/environment" "github.com/hashicorp/consul-k8s/acceptance/framework/helpers" "github.com/hashicorp/consul-k8s/acceptance/framework/k8s" "github.com/hashicorp/consul-k8s/acceptance/framework/logger" - "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -41,12 +40,8 @@ func TestWANFederation(t *testing.T) { env := suite.Environment() cfg := suite.Config() - if cfg.UseKind { - t.Skipf("skipping wan federation tests as they currently fail on Kind even though they work on other clouds.") - } - primaryContext := env.DefaultContext(t) - secondaryContext := env.Context(t, environment.SecondaryContextName) + secondaryContext := env.Context(t, 1) primaryHelmValues := map[string]string{ "global.datacenter": "dc1", @@ -84,6 +79,7 @@ func TestWANFederation(t *testing.T) { federationSecret, err := primaryContext.KubernetesClient(t).CoreV1().Secrets(primaryContext.KubectlOptions(t).Namespace).Get(context.Background(), federationSecretName, metav1.GetOptions{}) require.NoError(t, err) federationSecret.ResourceVersion = "" + federationSecret.Namespace = secondaryContext.KubectlOptions(t).Namespace _, err = secondaryContext.KubernetesClient(t).CoreV1().Secrets(secondaryContext.KubectlOptions(t).Namespace).Create(context.Background(), federationSecret, metav1.CreateOptions{}) require.NoError(t, err) @@ -159,30 +155,43 @@ func TestWANFederation(t *testing.T) { k8s.KubectlDeleteK(t, secondaryContext.KubectlOptions(t), kustomizeDir) }) + primaryHelper := connhelper.ConnectHelper{ + Secure: c.secure, + ReleaseName: releaseName, + Ctx: primaryContext, + UseAppNamespace: cfg.EnableRestrictedPSAEnforcement, + Cfg: cfg, + ConsulClient: primaryClient, + } + secondaryHelper := connhelper.ConnectHelper{ + Secure: c.secure, + ReleaseName: releaseName, + Ctx: secondaryContext, + UseAppNamespace: cfg.EnableRestrictedPSAEnforcement, + Cfg: cfg, + ConsulClient: secondaryClient, + } + + // When restricted PSA enforcement is enabled on the Consul + // namespace, deploy the test apps to a different unrestricted + // namespace because they can't run in a restricted namespace. + // This creates the app namespace only if necessary. + primaryHelper.SetupAppNamespace(t) + secondaryHelper.SetupAppNamespace(t) + // Check that we can connect services over the mesh gateways logger.Log(t, "creating static-server in dc2") - k8s.DeployKustomize(t, secondaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") + k8s.DeployKustomize(t, secondaryHelper.KubectlOptsForApp(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-server-inject") logger.Log(t, "creating static-client in dc1") - k8s.DeployKustomize(t, primaryContext.KubectlOptions(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") + k8s.DeployKustomize(t, primaryHelper.KubectlOptsForApp(t), cfg.NoCleanupOnFailure, cfg.DebugDirectory, "../fixtures/cases/static-client-multi-dc") if c.secure { - logger.Log(t, "creating intention") - _, _, err = primaryClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{ - Kind: api.ServiceIntentions, - Name: "static-server", - Sources: []*api.SourceIntention{ - { - Name: StaticClientName, - Action: api.IntentionActionAllow, - }, - }, - }, nil) - require.NoError(t, err) + primaryHelper.CreateIntention(t) } logger.Log(t, "checking that connection is successful") - k8s.CheckStaticServerConnectionSuccessful(t, primaryContext.KubectlOptions(t), StaticClientName, "http://localhost:1234") + k8s.CheckStaticServerConnectionSuccessful(t, primaryHelper.KubectlOptsForApp(t), StaticClientName, "http://localhost:1234") }) } } diff --git a/charts/consul/Chart.yaml b/charts/consul/Chart.yaml index 41b95611e6..23f092c8ac 100644 --- a/charts/consul/Chart.yaml +++ b/charts/consul/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v2 name: consul -version: 1.1.0-dev -appVersion: 1.14.4 -kubeVersion: ">=1.21.0-0" +version: 1.1.5-dev +appVersion: 1.15-dev +kubeVersion: ">=1.22.0-0" description: Official HashiCorp Consul Chart home: https://www.consul.io icon: https://raw.githubusercontent.com/hashicorp/consul-k8s/main/assets/icon.png @@ -13,13 +13,13 @@ annotations: artifacthub.io/prerelease: true artifacthub.io/images: | - name: consul - image: hashicorp/consul:1.14.4 + image: docker.mirror.hashicorp.services/hashicorppreview/consul:1.15-dev - name: consul-k8s-control-plane - image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.1.0-dev + image: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.1.5-dev - name: consul-dataplane - image: hashicorp/consul-dataplane:1.0.1 + image: docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.1-dev - name: envoy - image: envoyproxy/envoy:v1.23.1 + image: envoyproxy/envoy:v1.25.9 artifacthub.io/license: MPL-2.0 artifacthub.io/links: | - name: Documentation diff --git a/charts/consul/templates/_helpers.tpl b/charts/consul/templates/_helpers.tpl index 3552c8c209..f5efa015bc 100644 --- a/charts/consul/templates/_helpers.tpl +++ b/charts/consul/templates/_helpers.tpl @@ -15,6 +15,47 @@ as well as the global.name setting. {{- end -}} {{- end -}} + +{{- define "consul.restrictedSecurityContext" -}} +{{- if not .Values.global.enablePodSecurityPolicies -}} +{{/* +To be compatible with the 'restricted' Pod Security Standards profile, we +should set this securityContext on containers whenever possible. + +In OpenShift < 4.11 the restricted SCC disallows setting most of these fields, +so we do not set any for simplicity (and because that's how it was configured +prior to adding restricted PSA support here). In OpenShift >= 4.11, the new +restricted-v2 SCC allows setting these in the securityContext, and by setting +them we avoid PSA warnings that are enabled by default. + +We use the K8s version as a proxy for the OpenShift version because there is a +1:1 mapping of versions. OpenShift 4.11 corresponds to K8s 1.24.x. +*/}} +{{- if (or (not .Values.global.openshift.enabled) (and (ge .Capabilities.KubeVersion.Major "1") (ge .Capabilities.KubeVersion.Minor "24"))) -}} +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + add: + - NET_BIND_SERVICE + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault +{{- end -}} +{{- if not .Values.global.openshift.enabled -}} +{{/* +We must set runAsUser or else the root user will be used in some cases and +containers will fail to start due to runAsNonRoot above (e.g. +tls-init-cleanup). On OpenShift, runAsUser is set automatically. We pick user 100 +because it is a non-root user id that exists in the consul, consul-dataplane, +and consul-k8s-control-plane images. +*/}} + runAsUser: 100 +{{- end -}} +{{- end -}} +{{- end -}} + {{- define "consul.vaultSecretTemplate" -}} | {{ "{{" }}- with secret "{{ .secretName }}" -{{ "}}" }} @@ -387,3 +428,39 @@ Usage: {{ template "consul.validateCloudSecretKeys" . }} {{- end }} {{- end }} {{- end -}} + + +{{/* +Fails if temeletryCollector.clientId or telemetryCollector.clientSecret exist and one of other secrets is nil or empty. +- telemetryCollector.cloud.clientId.secretName +- telemetryCollector.cloud.clientSecret.secretName +- global.cloud.resourceId.secretName + +Usage: {{ template "consul.validateTelemetryCollectorCloud" . }} + +*/}} +{{- define "consul.validateTelemetryCollectorCloud" -}} +{{- if (and .Values.telemetryCollector.cloud.clientId.secretName (or (not .Values.global.cloud.resourceId.secretName) (not .Values.telemetryCollector.cloud.clientSecret.secretName))) }} +{{fail "When telemetryCollector.cloud.clientId.secretName is set, global.cloud.resourceId.secretName, telemetryCollector.cloud.clientSecret.secretName must also be set."}} +{{- end }} +{{- if (and .Values.telemetryCollector.cloud.clientSecret.secretName (or (not .Values.global.cloud.resourceId.secretName) (not .Values.telemetryCollector.cloud.clientSecret.secretName))) }} +{{fail "When telemetryCollector.cloud.clientSecret.secretName is set, global.cloud.resourceId.secretName,telemetryCollector.cloud.clientId.secretName must also be set."}} +{{- end }} +{{- end }} + +{{/**/}} + +{{- define "consul.validateTelemetryCollectorCloudSecretKeys" -}} +{{- if or (and .Values.telemetryCollector.cloud.clientId.secretName (not .Values.telemetryCollector.cloud.clientId.secretKey)) (and .Values.telemetryCollector.cloud.clientId.secretKey (not .Values.telemetryCollector.cloud.clientId.secretName)) }} +{{fail "When either telemetryCollector.cloud.clientId.secretName or telemetryCollector.cloud.clientId.secretKey is defined, both must be set."}} +{{- end }} +{{- if or (and .Values.telemetryCollector.cloud.clientSecret.secretName (not .Values.telemetryCollector.cloud.clientSecret.secretKey)) (and .Values.telemetryCollector.cloud.clientSecret.secretKey (not .Values.telemetryCollector.cloud.clientSecret.secretName)) }} +{{fail "When either telemetryCollector.cloud.clientSecret.secretName or telemetryCollector.cloud.clientSecret.secretKey is defined, both must be set."}} +{{- end }} +{{- if or (and .Values.telemetryCollector.cloud.clientSecret.secretName .Values.telemetryCollector.cloud.clientSecret.secretKey .Values.telemetryCollector.cloud.clientId.secretName .Values.telemetryCollector.cloud.clientId.secretKey (not .Values.global.cloud.resourceId.secretName)) }} +{{fail "When telemetryCollector has clientId and clientSecret global.cloud.resourceId.secretName must be set"}} +{{- end }} +{{- if or (and .Values.telemetryCollector.cloud.clientSecret.secretName .Values.telemetryCollector.cloud.clientSecret.secretKey .Values.telemetryCollector.cloud.clientId.secretName .Values.telemetryCollector.cloud.clientId.secretKey (not .Values.global.cloud.resourceId.secretKey)) }} +{{fail "When telemetryCollector has clientId and clientSecret .global.cloud.resourceId.secretKey must be set"}} +{{- end }} +{{- end -}} diff --git a/charts/consul/templates/api-gateway-controller-deployment.yaml b/charts/consul/templates/api-gateway-controller-deployment.yaml index a9f1806cc8..8c5c2fa73e 100644 --- a/charts/consul/templates/api-gateway-controller-deployment.yaml +++ b/charts/consul/templates/api-gateway-controller-deployment.yaml @@ -65,7 +65,14 @@ spec: {{- if or (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) .Values.client.enabled }} {{- if .Values.global.tls.enabled }} - name: CONSUL_CACERT + {{- /* When Vault is being used as a secrets backend, auto-encrypt must be enabled. Since clients use a separate + root CA from servers when auto-encrypt is enabled, and our controller communicates with the agent when clients are + enabled, we only use the Vault server CA if clients are disabled and our controller will be communicating w/ the server. */}} + {{- if and (not .Values.client.enabled) .Values.global.secretsBackend.vault.enabled }} + value: /vault/secrets/serverca.crt + {{- else }} value: /consul/tls/ca/tls.crt + {{- end }} {{- end }} {{- end }} - name: HOST_IP @@ -112,7 +119,7 @@ spec: {{- end }} - name: CONSUL_HTTP_SSL value: "{{ .Values.global.tls.enabled }}" - {{- if and .Values.externalServers.enabled .Values.externalServers.tlsServerName }} + {{- if and (not .Values.client.enabled) .Values.externalServers.enabled .Values.externalServers.tlsServerName }} - name: CONSUL_TLS_SERVER_NAME value: {{ .Values.externalServers.tlsServerName }} {{- end }} @@ -156,7 +163,7 @@ spec: - name: consul-bin mountPath: /consul-bin {{- end }} - {{- if or (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) .Values.client.enabled }} + {{- if or (not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled)) .Values.client.enabled }} {{- if .Values.global.tls.enabled }} {{- if and .Values.client.enabled .Values.global.tls.enableAutoEncrypt }} - name: consul-auto-encrypt-ca-cert @@ -186,7 +193,7 @@ spec: emptyDir: { } {{- end }} {{- if .Values.global.tls.enabled }} - {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} - name: consul-ca-cert secret: {{- if .Values.global.tls.caCert.secretName }} @@ -253,7 +260,7 @@ spec: - mountPath: /consul/login name: consul-data readOnly: false - {{- if not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} {{- if .Values.global.tls.enabled }} - name: consul-ca-cert mountPath: /consul/tls/ca diff --git a/charts/consul/templates/client-config-configmap.yaml b/charts/consul/templates/client-config-configmap.yaml index f9650a100b..d91a4d21bf 100644 --- a/charts/consul/templates/client-config-configmap.yaml +++ b/charts/consul/templates/client-config-configmap.yaml @@ -19,6 +19,12 @@ data: "auto_reload_config": true {{- end }} } + log-level.json: |- + { + {{- if .Values.client.logLevel }} + "log_level": "{{ .Values.client.logLevel | upper }}" + {{- end }} + } extra-from-values.json: |- {{ tpl .Values.client.extraConfig . | trimAll "\"" | indent 4 }} central-config.json: |- diff --git a/charts/consul/templates/client-daemonset.yaml b/charts/consul/templates/client-daemonset.yaml index 09a70b394e..345c5c731e 100644 --- a/charts/consul/templates/client-daemonset.yaml +++ b/charts/consul/templates/client-daemonset.yaml @@ -510,11 +510,7 @@ spec: value: "component=client,pod=$(NAMESPACE)/$(POD_NAME)" {{- end }} - name: CONSUL_LOGIN_DATACENTER - {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter }} - value: {{ .Values.global.federation.primaryDatacenter }} - {{- else }} value: {{ .Values.global.datacenter }} - {{- end}} command: - "/bin/sh" - "-ec" diff --git a/charts/consul/templates/connect-inject-deployment.yaml b/charts/consul/templates/connect-inject-deployment.yaml index 2b52c1b81c..e726c9ecc9 100644 --- a/charts/consul/templates/connect-inject-deployment.yaml +++ b/charts/consul/templates/connect-inject-deployment.yaml @@ -94,6 +94,7 @@ spec: - containerPort: 8080 name: webhook-server protocol: TCP + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} env: - name: NAMESPACE valueFrom: @@ -234,6 +235,19 @@ spec: -default-sidecar-proxy-cpu-request={{ $resources.requests.cpu }} \ {{- end }} -default-envoy-proxy-concurrency={{ .Values.connectInject.sidecarProxy.concurrency }} \ + {{- if .Values.connectInject.sidecarProxy.lifecycle.defaultEnabled }} + -default-enable-sidecar-proxy-lifecycle=true \ + {{- else }} + -default-enable-sidecar-proxy-lifecycle=false \ + {{- end }} + {{- if .Values.connectInject.sidecarProxy.lifecycle.defaultEnableShutdownDrainListeners }} + -default-enable-sidecar-proxy-lifecycle-shutdown-drain-listeners=true \ + {{- else }} + -default-enable-sidecar-proxy-lifecycle-shutdown-drain-listeners=false \ + {{- end }} + -default-sidecar-proxy-lifecycle-shutdown-grace-period-seconds={{ .Values.connectInject.sidecarProxy.lifecycle.defaultShutdownGracePeriodSeconds }} \ + -default-sidecar-proxy-lifecycle-graceful-port={{ .Values.connectInject.sidecarProxy.lifecycle.defaultGracefulPort }} \ + -default-sidecar-proxy-lifecycle-graceful-shutdown-path="{{ .Values.connectInject.sidecarProxy.lifecycle.defaultGracefulShutdownPath }}" \ {{- if .Values.connectInject.initContainer }} {{- $initResources := .Values.connectInject.initContainer.resources }} @@ -257,6 +271,7 @@ spec: {{- if and .Values.global.tls.enabled .Values.global.tls.enableAutoEncrypt }} -enable-auto-encrypt \ {{- end }} + -enable-telemetry-collector={{ .Values.global.metrics.enableTelemetryCollector}} \ startupProbe: httpGet: path: /readyz/ready diff --git a/charts/consul/templates/crd-servicedefaults.yaml b/charts/consul/templates/crd-servicedefaults.yaml index 5c6ecc7476..9ed10be65b 100644 --- a/charts/consul/templates/crd-servicedefaults.yaml +++ b/charts/consul/templates/crd-servicedefaults.yaml @@ -274,7 +274,14 @@ spec: upstream proxy instances will be monitored for removal from the load balancing pool. properties: - enforcing_consecutive_5xx: + baseEjectionTime: + description: The base time that a host is ejected for. + The real time is equal to the base time multiplied by + the number of times the host has been ejected and is + capped by max_ejection_time (Default 300s). Defaults + to 30000ms or 30s. + type: string + enforcingConsecutive5xx: description: EnforcingConsecutive5xx is the % chance that a host will be actually ejected when an outlier status is detected through consecutive 5xx. This setting can @@ -285,6 +292,13 @@ spec: description: Interval between health check analysis sweeps. Each sweep may remove hosts or return hosts to the pool. type: string + maxEjectionPercent: + description: The maximum % of an upstream cluster that + can be ejected due to outlier detection. Defaults to + 10% but will eject at least one host regardless of the + value. + format: int32 + type: integer maxFailures: description: MaxFailures is the count of consecutive failures that results in a host being removed from the pool. @@ -377,7 +391,14 @@ spec: how upstream proxy instances will be monitored for removal from the load balancing pool. properties: - enforcing_consecutive_5xx: + baseEjectionTime: + description: The base time that a host is ejected for. + The real time is equal to the base time multiplied + by the number of times the host has been ejected and + is capped by max_ejection_time (Default 300s). Defaults + to 30000ms or 30s. + type: string + enforcingConsecutive5xx: description: EnforcingConsecutive5xx is the % chance that a host will be actually ejected when an outlier status is detected through consecutive 5xx. This setting @@ -389,6 +410,13 @@ spec: sweeps. Each sweep may remove hosts or return hosts to the pool. type: string + maxEjectionPercent: + description: The maximum % of an upstream cluster that + can be ejected due to outlier detection. Defaults + to 10% but will eject at least one host regardless + of the value. + format: int32 + type: integer maxFailures: description: MaxFailures is the count of consecutive failures that results in a host being removed from diff --git a/charts/consul/templates/crd-serviceresolvers.yaml b/charts/consul/templates/crd-serviceresolvers.yaml index e058052e97..103814e73f 100644 --- a/charts/consul/templates/crd-serviceresolvers.yaml +++ b/charts/consul/templates/crd-serviceresolvers.yaml @@ -242,6 +242,10 @@ spec: If empty the default subset is used. type: string type: object + requestTimeout: + description: RequestTimeout is the timeout for receiving an HTTP response + from this service before the connection is terminated. + type: string subsets: additionalProperties: properties: diff --git a/charts/consul/templates/crd-servicerouters.yaml b/charts/consul/templates/crd-servicerouters.yaml index 5052facc06..f28da9e7c1 100644 --- a/charts/consul/templates/crd-servicerouters.yaml +++ b/charts/consul/templates/crd-servicerouters.yaml @@ -68,6 +68,10 @@ spec: description: Destination controls how to proxy the matching request(s) to a service. properties: + idleTimeout: + description: IdleTimeout is total amount of time permitted + for the request stream to be idle. + type: string namespace: description: Namespace is the Consul namespace to resolve the service from instead of the current namespace. If diff --git a/charts/consul/templates/create-federation-secret-job.yaml b/charts/consul/templates/create-federation-secret-job.yaml index 4f83a1f82a..678a2af3ba 100644 --- a/charts/consul/templates/create-federation-secret-job.yaml +++ b/charts/consul/templates/create-federation-secret-job.yaml @@ -93,6 +93,7 @@ spec: containers: - name: create-federation-secret image: "{{ .Values.global.imageK8S }}" + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} env: - name: NAMESPACE valueFrom: @@ -119,7 +120,7 @@ spec: - "-ec" - | consul-k8s-control-plane create-federation-secret \ - -log-level={{ .Values.global.logLevel }} \ + -log-level={{ default .Values.global.logLevel .Values.global.federation.logLevel }} \ -log-json={{ .Values.global.logJSON }} \ {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} -gossip-key-file=/consul/gossip/gossip.key \ diff --git a/charts/consul/templates/gossip-encryption-autogenerate-job.yaml b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml index 9d296478a1..02fb3ea168 100644 --- a/charts/consul/templates/gossip-encryption-autogenerate-job.yaml +++ b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml @@ -48,6 +48,7 @@ spec: containers: - name: gossip-encryption-autogen image: "{{ .Values.global.imageK8S }}" + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} command: - "/bin/sh" - "-ec" @@ -56,7 +57,7 @@ spec: -namespace={{ .Release.Namespace }} \ -secret-name={{ template "consul.fullname" . }}-gossip-encryption-key \ -secret-key="key" \ - -log-level={{ .Values.global.logLevel }} \ + -log-level={{ default .Values.global.logLevel .Values.global.gossipEncryption.logLevel }} \ -log-json={{ .Values.global.logJSON }} resources: requests: diff --git a/charts/consul/templates/ingress-gateways-deployment.yaml b/charts/consul/templates/ingress-gateways-deployment.yaml index 4f72031855..c10f1549f6 100644 --- a/charts/consul/templates/ingress-gateways-deployment.yaml +++ b/charts/consul/templates/ingress-gateways-deployment.yaml @@ -175,6 +175,7 @@ spec: # ingress-gateway-init registers the ingress gateway service with Consul. - name: ingress-gateway-init image: {{ $root.Values.global.imageK8S }} + {{- include "consul.restrictedSecurityContext" $ | nindent 8 }} env: - name: NAMESPACE valueFrom: @@ -211,7 +212,7 @@ spec: -gateway-kind="ingress-gateway" \ -proxy-id-file=/consul/service/proxy-id \ -service-name={{ template "consul.fullname" $root }}-{{ .name }} \ - -log-level={{ default $root.Values.global.logLevel }} \ + -log-level={{ default $root.Values.global.logLevel $root.Values.ingressGateways.logLevel }} \ -log-json={{ $root.Values.global.logJSON }} volumeMounts: - name: consul-service @@ -233,6 +234,7 @@ spec: containers: - name: ingress-gateway image: {{ $root.Values.global.imageConsulDataplane | quote }} + {{- include "consul.restrictedSecurityContext" $ | nindent 8 }} {{- if (default $defaults.resources .resources) }} resources: {{ toYaml (default $defaults.resources .resources) | nindent 10 }} {{- end }} @@ -319,7 +321,7 @@ spec: {{- if $root.Values.global.adminPartitions.enabled }} - -service-partition={{ $root.Values.global.adminPartitions.name }} {{- end }} - - -log-level={{ default $root.Values.global.logLevel }} + - -log-level={{ default $root.Values.global.logLevel $root.Values.ingressGateways.logLevel }} - -log-json={{ $root.Values.global.logJSON }} {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} - -telemetry-prom-scrape-path=/metrics diff --git a/charts/consul/templates/mesh-gateway-deployment.yaml b/charts/consul/templates/mesh-gateway-deployment.yaml index 2b2bdc8c2a..1936138db3 100644 --- a/charts/consul/templates/mesh-gateway-deployment.yaml +++ b/charts/consul/templates/mesh-gateway-deployment.yaml @@ -161,7 +161,7 @@ spec: -gateway-kind="mesh-gateway" \ -proxy-id-file=/consul/service/proxy-id \ -service-name={{ .Values.meshGateway.consulServiceName }} \ - -log-level={{ default .Values.global.logLevel }} \ + -log-level={{ default .Values.global.logLevel .Values.meshGateway.logLevel }} \ -log-json={{ .Values.global.logJSON }} volumeMounts: - name: consul-service @@ -203,6 +203,10 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP - name: POD_NAME valueFrom: fieldRef: @@ -263,7 +267,7 @@ spec: {{- if .Values.global.adminPartitions.enabled }} - -service-partition={{ .Values.global.adminPartitions.name }} {{- end }} - - -log-level={{ default .Values.global.logLevel }} + - -log-level={{ default .Values.global.logLevel .Values.meshGateway.logLevel }} - -log-json={{ .Values.global.logJSON }} {{- if (and .Values.global.metrics.enabled .Values.global.metrics.enableGatewayMetrics) }} - -telemetry-prom-scrape-path=/metrics diff --git a/charts/consul/templates/partition-init-job.yaml b/charts/consul/templates/partition-init-job.yaml index db73ef783b..9209f850c8 100644 --- a/charts/consul/templates/partition-init-job.yaml +++ b/charts/consul/templates/partition-init-job.yaml @@ -81,6 +81,7 @@ spec: containers: - name: partition-init-job image: {{ .Values.global.imageK8S }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} env: {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 10 }} {{- if (and .Values.global.acls.bootstrapToken.secretName .Values.global.acls.bootstrapToken.secretKey) }} diff --git a/charts/consul/templates/server-acl-init-cleanup-job.yaml b/charts/consul/templates/server-acl-init-cleanup-job.yaml index 35b0877ab4..39754d6c6f 100644 --- a/charts/consul/templates/server-acl-init-cleanup-job.yaml +++ b/charts/consul/templates/server-acl-init-cleanup-job.yaml @@ -47,27 +47,34 @@ spec: {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.global.acls.annotations }} + {{- tpl .Values.global.acls.annotations . | nindent 8 }} + {{- end }} spec: restartPolicy: Never serviceAccountName: {{ template "consul.fullname" . }}-server-acl-init-cleanup + {{- if .Values.server.containerSecurityContext.aclInit }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.aclInit | nindent 8 }} + {{- end }} containers: - name: server-acl-init-cleanup image: {{ .Values.global.imageK8S }} + {{- if not .Values.server.containerSecurityContext.aclInit }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + {{- end }} command: - consul-k8s-control-plane args: - delete-completed-job - - -log-level={{ .Values.global.logLevel }} + - -log-level={{ default .Values.global.logLevel .Values.global.acls.logLevel }} - -log-json={{ .Values.global.logJSON }} - -k8s-namespace={{ .Release.Namespace }} - {{ template "consul.fullname" . }}-server-acl-init + {{- if .Values.global.acls.resources }} resources: - requests: - memory: "50Mi" - cpu: "50m" - limits: - memory: "50Mi" - cpu: "50m" + {{- toYaml .Values.global.acls.resources | nindent 12 }} + {{- end }} {{- if .Values.global.acls.tolerations }} tolerations: {{ tpl .Values.global.acls.tolerations . | indent 8 | trim }} diff --git a/charts/consul/templates/server-acl-init-job.yaml b/charts/consul/templates/server-acl-init-job.yaml index 440ab8bee0..e8a06cf7aa 100644 --- a/charts/consul/templates/server-acl-init-job.yaml +++ b/charts/consul/templates/server-acl-init-job.yaml @@ -46,15 +46,27 @@ spec: {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.global.acls.annotations }} + {{- tpl .Values.global.acls.annotations . | nindent 8 }} + {{- end }} {{- if .Values.global.secretsBackend.vault.enabled }} - "vault.hashicorp.com/agent-pre-populate-only": "true" + + {{- /* Run the Vault agent as both an init container and sidecar. + The Vault agent sidecar is needed when server-acl-init bootstraps ACLs + and writes the bootstrap token back to Vault. + * agent-pre-populate: true - Run the Vault agent init container. + * agent-pre-populate-only: false - Also, run the Vault agent sidecar. + * agent-cache-enable: true - Enable the Agent cache listener. + * agent-cache-listener-port: 8200 - (optional) Listen on 127.0.0.1:8200. + * agent-enable-quit: true - Enable a "quit" endpoint. server-acl-init + tells the Vault agent to stop (without this the Job will not complete). + */}} + "vault.hashicorp.com/agent-pre-populate": "true" + "vault.hashicorp.com/agent-pre-populate-only": "false" + "vault.hashicorp.com/agent-cache-enable": "true" + "vault.hashicorp.com/agent-cache-listener-port": "8200" + "vault.hashicorp.com/agent-enable-quit": "true" "vault.hashicorp.com/agent-inject": "true" - {{- if .Values.global.acls.bootstrapToken.secretName }} - {{- with .Values.global.acls.bootstrapToken }} - "vault.hashicorp.com/agent-inject-secret-bootstrap-token": "{{ .secretName }}" - "vault.hashicorp.com/agent-inject-template-bootstrap-token": {{ template "consul.vaultSecretTemplate" . }} - {{- end }} - {{- end }} {{- if .Values.global.acls.partitionToken.secretName }} {{- with .Values.global.acls.partitionToken }} "vault.hashicorp.com/agent-inject-secret-partition-token": "{{ .secretName }}" @@ -85,6 +97,10 @@ spec: spec: restartPolicy: Never serviceAccountName: {{ template "consul.fullname" . }}-server-acl-init + {{- if .Values.server.containerSecurityContext.aclInit }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.aclInit | nindent 8 }} + {{- end }} {{- if (or .Values.global.tls.enabled .Values.global.acls.replicationToken.secretName .Values.global.acls.bootstrapToken.secretName) }} volumes: {{- if and .Values.global.tls.enabled (not .Values.global.secretsBackend.vault.enabled) }} @@ -101,14 +117,7 @@ spec: path: tls.crt {{- end }} {{- end }} - {{- if (and .Values.global.acls.bootstrapToken.secretName (not .Values.global.secretsBackend.vault.enabled)) }} - - name: bootstrap-token - secret: - secretName: {{ .Values.global.acls.bootstrapToken.secretName }} - items: - - key: {{ .Values.global.acls.bootstrapToken.secretKey }} - path: bootstrap-token - {{- else if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} + {{- if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} - name: acl-replication-token secret: secretName: {{ .Values.global.acls.replicationToken.secretName }} @@ -120,6 +129,9 @@ spec: containers: - name: server-acl-init-job image: {{ .Values.global.imageK8S }} + {{- if not .Values.server.containerSecurityContext.aclInit }} + {{- include "consul.restrictedSecurityContext" . | nindent 8 }} + {{- end }} env: - name: NAMESPACE valueFrom: @@ -129,6 +141,13 @@ spec: valueFrom: fieldRef: fieldPath: metadata.name + # Extract the Vault namespace from the Vault agent annotations. + {{- if .Values.global.secretsBackend.vault.enabled }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + - name: VAULT_NAMESPACE + value: {{ get (tpl .Values.global.secretsBackend.vault.agentAnnotations . | fromYaml) "vault.hashicorp.com/namespace" }} + {{- end }} + {{- end }} {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 8 }} {{- if (or .Values.global.tls.enabled .Values.global.acls.replicationToken.secretName .Values.global.acls.bootstrapToken.secretName) }} volumeMounts: @@ -139,11 +158,7 @@ spec: readOnly: true {{- end }} {{- end }} - {{- if (and .Values.global.acls.bootstrapToken.secretName (not .Values.global.secretsBackend.vault.enabled)) }} - - name: bootstrap-token - mountPath: /consul/acl/tokens - readOnly: true - {{- else if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} + {{- if and .Values.global.acls.replicationToken.secretName (not .Values.global.secretsBackend.vault.enabled) }} - name: acl-replication-token mountPath: /consul/acl/tokens readOnly: true @@ -156,18 +171,20 @@ spec: CONSUL_FULLNAME="{{template "consul.fullname" . }}" consul-k8s-control-plane server-acl-init \ - -log-level={{ .Values.global.logLevel }} \ + -log-level={{ default .Values.global.logLevel .Values.global.acls.logLevel}} \ -log-json={{ .Values.global.logJSON }} \ -resource-prefix=${CONSUL_FULLNAME} \ -k8s-namespace={{ .Release.Namespace }} \ -set-server-tokens={{ $serverEnabled }} \ - - {{- if .Values.global.acls.bootstrapToken.secretName }} {{- if .Values.global.secretsBackend.vault.enabled }} - -bootstrap-token-file=/vault/secrets/bootstrap-token \ + -secrets-backend=vault \ {{- else }} - -bootstrap-token-file=/consul/acl/tokens/bootstrap-token \ + -secrets-backend=kubernetes \ {{- end }} + + {{- if .Values.global.acls.bootstrapToken.secretName }} + -bootstrap-token-secret-name={{ .Values.global.acls.bootstrapToken.secretName }} \ + -bootstrap-token-secret-key={{ .Values.global.acls.bootstrapToken.secretKey }} \ {{- end }} {{- if .Values.syncCatalog.enabled }} @@ -300,13 +317,10 @@ spec: {{- end }} {{- end }} {{- end }} + {{- if .Values.global.acls.resources }} resources: - requests: - memory: "50Mi" - cpu: "50m" - limits: - memory: "50Mi" - cpu: "50m" + {{- toYaml .Values.global.acls.resources | nindent 10 }} + {{- end }} {{- if .Values.global.acls.tolerations }} tolerations: {{ tpl .Values.global.acls.tolerations . | indent 8 | trim }} diff --git a/charts/consul/templates/server-config-configmap.yaml b/charts/consul/templates/server-config-configmap.yaml index f7dd85f166..f4f787cb24 100644 --- a/charts/consul/templates/server-config-configmap.yaml +++ b/charts/consul/templates/server-config-configmap.yaml @@ -1,4 +1,5 @@ {{- if (or (and (ne (.Values.server.enabled | toString) "-") .Values.server.enabled) (and (eq (.Values.server.enabled | toString) "-") .Values.global.enabled)) }} +{{- if and .Values.server.auditLogs.enabled (not .Values.global.acls.manageSystemACLs) }}{{fail "ACLs must be enabled inorder to configure audit logs"}}{{ end -}} # StatefulSet to run the actual Consul server cluster. apiVersion: v1 kind: ConfigMap @@ -25,6 +26,9 @@ data: }, "datacenter": "{{ .Values.global.datacenter }}", "data_dir": "/consul/data", + {{- if .Values.server.logLevel }} + "log_level": "{{ .Values.server.logLevel | upper }}", + {{- end }} "domain": "{{ .Values.global.domain }}", "ports": { {{- if not .Values.global.tls.enabled }} @@ -178,4 +182,27 @@ data: } } {{- end }} + {{- if and .Values.server.auditLogs.enabled .Values.global.acls.manageSystemACLs }} + audit-logging.json: |- + { + "audit": { + "enabled": true, + "sink": { + {{- range $index, $element := .Values.server.auditLogs.sinks }} + {{- if ne $index 0 }},{{end}} + "{{ $element.name }}": { + {{- $firstKeyValuePair := false }} + {{- range $k, $v := $element }} + {{- if ne $k "name" }} + {{- if ne $firstKeyValuePair false }},{{end}} + {{- $firstKeyValuePair = true }} + "{{ $k }}": "{{ $v }}" + {{- end }} + {{- end }} + } + {{- end }} + } + } + } + {{- end }} {{- end }} diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index 8b73306fd7..5713310866 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -119,7 +119,13 @@ spec: {{- if (and .Values.global.metrics.enabled .Values.global.metrics.enableAgentMetrics) }} "prometheus.io/scrape": "true" "prometheus.io/path": "/v1/agent/metrics" + {{- if .Values.global.tls.enabled }} + "prometheus.io/port": "8501" + "prometheus.io/scheme": "https" + {{- else }} "prometheus.io/port": "8500" + "prometheus.io/scheme": "http" + {{- end }} {{- end }} spec: {{- if .Values.server.affinity }} @@ -198,6 +204,11 @@ spec: medium: "Memory" {{- end }} {{- end }} + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + emptyDir: + medium: "Memory" + {{- end }} {{- range .Values.server.extraVolumes }} - name: userconfig-{{ .name }} {{ .type }}: @@ -291,9 +302,9 @@ spec: {{- end }} {{- if .Values.global.cloud.enabled}} # These are mounted as secrets so that the consul server agent can use them. - # - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL, + # - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL, # HCP_SCADA_ADDRESS, and HCP_API_HOST. so nothing more needs to be done. - # - HCP_RESOURCE_ID is created for use in the + # - HCP_RESOURCE_ID is created for use in the # `-hcl="cloud { resource_id = \"${HCP_RESOURCE_ID}\" }"` logic in the command below. {{- if .Values.global.cloud.clientId.secretName }} - name: HCP_CLIENT_ID @@ -328,7 +339,7 @@ spec: valueFrom: secretKeyRef: name: {{ .Values.global.cloud.apiHost.secretName }} - key: {{ .Values.global.cloud.apiHost.secretKey }} + key: {{ .Values.global.cloud.apiHost.secretKey }} {{- end}} {{- if .Values.global.cloud.scadaAddress.secretName }} - name: HCP_SCADA_ADDRESS @@ -336,13 +347,25 @@ spec: secretKeyRef: name: {{ .Values.global.cloud.scadaAddress.secretName }} key: {{ .Values.global.cloud.scadaAddress.secretKey }} - {{- end}} + {{- end}} + {{- end }} + {{- if .Values.global.trustedCAs }} + - name: SSL_CERT_DIR + value: "/etc/ssl/certs:/trusted-cas" {{- end }} {{- include "consul.extraEnvironmentVars" .Values.server | nindent 12 }} command: - "/bin/sh" - "-ec" - | + {{- if .Values.global.trustedCAs }} + {{- range $i, $cert := .Values.global.trustedCAs }} + cat < /trusted-cas/custom-ca-{{$i}}.pem + {{- $cert | nindent 14 }} + EOF + {{- end }} + {{- end }} + {{- if and .Values.global.secretsBackend.vault.enabled .Values.global.gossipEncryption.secretName }} GOSSIP_KEY=`cat /vault/secrets/gossip.txt` {{- end }} @@ -409,6 +432,11 @@ spec: mountPath: /consul/vault-ca/ readOnly: true {{- end }} + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + mountPath: /trusted-cas + readOnly: false + {{- end }} ports: {{- if (or (not .Values.global.tls.enabled) (not .Values.global.tls.httpsOnly)) }} - name: http @@ -487,9 +515,11 @@ spec: {{- toYaml .Values.server.resources | nindent 12 }} {{- end }} {{- end }} - {{- if not .Values.global.openshift.enabled }} + {{- if .Values.server.containerSecurityContext.server }} securityContext: {{- toYaml .Values.server.containerSecurityContext.server | nindent 12 }} + {{- else }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} {{- end }} {{- if .Values.server.extraContainers }} {{ toYaml .Values.server.extraContainers | nindent 8 }} diff --git a/charts/consul/templates/sync-catalog-clusterrole.yaml b/charts/consul/templates/sync-catalog-clusterrole.yaml index 0b0837c0df..585b5ad171 100644 --- a/charts/consul/templates/sync-catalog-clusterrole.yaml +++ b/charts/consul/templates/sync-catalog-clusterrole.yaml @@ -11,31 +11,38 @@ metadata: release: {{ .Release.Name }} component: sync-catalog rules: - - apiGroups: [""] - resources: - - services - - endpoints - verbs: - - get - - list - - watch +- apiGroups: [ "" ] + resources: + - services + - endpoints + verbs: + - get + - list + - watch {{- if .Values.syncCatalog.toK8S }} - - update - - patch - - delete - - create + - update + - patch + - delete + - create {{- end }} - - apiGroups: [""] - resources: - - nodes - verbs: - - get +- apiGroups: [ "" ] + resources: + - nodes + verbs: + - get {{- if .Values.global.enablePodSecurityPolicies }} - - apiGroups: ["policy"] - resources: ["podsecuritypolicies"] - verbs: - - use - resourceNames: - - {{ template "consul.fullname" . }}-sync-catalog -{{- end }} +- apiGroups: [ "policy" ] + resources: [ "podsecuritypolicies" ] + verbs: + - use + resourceNames: + - {{ template "consul.fullname" . }}-sync-catalog {{- end }} +- apiGroups: [ "networking.k8s.io" ] + resources: + - ingresses + verbs: + - get + - list + - watch +{{- end }} \ No newline at end of file diff --git a/charts/consul/templates/sync-catalog-deployment.yaml b/charts/consul/templates/sync-catalog-deployment.yaml index f2815d9627..a8793ef6f6 100644 --- a/charts/consul/templates/sync-catalog-deployment.yaml +++ b/charts/consul/templates/sync-catalog-deployment.yaml @@ -75,136 +75,143 @@ spec: {{- end }} {{- end }} containers: - - name: sync-catalog - image: "{{ default .Values.global.imageK8S .Values.syncCatalog.image }}" - env: - {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 12 }} - {{- if .Values.global.acls.manageSystemACLs }} - - name: CONSUL_LOGIN_AUTH_METHOD - {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter .Values.global.enableConsulNamespaces }} - value: {{ template "consul.fullname" . }}-k8s-component-auth-method-{{ .Values.global.datacenter }} - {{- else }} - value: {{ template "consul.fullname" . }}-k8s-component-auth-method - {{- end }} - - name: CONSUL_LOGIN_DATACENTER - {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter .Values.global.enableConsulNamespaces }} - value: {{ .Values.global.federation.primaryDatacenter }} - {{- else }} - value: {{ .Values.global.datacenter }} - {{- end }} - - name: CONSUL_LOGIN_META - value: "component=sync-catalog,pod=$(NAMESPACE)/$(POD_NAME)" - {{- end }} - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - {{- if (and .Values.syncCatalog.aclSyncToken.secretName .Values.syncCatalog.aclSyncToken.secretKey) }} - - name: CONSUL_HTTP_TOKEN - valueFrom: - secretKeyRef: - name: {{ .Values.syncCatalog.aclSyncToken.secretName }} - key: {{ .Values.syncCatalog.aclSyncToken.secretKey }} - {{- end }} - volumeMounts: - {{- if .Values.global.tls.enabled }} - {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} - - name: consul-ca-cert - mountPath: /consul/tls/ca - readOnly: true - {{- end }} - {{- end }} - command: - - "/bin/sh" - - "-ec" - - | - consul-k8s-control-plane sync-catalog \ - -log-level={{ default .Values.global.logLevel .Values.syncCatalog.logLevel }} \ - -log-json={{ .Values.global.logJSON }} \ - -k8s-default-sync={{ .Values.syncCatalog.default }} \ - {{- if (not .Values.syncCatalog.toConsul) }} - -to-consul=false \ - {{- end }} - {{- if (not .Values.syncCatalog.toK8S) }} - -to-k8s=false \ - {{- end }} - -consul-domain={{ .Values.global.domain }} \ - {{- if .Values.syncCatalog.k8sPrefix }} - -k8s-service-prefix="{{ .Values.syncCatalog.k8sPrefix}}" \ - {{- end }} - {{- if .Values.syncCatalog.k8sSourceNamespace }} - -k8s-source-namespace="{{ .Values.syncCatalog.k8sSourceNamespace}}" \ - {{- end }} - {{- range $value := .Values.syncCatalog.k8sAllowNamespaces }} - -allow-k8s-namespace="{{ $value }}" \ - {{- end }} - {{- range $value := .Values.syncCatalog.k8sDenyNamespaces }} - -deny-k8s-namespace="{{ $value }}" \ - {{- end }} - -k8s-write-namespace=${NAMESPACE} \ - {{- if (not .Values.syncCatalog.syncClusterIPServices) }} - -sync-clusterip-services=false \ - {{- end }} - {{- if .Values.syncCatalog.nodePortSyncType }} - -node-port-sync-type={{ .Values.syncCatalog.nodePortSyncType }} \ - {{- end }} - {{- if .Values.syncCatalog.consulWriteInterval }} - -consul-write-interval={{ .Values.syncCatalog.consulWriteInterval }} \ - {{- end }} - {{- if .Values.syncCatalog.k8sTag }} - -consul-k8s-tag={{ .Values.syncCatalog.k8sTag }} \ - {{- end }} - {{- if .Values.syncCatalog.consulNodeName }} - -consul-node-name={{ .Values.syncCatalog.consulNodeName }} \ - {{- end }} - {{- if .Values.global.adminPartitions.enabled }} - -partition={{ .Values.global.adminPartitions.name }} \ - {{- end }} - {{- if .Values.syncCatalog.consulPrefix}} - -consul-service-prefix="{{ .Values.syncCatalog.consulPrefix}}" \ - {{- end}} - {{- if .Values.syncCatalog.addK8SNamespaceSuffix}} - -add-k8s-namespace-suffix \ - {{- end}} - {{- if .Values.global.enableConsulNamespaces }} - -enable-namespaces=true \ - {{- if .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} - -consul-destination-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} \ - {{- end }} - {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} - -enable-k8s-namespace-mirroring=true \ - {{- if .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} - -k8s-namespace-mirroring-prefix={{ .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} \ - {{- end }} - {{- end }} - {{- if .Values.global.acls.manageSystemACLs }} - -consul-cross-namespace-acl-policy=cross-namespace-policy \ - {{- end }} - {{- end }} - livenessProbe: - httpGet: - path: /health/ready - port: 8080 - scheme: HTTP - failureThreshold: 3 - initialDelaySeconds: 30 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 5 - readinessProbe: - httpGet: - path: /health/ready - port: 8080 - scheme: HTTP - failureThreshold: 5 - initialDelaySeconds: 10 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 5 - {{- with .Values.syncCatalog.resources }} - resources: - {{- toYaml . | nindent 12 }} + - name: sync-catalog + image: "{{ default .Values.global.imageK8S .Values.syncCatalog.image }}" + {{- include "consul.restrictedSecurityContext" . | nindent 8 }} + env: + {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 8 }} + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_LOGIN_AUTH_METHOD + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter .Values.global.enableConsulNamespaces }} + value: {{ template "consul.fullname" . }}-k8s-component-auth-method-{{ .Values.global.datacenter }} + {{- else }} + value: {{ template "consul.fullname" . }}-k8s-component-auth-method + {{- end }} + - name: CONSUL_LOGIN_DATACENTER + {{- if and .Values.global.federation.enabled .Values.global.federation.primaryDatacenter .Values.global.enableConsulNamespaces }} + value: {{ .Values.global.federation.primaryDatacenter }} + {{- else }} + value: {{ .Values.global.datacenter }} {{- end }} + - name: CONSUL_LOGIN_META + value: "component=sync-catalog,pod=$(NAMESPACE)/$(POD_NAME)" + {{- end }} + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if (and .Values.syncCatalog.aclSyncToken.secretName .Values.syncCatalog.aclSyncToken.secretKey) }} + - name: CONSUL_ACL_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.syncCatalog.aclSyncToken.secretName }} + key: {{ .Values.syncCatalog.aclSyncToken.secretKey }} + {{- end }} + volumeMounts: + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + command: + - "/bin/sh" + - "-ec" + - | + consul-k8s-control-plane sync-catalog \ + -log-level={{ default .Values.global.logLevel .Values.syncCatalog.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -k8s-default-sync={{ .Values.syncCatalog.default }} \ + {{- if (not .Values.syncCatalog.toConsul) }} + -to-consul=false \ + {{- end }} + {{- if (not .Values.syncCatalog.toK8S) }} + -to-k8s=false \ + {{- end }} + -consul-domain={{ .Values.global.domain }} \ + {{- if .Values.syncCatalog.k8sPrefix }} + -k8s-service-prefix="{{ .Values.syncCatalog.k8sPrefix}}" \ + {{- end }} + {{- if .Values.syncCatalog.k8sSourceNamespace }} + -k8s-source-namespace="{{ .Values.syncCatalog.k8sSourceNamespace}}" \ + {{- end }} + {{- range $value := .Values.syncCatalog.k8sAllowNamespaces }} + -allow-k8s-namespace="{{ $value }}" \ + {{- end }} + {{- range $value := .Values.syncCatalog.k8sDenyNamespaces }} + -deny-k8s-namespace="{{ $value }}" \ + {{- end }} + -k8s-write-namespace=${NAMESPACE} \ + {{- if (not .Values.syncCatalog.syncClusterIPServices) }} + -sync-clusterip-services=false \ + {{- end }} + {{- if .Values.syncCatalog.nodePortSyncType }} + -node-port-sync-type={{ .Values.syncCatalog.nodePortSyncType }} \ + {{- end }} + {{- if .Values.syncCatalog.consulWriteInterval }} + -consul-write-interval={{ .Values.syncCatalog.consulWriteInterval }} \ + {{- end }} + {{- if .Values.syncCatalog.k8sTag }} + -consul-k8s-tag={{ .Values.syncCatalog.k8sTag }} \ + {{- end }} + {{- if .Values.syncCatalog.consulNodeName }} + -consul-node-name={{ .Values.syncCatalog.consulNodeName }} \ + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + -partition={{ .Values.global.adminPartitions.name }} \ + {{- end }} + {{- if .Values.syncCatalog.consulPrefix}} + -consul-service-prefix="{{ .Values.syncCatalog.consulPrefix}}" \ + {{- end}} + {{- if .Values.syncCatalog.addK8SNamespaceSuffix}} + -add-k8s-namespace-suffix \ + {{- end}} + {{- if .Values.global.enableConsulNamespaces }} + -enable-namespaces=true \ + {{- if .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + -consul-destination-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} \ + {{- end }} + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} + -enable-k8s-namespace-mirroring=true \ + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} + -k8s-namespace-mirroring-prefix={{ .Values.syncCatalog.consulNamespaces.mirroringK8SPrefix }} \ + {{- end }} + {{- end }} + {{- if .Values.global.acls.manageSystemACLs }} + -consul-cross-namespace-acl-policy=cross-namespace-policy \ + {{- end }} + {{- end }} + {{- if .Values.syncCatalog.ingress.enabled }} + -enable-ingress=true \ + {{- if .Values.syncCatalog.ingress.loadBalancerIPs }} + -loadBalancer-ips=true \ + {{- end }} + {{- end }} + livenessProbe: + httpGet: + path: /health/ready + port: 8080 + scheme: HTTP + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + httpGet: + path: /health/ready + port: 8080 + scheme: HTTP + failureThreshold: 5 + initialDelaySeconds: 10 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + {{- with .Values.syncCatalog.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} {{- if .Values.syncCatalog.priorityClassName }} priorityClassName: {{ .Values.syncCatalog.priorityClassName | quote }} {{- end }} diff --git a/charts/consul/templates/telemetry-collector-configmap.yaml b/charts/consul/templates/telemetry-collector-configmap.yaml new file mode 100644 index 0000000000..0bf5b8753c --- /dev/null +++ b/charts/consul/templates/telemetry-collector-configmap.yaml @@ -0,0 +1,18 @@ +{{- if (and .Values.telemetryCollector.enabled .Values.telemetryCollector.customExporterConfig) }} +# Immutable ConfigMap which saves the partition name. Attempting to update this configmap +# with a new Admin Partition name will cause the helm upgrade to fail +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: consul-telemetry-collector +data: + config.json: |- + {{ tpl .Values.telemetryCollector.customExporterConfig . | trimAll "\"" | indent 4 }} +{{- end }} diff --git a/charts/consul/templates/telemetry-collector-deployment.yaml b/charts/consul/templates/telemetry-collector-deployment.yaml new file mode 100644 index 0000000000..bf00fd9a03 --- /dev/null +++ b/charts/consul/templates/telemetry-collector-deployment.yaml @@ -0,0 +1,391 @@ +{{- if .Values.telemetryCollector.enabled }} +{{- if not .Values.telemetryCollector.image}}{{ fail "telemetryCollector.image must be set to enable consul-telemetry-collector" }}{{ end }} +{{- if not .Values.connectInject.enabled }}{{ fail "connectInject.enabled must be true" }}{{ end -}} +{{- if and .Values.global.adminPartitions.enabled (not .Values.global.enableConsulNamespaces) }}{{ fail "global.enableConsulNamespaces must be true if global.adminPartitions.enabled=true" }}{{ end }} +{{ template "consul.validateCloudSecretKeys" . }} +{{ template "consul.validateTelemetryCollectorCloud" . }} +{{ template "consul.validateTelemetryCollectorCloudSecretKeys" . }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.telemetryCollector.replicas }} + selector: + matchLabels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + template: + metadata: + annotations: + "consul.hashicorp.com/connect-inject": "false" + # This annotation tells the endpoints controller that this pod was injected even though it wasn't. The + # endpoints controller would then sync the endpoint into Consul + "consul.hashicorp.com/connect-inject-status": "injected" + # We aren't using tproxy and we don't have an original pod. This would be simpler if we made a path similar + # to gateways + "consul.hashicorp.com/connect-service-port": "metricsserver" + "consul.hashicorp.com/transparent-proxy": "false" + "consul.hashicorp.com/transparent-proxy-overwrite-probes": "false" + "consul.hashicorp.com/connect-k8s-version": {{ $.Chart.Version }} + {{- if .Values.telemetryCollector.customExporterConfig }} + # configmap checksum + "consul.hashicorp.com/config-checksum": {{ include (print $.Template.BasePath "/telemetry-collector-configmap.yaml") . | sha256sum }} + {{- end }} + # vault annotations + {{- if (and .Values.global.secretsBackend.vault.enabled .Values.global.tls.enabled) }} + "vault.hashicorp.com/agent-init-first": "true" + "vault.hashicorp.com/agent-inject": "true" + "vault.hashicorp.com/role": {{ .Values.global.secretsBackend.vault.consulCARole }} + "vault.hashicorp.com/agent-inject-secret-serverca.crt": {{ .Values.global.tls.caCert.secretName }} + "vault.hashicorp.com/agent-inject-template-serverca.crt": {{ template "consul.serverTLSCATemplate" . }} + {{- if and .Values.global.secretsBackend.vault.ca.secretName .Values.global.secretsBackend.vault.ca.secretKey }} + "vault.hashicorp.com/agent-extra-secret": "{{ .Values.global.secretsBackend.vault.ca.secretName }}" + "vault.hashicorp.com/ca-cert": "/vault/custom/{{ .Values.global.secretsBackend.vault.ca.secretKey }}" + {{- end }} + {{- if .Values.global.secretsBackend.vault.agentAnnotations }} + {{ tpl .Values.global.secretsBackend.vault.agentAnnotations . | nindent 8 | trim }} + {{- end }} + {{- end }} + + labels: + consul.hashicorp.com/connect-inject-managed-by: consul-k8s-endpoints-controller + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{- if .Values.global.extraLabels }} + {{- toYaml .Values.global.extraLabels | nindent 8 }} + {{- end }} + spec: + # This needs to explicitly be consul-telemetry-collector because we look this up from each service consul-dataplane + # to forward metrics to it. + serviceAccountName: consul-telemetry-collector + initContainers: + # We're manually managing this init container instead of using the connect injector so that we don't run into + # any race conditions on the connect-injector deployment or upgrade + - name: consul-connect-init + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + {{- if .Values.global.acls.manageSystemACLs }} + - name: CONSUL_LOGIN_AUTH_METHOD + value: {{ template "consul.fullname" . }}-k8s-auth-method + - name: CONSUL_LOGIN_META + value: "component=consul-telemetry-collector,pod=$(NAMESPACE)/$(POD_NAME)" + {{- end }} + - name: CONSUL_NODE_NAME + value: $(NODE_NAME)-virtual + {{- include "consul.consulK8sConsulServerEnvVars" . | nindent 10 }} + {{- if .Values.global.enableConsulNamespaces }} + - name: CONSUL_NAMESPACE + value: {{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} + - name: CONSUL_LOGIN_NAMESPACE + value: "default" + {{- else }} + - name: CONSUL_LOGIN_NAMESPACE + value: {{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- end }} + command: + - /bin/sh + - -ec + - |- + consul-k8s-control-plane connect-init -pod-name=${POD_NAME} -pod-namespace=${POD_NAMESPACE} \ + -log-level={{ default .Values.global.logLevel .Values.telemetryCollector.logLevel }} \ + -log-json={{ .Values.global.logJSON }} \ + -service-account-name="consul-telemetry-collector" \ + -service-name="" \ + -proxy-id-file="/consul/connect-inject/proxyid" + + image: {{ .Values.global.imageK8S }} + imagePullPolicy: IfNotPresent + {{- if .Values.telemetryCollector.initContainer.resources }} + resources: + {{- toYaml .Values.telemetryCollector.initContainer.resources | nindent 12 }} + {{- else }} + resources: + limits: + cpu: 50m + memory: 150Mi + requests: + cpu: 50m + memory: 25Mi + {{- end }} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /consul/connect-inject + name: consul-connect-inject-data + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + containers: + - name: consul-telemetry-collector + image: {{ .Values.telemetryCollector.image }} + imagePullPolicy: Always + ports: + - containerPort: 9090 + name: metrics + protocol: TCP + - containerPort: 9356 + name: metricsserver + protocol: TCP + env: + # These are mounted as secrets so that the telemetry-collector can use them when cloud is enabled. + # - the hcp-go-sdk in consul agent will already look for HCP_CLIENT_ID, HCP_CLIENT_SECRET, HCP_AUTH_URL, + # HCP_SCADA_ADDRESS, and HCP_API_HOST. so nothing more needs to be done. + # - HCP_RESOURCE_ID is created for use in the global cloud section but we will share it here + {{- if .Values.telemetryCollector.cloud.clientId.secretName }} + - name: HCP_CLIENT_ID + valueFrom: + secretKeyRef: + name: {{ .Values.telemetryCollector.cloud.clientId.secretName }} + key: {{ .Values.telemetryCollector.cloud.clientId.secretKey }} + {{- end }} + {{- if .Values.telemetryCollector.cloud.clientSecret.secretName }} + - name: HCP_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: {{ .Values.telemetryCollector.cloud.clientSecret.secretName }} + key: {{ .Values.telemetryCollector.cloud.clientSecret.secretKey }} + {{- end}} + {{- if .Values.global.cloud.resourceId.secretName }} + - name: HCP_RESOURCE_ID + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.resourceId.secretName }} + key: {{ .Values.global.cloud.resourceId.secretKey }} + {{- end }} + {{- if .Values.global.cloud.authUrl.secretName }} + - name: HCP_AUTH_URL + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.authUrl.secretName }} + key: {{ .Values.global.cloud.authUrl.secretKey }} + {{- end}} + {{- if .Values.global.cloud.apiHost.secretName }} + - name: HCP_API_HOST + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.apiHost.secretName }} + key: {{ .Values.global.cloud.apiHost.secretKey }} + {{- end}} + {{- if .Values.global.cloud.scadaAddress.secretName }} + - name: HCP_SCADA_ADDRESS + valueFrom: + secretKeyRef: + name: {{ .Values.global.cloud.scadaAddress.secretName }} + key: {{ .Values.global.cloud.scadaAddress.secretKey }} + {{- end}} + {{- if .Values.global.trustedCAs }} + - name: SSL_CERT_DIR + value: "/etc/ssl/certs:/trusted-cas" + {{- end }} + {{- include "consul.extraEnvironmentVars" .Values.telemetryCollector | nindent 12 }} + command: + - "/bin/sh" + - "-ec" + - | + {{- if .Values.global.trustedCAs }} + {{- range $i, $cert := .Values.global.trustedCAs }} + cat < /trusted-cas/custom-ca-{{$i}}.pem + {{- $cert | nindent 10 }} + EOF + {{- end }} + {{- end }} + + consul-telemetry-collector agent \ + {{- if .Values.telemetryCollector.customExporterConfig }} + -config-file-path /consul/config/config.json \ + {{ end }} + volumeMounts: + {{- if .Values.telemetryCollector.customExporterConfig }} + - name: config + mountPath: /consul/config + {{- end }} + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + mountPath: /trusted-cas + readOnly: false + {{- end }} + resources: + {{- if .Values.telemetryCollector.resources }} + {{- toYaml .Values.telemetryCollector.resources | nindent 12 }} + {{- end }} + # consul-dataplane container + - name: consul-dataplane + image: "{{ .Values.global.imageConsulDataplane }}" + imagePullPolicy: IfNotPresent + command: + - consul-dataplane + args: + # addresses + {{- if .Values.externalServers.enabled }} + - -addresses={{ .Values.externalServers.hosts | first }} + {{- else }} + - -addresses={{ template "consul.fullname" . }}-server.{{ .Release.Namespace }}.svc + {{- end }} + # grpc + {{- if .Values.externalServers.enabled }} + - -grpc-port={{ .Values.externalServers.grpcPort }} + {{- else }} + - -grpc-port=8502 + {{- end }} + - -proxy-service-id-path=/consul/connect-inject/proxyid + # tls + {{- if .Values.global.tls.enabled }} + {{- if (not (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots)) }} + {{- if .Values.global.secretsBackend.vault.enabled }} + - -ca-certs=/vault/secrets/serverca.crt + {{- else }} + - -ca-certs=/consul/tls/ca/tls.crt + {{- end }} + {{- end }} + {{- if and .Values.externalServers.enabled .Values.externalServers.tlsServerName }} + - -tls-server-name={{.Values.externalServers.tlsServerName }} + {{- else if .Values.global.cloud.enabled }} + - -tls-server-name=server.{{ .Values.global.datacenter}}.{{ .Values.global.domain}} + {{- end }} + {{- else }} + - -tls-disabled + {{- end }} + # credentials + {{- if .Values.global.acls.manageSystemACLs }} + - -credential-type=login + - -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token + - -login-auth-method={{ template "consul.fullname" . }}-k8s-auth-method + {{- if .Values.global.enableConsulNamespaces }} + {{- if .Values.syncCatalog.consulNamespaces.mirroringK8S }} + - -login-namespace="default" + {{- else }} + - -login-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + - foo + - -login-partition={{ .Values.global.adminPartitions.name }} + {{- end }} + {{- end }} + {{- if .Values.global.enableConsulNamespaces }} + - -service-namespace={{ .Values.syncCatalog.consulNamespaces.consulDestinationNamespace }} + {{- end }} + {{- if .Values.global.adminPartitions.enabled }} + - -service-partition={{ .Values.global.adminPartitions.name }} + {{- end }} + {{- if .Values.global.metrics.enabled }} + - -telemetry-prom-scrape-path=/metrics + {{- end }} + - -log-level={{ default .Values.global.logLevel .Values.telemetryCollector.logLevel }} + - -log-json={{ .Values.global.logJSON }} + - -envoy-concurrency=2 + {{- if and .Values.externalServers.enabled .Values.externalServers.skipServerWatch }} + - -server-watch-disabled=true + {{- end }} + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: DP_CREDENTIAL_LOGIN_META1 + value: pod=$(NAMESPACE)/$(POD_NAME) + - name: DP_CREDENTIAL_LOGIN_META2 + value: component=consul-telemetry-collector + - name: DP_SERVICE_NODE_NAME + value: $(NODE_NAME)-virtual + - name: TMPDIR + value: /consul/connect-inject + readinessProbe: + failureThreshold: 3 + initialDelaySeconds: 1 + periodSeconds: 10 + successThreshold: 1 + tcpSocket: + port: 20000 + timeoutSeconds: 1 + securityContext: + readOnlyRootFilesystem: true + runAsGroup: 5995 + runAsNonRoot: true + runAsUser: 5995 + # dataplane volume mounts + volumeMounts: + - mountPath: /consul/connect-inject + name: consul-connect-inject-data + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + mountPath: /consul/tls/ca + readOnly: true + {{- end }} + {{- end }} + + {{- if .Values.telemetryCollector.nodeSelector }} + nodeSelector: + {{ tpl .Values.telemetryCollector.nodeSelector . | indent 8 | trim }} + {{- end }} + {{- if .Values.telemetryCollector.priorityClassName }} + priorityClassName: {{ .Values.telemetryCollector.priorityClassName }} + {{- end }} + volumes: + - emptyDir: + medium: Memory + name: consul-connect-inject-data + {{- if .Values.global.trustedCAs }} + - name: trusted-cas + emptyDir: + medium: "Memory" + {{- end }} + {{- if .Values.global.tls.enabled }} + {{- if not (or (and .Values.externalServers.enabled .Values.externalServers.useSystemRoots) .Values.global.secretsBackend.vault.enabled) }} + - name: consul-ca-cert + secret: + {{- if .Values.global.tls.caCert.secretName }} + secretName: {{ .Values.global.tls.caCert.secretName }} + {{- else }} + secretName: {{ template "consul.fullname" . }}-ca-cert + {{- end }} + items: + - key: {{ default "tls.crt" .Values.global.tls.caCert.secretKey }} + path: tls.crt + {{- end }} + {{- end }} + - name: config + configMap: + name: {{ template "consul.fullname" . }}-telemetry-collector +{{- end }} diff --git a/charts/consul/templates/telemetry-collector-podsecuritypolicy.yaml b/charts/consul/templates/telemetry-collector-podsecuritypolicy.yaml new file mode 100644 index 0000000000..286a92d0bd --- /dev/null +++ b/charts/consul/templates/telemetry-collector-podsecuritypolicy.yaml @@ -0,0 +1,40 @@ +{{- if and .Values.global.enablePodSecurityPolicies .Values.telemetryCollector.enabled }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: telemetry-collector +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + - 'projected' + - 'secret' + - 'downwardAPI' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/consul/templates/telemetry-collector-role.yaml b/charts/consul/templates/telemetry-collector-role.yaml new file mode 100644 index 0000000000..f89373252d --- /dev/null +++ b/charts/consul/templates/telemetry-collector-role.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.global.enablePodSecurityPolicies .Values.telemetryCollector.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: consul-telemetry-collector +rules: + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: + - {{ template "consul.fullname" . }}-telemetry-collector + verbs: + - use +{{- end }} + diff --git a/charts/consul/templates/telemetry-collector-rolebinding.yaml b/charts/consul/templates/telemetry-collector-rolebinding.yaml new file mode 100644 index 0000000000..1f9a896997 --- /dev/null +++ b/charts/consul/templates/telemetry-collector-rolebinding.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.global.enablePodSecurityPolicies .Values.telemetryCollector.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + component: consul-telemetry-collector +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-telemetry-collector +subjects: + - kind: ServiceAccount + name: {{ template "consul.fullname" . }}-telemetry-collector +{{- end }} + diff --git a/charts/consul/templates/telemetry-collector-service.yaml b/charts/consul/templates/telemetry-collector-service.yaml new file mode 100644 index 0000000000..266c80b4bf --- /dev/null +++ b/charts/consul/templates/telemetry-collector-service.yaml @@ -0,0 +1,24 @@ +{{- if .Values.telemetryCollector.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: consul-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{ if .Values.telemetryCollector.service.annotations }} + annotations: + {{ tpl .Values.telemetryCollector.service.annotations . | nindent 4 | trim }} + {{- end }} +spec: + type: ClusterIP + ports: + - port: 9356 + targetPort: 9356 + selector: + app: consul + component: consul-telemetry-collector +{{- end }} \ No newline at end of file diff --git a/charts/consul/templates/telemetry-collector-serviceaccount.yaml b/charts/consul/templates/telemetry-collector-serviceaccount.yaml new file mode 100644 index 0000000000..fca58eede9 --- /dev/null +++ b/charts/consul/templates/telemetry-collector-serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- if .Values.telemetryCollector.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: consul-telemetry-collector + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: consul-telemetry-collector + {{- if .Values.telemetryCollector.serviceAccount.annotations }} + annotations: + {{ tpl .Values.telemetryCollector.serviceAccount.annotations . | nindent 4 | trim }} + {{- end }} +automountServiceAccountToken: true +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/consul/templates/terminating-gateways-deployment.yaml b/charts/consul/templates/terminating-gateways-deployment.yaml index 2f2cb9a921..9433e44bc9 100644 --- a/charts/consul/templates/terminating-gateways-deployment.yaml +++ b/charts/consul/templates/terminating-gateways-deployment.yaml @@ -160,6 +160,7 @@ spec: # terminating-gateway-init registers the terminating gateway service with Consul. - name: terminating-gateway-init image: {{ $root.Values.global.imageK8S }} + {{- include "consul.restrictedSecurityContext" $ | nindent 10 }} env: - name: NAMESPACE valueFrom: @@ -196,7 +197,7 @@ spec: -gateway-kind="terminating-gateway" \ -proxy-id-file=/consul/service/proxy-id \ -service-name={{ .name }} \ - -log-level={{ default $root.Values.global.logLevel }} \ + -log-level={{ default $root.Values.global.logLevel $root.Values.terminatingGateways.logLevel }} \ -log-json={{ $root.Values.global.logJSON }} volumeMounts: - name: consul-service @@ -218,6 +219,7 @@ spec: containers: - name: terminating-gateway image: {{ $root.Values.global.imageConsulDataplane | quote }} + {{- include "consul.restrictedSecurityContext" $ | nindent 10 }} volumeMounts: - name: consul-service mountPath: /consul/service @@ -300,7 +302,7 @@ spec: {{- if $root.Values.global.adminPartitions.enabled }} - -service-partition={{ $root.Values.global.adminPartitions.name }} {{- end }} - - -log-level={{ default $root.Values.global.logLevel }} + - -log-level={{ default $root.Values.global.logLevel $root.Values.terminatingGateways.logLevel }} - -log-json={{ $root.Values.global.logJSON }} {{- if (and $root.Values.global.metrics.enabled $root.Values.global.metrics.enableGatewayMetrics) }} - -telemetry-prom-scrape-path=/metrics diff --git a/charts/consul/templates/tls-init-cleanup-job.yaml b/charts/consul/templates/tls-init-cleanup-job.yaml index ba29bb84ae..2254a38ed2 100644 --- a/charts/consul/templates/tls-init-cleanup-job.yaml +++ b/charts/consul/templates/tls-init-cleanup-job.yaml @@ -35,12 +35,22 @@ spec: {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.global.tls.annotations }} + {{- tpl .Values.global.tls.annotations . | nindent 8 }} + {{- end }} spec: restartPolicy: Never serviceAccountName: {{ template "consul.fullname" . }}-tls-init-cleanup + {{- if .Values.server.containerSecurityContext.tlsInit }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.tlsInit | nindent 8 }} + {{- end }} containers: - name: tls-init-cleanup image: "{{ .Values.global.image }}" + {{- if not .Values.server.containerSecurityContext.tlsInit }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + {{- end }} env: - name: NAMESPACE valueFrom: diff --git a/charts/consul/templates/tls-init-job.yaml b/charts/consul/templates/tls-init-job.yaml index d002ae7a75..47651fe14b 100644 --- a/charts/consul/templates/tls-init-job.yaml +++ b/charts/consul/templates/tls-init-job.yaml @@ -35,9 +35,16 @@ spec: {{- end }} annotations: "consul.hashicorp.com/connect-inject": "false" + {{- if .Values.global.tls.annotations }} + {{- tpl .Values.global.tls.annotations . | nindent 8 }} + {{- end }} spec: restartPolicy: Never serviceAccountName: {{ template "consul.fullname" . }}-tls-init + {{- if .Values.server.containerSecurityContext.tlsInit }} + securityContext: + {{- toYaml .Values.server.containerSecurityContext.tlsInit | nindent 8 }} + {{- end }} {{- if (and .Values.global.tls.caCert.secretName .Values.global.tls.caKey.secretName) }} volumes: - name: consul-ca-cert @@ -56,6 +63,9 @@ spec: containers: - name: tls-init image: "{{ .Values.global.imageK8S }}" + {{- if not .Values.server.containerSecurityContext.tlsInit }} + {{- include "consul.restrictedSecurityContext" . | nindent 10 }} + {{- end }} env: - name: NAMESPACE valueFrom: @@ -70,7 +80,7 @@ spec: # and use * at the start of the dns name when setting -additional-dnsname. set -o noglob consul-k8s-control-plane tls-init \ - -log-level={{ .Values.global.logLevel }} \ + -log-level={{ default .Values.global.logLevel .Values.global.tls.logLevel }} \ -log-json={{ .Values.global.logJSON }} \ -domain={{ .Values.global.domain }} \ -days=730 \ diff --git a/charts/consul/templates/ui-ingress.yaml b/charts/consul/templates/ui-ingress.yaml index 0414a7cc2d..f8c7f92a77 100644 --- a/charts/consul/templates/ui-ingress.yaml +++ b/charts/consul/templates/ui-ingress.yaml @@ -25,9 +25,11 @@ metadata: {{ tpl .Values.ui.ingress.annotations . | nindent 4 | trim }} {{- end }} spec: + {{- if ne .Values.ui.ingress.ingressClassName "" }} ingressClassName: {{ .Values.ui.ingress.ingressClassName }} + {{- end }} rules: - {{ $global := .Values.global }} + {{- $global := .Values.global }} {{- if or ( gt .Capabilities.KubeVersion.Major "1" ) ( ge .Capabilities.KubeVersion.Minor "19" ) }} {{- range .Values.ui.ingress.hosts }} - host: {{ .host | quote }} diff --git a/charts/consul/templates/webhook-cert-manager-deployment.yaml b/charts/consul/templates/webhook-cert-manager-deployment.yaml index dd93c039d2..7ba25b330c 100644 --- a/charts/consul/templates/webhook-cert-manager-deployment.yaml +++ b/charts/consul/templates/webhook-cert-manager-deployment.yaml @@ -51,6 +51,7 @@ spec: -deployment-namespace={{ .Release.Namespace }} image: {{ .Values.global.imageK8S }} name: webhook-cert-manager + {{- include "consul.restrictedSecurityContext" . | nindent 8 }} resources: limits: cpu: 100m diff --git a/charts/consul/test/terraform/aks/main.tf b/charts/consul/test/terraform/aks/main.tf index 1db5145531..f15f1d106b 100644 --- a/charts/consul/test/terraform/aks/main.tf +++ b/charts/consul/test/terraform/aks/main.tf @@ -1,5 +1,15 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +terraform { + required_providers { + azurerm = { + version = "3.40.0" + } + } +} + provider "azurerm" { - version = "3.40.0" features {} } @@ -45,7 +55,7 @@ resource "azurerm_kubernetes_cluster" "default" { location = azurerm_resource_group.default[count.index].location resource_group_name = azurerm_resource_group.default[count.index].name dns_prefix = "consul-k8s-${random_id.suffix[count.index].dec}" - kubernetes_version = "1.24.6" + kubernetes_version = var.kubernetes_version role_based_access_control_enabled = true // We're setting the network plugin and other network properties explicitly diff --git a/charts/consul/test/terraform/aks/variables.tf b/charts/consul/test/terraform/aks/variables.tf index bb9dbef537..bb8d48251a 100644 --- a/charts/consul/test/terraform/aks/variables.tf +++ b/charts/consul/test/terraform/aks/variables.tf @@ -1,8 +1,16 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + variable "location" { default = "West US 2" description = "The location to launch this AKS cluster in." } +variable "kubernetes_version" { + default = "1.25" + description = "Kubernetes version supported on AKS" +} + variable "client_id" { default = "" description = "The client ID of the service principal to be used by Kubernetes when creating Azure resources like load balancers." diff --git a/charts/consul/test/unit/api-gateway-controller-deployment.bats b/charts/consul/test/unit/api-gateway-controller-deployment.bats index 2dbcb9e0f1..327802af07 100755 --- a/charts/consul/test/unit/api-gateway-controller-deployment.bats +++ b/charts/consul/test/unit/api-gateway-controller-deployment.bats @@ -1418,6 +1418,24 @@ load _helpers [ "${actual}" = "true" ] } +@test "apiGateway/Deployment: CONSUL_TLS_SERVER_NAME will not be set for when clients are used" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.httpsPort=8501' \ + --set 'externalServers.tlsServerName=hashi' \ + --set 'client.enabled=true' \ + --set 'server.enabled=false' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[] | select (.name == "api-gateway-controller") | .env[] | select(.name == "CONSUL_TLS_SERVER_NAME")' | tee /dev/stderr) + [ "${actual}" = "" ] +} + #-------------------------------------------------------------------- # Admin Partitions @@ -1504,6 +1522,23 @@ load _helpers [ "${actual}" = "true" ] } +@test "apiGateway/Deployment: CONSUL_CACERT has correct path with Vault as secrets backend and client disabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'server.enabled=true' \ + --set 'client.enabled=false' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + . | tee /dev/stderr| + yq '.spec.template.spec.containers[0].env[0].value == "/vault/secrets/serverca.crt"' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + @test "apiGateway/Deployment: CONSUL_CACERT is not set when using tls and useSystemRoots" { cd `chart_dir` local actual=$(helm template \ @@ -1537,6 +1572,21 @@ load _helpers [ "${actual}" = "" ] } +@test "apiGateway/Deployment: consul-ca-cert volume mount is not set when using Vault as a secrets backend" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'server.enabled=true' \ + --set 'global.secretsBackend.vault.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" = "" ] +} + @test "apiGateway/Deployment: consul-ca-cert volume mount is not set on acl-init when using externalServers and useSystemRoots" { cd `chart_dir` local actual=$(helm template \ @@ -1554,6 +1604,21 @@ load _helpers [ "${actual}" = "" ] } +@test "apiGateway/Deployment: consul-ca-cert volume mount is not set on acl-init when using Vault as secrets backend" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/api-gateway-controller-deployment.yaml \ + --set 'apiGateway.enabled=true' \ + --set 'apiGateway.image=bar' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'server.enabled=true' \ + --set 'global.secretsBackend.vault.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.initContainers[1].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" = "" ] +} + @test "apiGateway/Deployment: consul-auto-encrypt-ca-cert volume mount is set when tls.enabled, client.enabled, externalServers, useSystemRoots, and autoencrypt" { cd `chart_dir` local actual=$(helm template \ diff --git a/charts/consul/test/unit/client-config-configmap.bats b/charts/consul/test/unit/client-config-configmap.bats index 5fc4a186d9..1f1443a156 100755 --- a/charts/consul/test/unit/client-config-configmap.bats +++ b/charts/consul/test/unit/client-config-configmap.bats @@ -95,3 +95,29 @@ load _helpers [ "${actual}" = null ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "client/ConfigMap: client.logLevel is empty" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/client-config-configmap.yaml \ + --set 'client.enabled=true' \ + . | tee /dev/stderr | + yq -r '.data["log-level.json"]' | jq -r .log_level | tee /dev/stderr) + + [ "${actual}" = "null" ] +} + +@test "client/ConfigMap: client.logLevel is non empty" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/client-config-configmap.yaml \ + --set 'client.enabled=true' \ + --set 'client.logLevel=DEBUG' \ + . | tee /dev/stderr | + yq -r '.data["log-level.json"]' | jq -r .log_level | tee /dev/stderr) + + [ "${actual}" = "DEBUG" ] +} diff --git a/charts/consul/test/unit/client-daemonset.bats b/charts/consul/test/unit/client-daemonset.bats index 4c38207635..d512ad8ab2 100755 --- a/charts/consul/test/unit/client-daemonset.bats +++ b/charts/consul/test/unit/client-daemonset.bats @@ -621,7 +621,7 @@ load _helpers --set 'client.enabled=true' \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = f9be2829fed80a127e3752e10be32f29c2f9ca0ea548abcf3d4fc2c985cb7201 ] + [ "${actual}" = 4fa9ddc3abc4c79eafccb19e5beef80006b7c9736b867d8873554ca03f42a6b3 ] } @test "client/DaemonSet: config-checksum annotation changes when extraConfig is provided" { @@ -632,7 +632,7 @@ load _helpers --set 'client.extraConfig="{\"hello\": \"world\"}"' \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = e9fb5f0b4ff4e36a89e8ca2dc1aed2072306e0dd6d4cc60b3edf155cf8dbe2e9 ] + [ "${actual}" = 42b99932385e7a0580b134fe36a9bda405aab2e375593326677b9838708f0796 ] } @test "client/DaemonSet: config-checksum annotation changes when connectInject.enabled=true" { @@ -643,7 +643,7 @@ load _helpers --set 'connectInject.enabled=true' \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = f9be2829fed80a127e3752e10be32f29c2f9ca0ea548abcf3d4fc2c985cb7201 ] + [ "${actual}" = 4fa9ddc3abc4c79eafccb19e5beef80006b7c9736b867d8873554ca03f42a6b3 ] } #-------------------------------------------------------------------- @@ -2127,29 +2127,6 @@ rollingUpdate: [[ "$output" =~ "If global.federation.enabled is true, global.adminPartitions.enabled must be false because they are mutually exclusive" ]] } -@test "client/DaemonSet: consul login datacenter is set to primary when when federation enabled in non-primary datacenter" { - cd `chart_dir` - local object=$(helm template \ - -s templates/client-daemonset.yaml \ - --set 'client.enabled=true' \ - --set 'meshGateway.enabled=true' \ - --set 'global.acls.manageSystemACLs=true' \ - --set 'global.datacenter=dc1' \ - --set 'global.federation.enabled=true' \ - --set 'global.federation.primaryDatacenter=dc2' \ - --set 'global.tls.enabled=true' \ - . | tee /dev/stderr | - yq '.spec.template.spec.initContainers[] | select(.name == "client-acl-init")' | tee /dev/stderr) - - local actual=$(echo $object | - yq '[.env[11].name] | any(contains("CONSUL_LOGIN_DATACENTER"))' | tee /dev/stderr) - [ "${actual}" = "true" ] - - local actual=$(echo $object | - yq '[.env[11].value] | any(contains("dc2"))' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - #-------------------------------------------------------------------- # extraContainers diff --git a/charts/consul/test/unit/connect-inject-deployment.bats b/charts/consul/test/unit/connect-inject-deployment.bats index 9da24a7568..ccc6eca68c 100755 --- a/charts/consul/test/unit/connect-inject-deployment.bats +++ b/charts/consul/test/unit/connect-inject-deployment.bats @@ -211,6 +211,19 @@ load _helpers [ "${actual}" = "true" ] } +@test "connectInject/Deployment: metrics.enableTelemetryCollector can be configured" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'global.metrics.enableTelemetryCollector=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-enable-telemetry-collector=true"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} #-------------------------------------------------------------------- # consul and consul-dataplane images @@ -947,7 +960,7 @@ load _helpers --set 'connectInject.enabled=true' \ . | tee /dev/stderr | yq -rc '.spec.template.spec.containers[0].resources' | tee /dev/stderr) - [ "${actual}" = '{"limits":{"cpu":"50m","memory":"50Mi"},"requests":{"cpu":"50m","memory":"50Mi"}}' ] + [ "${actual}" = '{"limits":{"cpu":"50m","memory":"200Mi"},"requests":{"cpu":"50m","memory":"200Mi"}}' ] } @test "connectInject/Deployment: can set resources" { @@ -987,9 +1000,6 @@ load _helpers yq 'any(contains("-init-container-memory-limit=150Mi"))' | tee /dev/stderr) [ "${actual}" = "true" ] - local actual=$(echo "$cmd" | - yq 'any(contains("-init-container-cpu-limit=50m"))' | tee /dev/stderr) - [ "${actual}" = "true" ] } @test "connectInject/Deployment: can set init container resources" { @@ -1221,6 +1231,144 @@ load _helpers [ "${actual}" = "true" ] } +#-------------------------------------------------------------------- +# sidecarProxy.lifecycle + +@test "connectInject/Deployment: by default sidecar proxy lifecycle management is enabled" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-enable-sidecar-proxy-lifecycle"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: sidecar proxy lifecycle management can be disabled" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.sidecarProxy.lifecycle.defaultEnabled=false' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-enable-sidecar-proxy-lifecycle=false"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: by default sidecar proxy lifecycle management shutdown listener draining is enabled" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-enable-sidecar-proxy-lifecycle-shutdown-drain-listeners"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: sidecar proxy lifecycle management shutdown listener draining can be disabled" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.sidecarProxy.lifecycle.defaultEnableShutdownDrainListeners=false' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-enable-sidecar-proxy-lifecycle-shutdown-drain-listeners=false"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: by default sidecar proxy lifecycle management shutdown grace period is set to 30 seconds" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-sidecar-proxy-lifecycle-shutdown-grace-period-seconds=30"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: sidecar proxy lifecycle management shutdown grace period can be set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.sidecarProxy.lifecycle.defaultShutdownGracePeriodSeconds=23' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-sidecar-proxy-lifecycle-shutdown-grace-period-seconds=23"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: by default sidecar proxy lifecycle management port is set to 20600" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-sidecar-proxy-lifecycle-graceful-port=20600"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: sidecar proxy lifecycle management port can be set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.sidecarProxy.lifecycle.defaultGracefulPort=20307' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-sidecar-proxy-lifecycle-graceful-port=20307"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: by default sidecar proxy lifecycle management graceful shutdown path is set to /graceful_shutdown" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-sidecar-proxy-lifecycle-graceful-shutdown-path=\"/graceful_shutdown\""))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "connectInject/Deployment: sidecar proxy lifecycle management graceful shutdown path can be set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/connect-inject-deployment.yaml \ + --set 'connectInject.enabled=true' \ + --set 'connectInject.sidecarProxy.lifecycle.defaultGracefulShutdownPath=/exit' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-default-sidecar-proxy-lifecycle-graceful-shutdown-path=\"/exit\""))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + #-------------------------------------------------------------------- # priorityClassName @@ -1408,7 +1556,7 @@ load _helpers } #-------------------------------------------------------------------- -# cni +# cni @test "connectInject/Deployment: cni is disabled by default" { cd `chart_dir` @@ -2290,7 +2438,7 @@ reservedNameTest() { --set 'global.cloud.authUrl.secretName=auth-url-name' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] } @@ -2311,7 +2459,7 @@ reservedNameTest() { --set 'global.cloud.authUrl.secretKey=auth-url-key' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] } @@ -2332,7 +2480,7 @@ reservedNameTest() { --set 'global.cloud.apiHost.secretName=auth-url-name' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] } @@ -2353,7 +2501,7 @@ reservedNameTest() { --set 'global.cloud.apiHost.secretKey=auth-url-key' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] } @@ -2374,7 +2522,7 @@ reservedNameTest() { --set 'global.cloud.scadaAddress.secretName=scada-address-name' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] } @@ -2395,7 +2543,7 @@ reservedNameTest() { --set 'global.cloud.scadaAddress.secretKey=scada-address-key' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] } diff --git a/charts/consul/test/unit/create-federation-secret-job.bats b/charts/consul/test/unit/create-federation-secret-job.bats index e528f28f0e..872cf2e36c 100644 --- a/charts/consul/test/unit/create-federation-secret-job.bats +++ b/charts/consul/test/unit/create-federation-secret-job.bats @@ -418,3 +418,41 @@ load _helpers [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "createFederationSecret/Job: logLevel is not set by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/create-federation-secret-job.yaml \ + --set 'global.federation.enabled=true' \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.federation.createFederationSecret=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "createFederationSecret/Job: override the global.logLevel flag with global.federation.logLevel" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/create-federation-secret-job.yaml \ + --set 'global.federation.enabled=true' \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.tls.enabled=true' \ + --set 'global.federation.createFederationSecret=true' \ + --set 'global.federation.logLevel=debug' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats b/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats index 662b523bc0..7520696182 100644 --- a/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats +++ b/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats @@ -105,3 +105,33 @@ load _helpers [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "gossipEncryptionAutogenerate/Job: uses the global.logLevel flag by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/gossip-encryption-autogenerate-job.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "gossipEncryptionAutogenerate/Job: overrides the global.logLevel flag when global.gossipEncryption.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/gossip-encryption-autogenerate-job.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + --set 'global.gossipEncryption.logLevel=debug' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/charts/consul/test/unit/ingress-gateways-deployment.bats b/charts/consul/test/unit/ingress-gateways-deployment.bats index 8ed76be13a..e8390278a8 100644 --- a/charts/consul/test/unit/ingress-gateways-deployment.bats +++ b/charts/consul/test/unit/ingress-gateways-deployment.bats @@ -1504,3 +1504,64 @@ key2: value2' \ [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "ingressGateways/Deployment: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "ingressGateways/Deployment: override global.logLevel when ingressGateways.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'ingressGateways.logLevel=warn' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=warn"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "ingressGateways/Deployment: use global.logLevel by default for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "ingressGateways/Deployment: override global.logLevel when ingressGateways.logLevel is set for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/ingress-gateways-deployment.yaml \ + --set 'ingressGateways.enabled=true' \ + --set 'ingressGateways.logLevel=trace' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=trace"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/mesh-gateway-deployment.bats b/charts/consul/test/unit/mesh-gateway-deployment.bats index 588b026d40..d58def05da 100755 --- a/charts/consul/test/unit/mesh-gateway-deployment.bats +++ b/charts/consul/test/unit/mesh-gateway-deployment.bats @@ -1644,3 +1644,64 @@ key2: value2' \ [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "meshGateway/Deployment: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/mesh-gateway-deployment.yaml \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "meshGateway/Deployment: override global.logLevel when meshGateway.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/mesh-gateway-deployment.yaml \ + --set 'meshGateway.enabled=true' \ + --set 'meshGateway.logLevel=warn' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=warn"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "meshGateway/Deployment: use global.logLevel by default for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/mesh-gateway-deployment.yaml \ + --set 'meshGateway.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "meshGateway/Deployment: override global.logLevel when meshGateway.logLevel is set for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/mesh-gateway-deployment.yaml \ + --set 'meshGateway.enabled=true' \ + --set 'meshGateway.logLevel=warn' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=warn"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/server-acl-init-cleanup-job.bats b/charts/consul/test/unit/server-acl-init-cleanup-job.bats index 947cfa9b42..8743ea4a8d 100644 --- a/charts/consul/test/unit/server-acl-init-cleanup-job.bats +++ b/charts/consul/test/unit/server-acl-init-cleanup-job.bats @@ -159,3 +159,94 @@ load _helpers [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "serverACLInitCleanup/Job: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "serverACLInitCleanup/Job: override global.logLevel when global.acls.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.acls.logLevel=debug' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} +# resources + +@test "serverACLInitCleanup/Job: resources defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -rc '.spec.template.spec.containers[0].resources' | tee /dev/stderr) + [ "${actual}" = '{"limits":{"cpu":"50m","memory":"50Mi"},"requests":{"cpu":"50m","memory":"50Mi"}}' ] +} + +@test "serverACLInitCleanup/Job: resources can be overridden" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.acls.resources.foo=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +#-------------------------------------------------------------------- +# server.containerSecurityContext.aclInit + +@test "serverACLInitCleanup/Job: securityContext is set when server.containerSecurityContext.aclInit is set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'server.containerSecurityContext.aclInit.runAsUser=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + + [ "${actual}" = "100" ] +} + +#-------------------------------------------------------------------- +# annotations + +@test "serverACLInitCleanup/Job: no annotations defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."consul.hashicorp.com/config-checksum")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "serverACLInitCleanup/Job: annotations can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-cleanup-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.acls.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/charts/consul/test/unit/server-acl-init-job.bats b/charts/consul/test/unit/server-acl-init-job.bats index 63450aa4c2..1dc55a9551 100644 --- a/charts/consul/test/unit/server-acl-init-job.bats +++ b/charts/consul/test/unit/server-acl-init-job.bats @@ -590,6 +590,22 @@ load _helpers [ "${actual}" = "key" ] } +#-------------------------------------------------------------------- +# server.containerSecurityContext.aclInit + +@test "serverACLInit/Job: securityContext is set when server.containerSecurityContext.aclInit is set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'server.containerSecurityContext.aclInit.runAsUser=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + + [ "${actual}" = "100" ] + +} + #-------------------------------------------------------------------- # Vault @@ -634,7 +650,19 @@ load _helpers yq -r '.spec.template' | tee /dev/stderr) # Check annotations + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate-only"]' | tee /dev/stderr) + [ "${actual}" = "false" ] + + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-cache-enable"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-cache-listener-port"]' | tee /dev/stderr) + [ "${actual}" = "8200" ] + + actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-enable-quit"]' | tee /dev/stderr) [ "${actual}" = "true" ] local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr) @@ -643,15 +671,13 @@ load _helpers local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) [ "${actual}" = "aclrole" ] - local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-secret-bootstrap-token"]' | tee /dev/stderr) - [ "${actual}" = "foo" ] + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-secrets-backend=vault"))') + [ "${actual}" = "true" ] - local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-template-bootstrap-token"]' | tee /dev/stderr) - local expected=$'{{- with secret \"foo\" -}}\n{{- .Data.data.bar -}}\n{{- end -}}' - [ "${actual}" = "${expected}" ] + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-name=foo"))') + [ "${actual}" = "true" ] - # Check that the bootstrap token flag is set to the path of the Vault secret. - local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-file=/vault/secrets/bootstrap-token"))') + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-key=bar"))') [ "${actual}" = "true" ] # Check that no (secret) volumes are not attached @@ -682,20 +708,31 @@ load _helpers yq -r '.spec.template' | tee /dev/stderr) # Check annotations - local actual - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate-only"]' | tee /dev/stderr) + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate"]' | tee /dev/stderr) [ "${actual}" = "true" ] - local actual - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr) + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-pre-populate-only"]' | tee /dev/stderr) + [ "${actual}" = "false" ] + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-cache-enable"]' | tee /dev/stderr) [ "${actual}" = "true" ] - local actual - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-cache-listener-port"]' | tee /dev/stderr) + [ "${actual}" = "8200" ] + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-enable-quit"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr) + [ "${actual}" = "true" ] + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr) [ "${actual}" = "aclrole" ] - local actual - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-secret-serverca.crt"]' | tee /dev/stderr) + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-secret-serverca.crt"]' | tee /dev/stderr) [ "${actual}" = "foo" ] - local actual - actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-template-serverca.crt"]' | tee /dev/stderr) + + local actual=$(echo $object | jq -r '.metadata.annotations["vault.hashicorp.com/agent-inject-template-serverca.crt"]' | tee /dev/stderr) [ "${actual}" = $'{{- with secret \"foo\" -}}\n{{- .Data.certificate -}}\n{{- end -}}' ] # Check that the consul-ca-cert volume is not attached @@ -881,12 +918,14 @@ load _helpers local expected=$'{{- with secret \"/vault/replication\" -}}\n{{- .Data.data.token -}}\n{{- end -}}' [ "${actual}" = "${expected}" ] - local actual=$(echo $object | yq -r '.metadata.annotations."vault.hashicorp.com/agent-inject-secret-bootstrap-token"') - [ "${actual}" = "/vault/bootstrap" ] + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-secrets-backend=vault"))') + [ "${actual}" = "true" ] - local actual=$(echo $object | yq -r '.metadata.annotations."vault.hashicorp.com/agent-inject-template-bootstrap-token"') - local expected=$'{{- with secret \"/vault/bootstrap\" -}}\n{{- .Data.data.token -}}\n{{- end -}}' - [ "${actual}" = "${expected}" ] + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-name=/vault/bootstrap"))') + [ "${actual}" = "true" ] + + local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-key=token"))') + [ "${actual}" = "true" ] # Check that replication token Kubernetes secret volumes and volumeMounts are not attached. local actual=$(echo $object | jq -r '.spec.volumes') @@ -895,12 +934,9 @@ load _helpers local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").volumeMounts') [ "${actual}" = "null" ] - # Check that the replication and bootstrap token flags are set to the path of the Vault secret. + # Replication token file is passed. local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-acl-replication-token-file=/vault/secrets/replication-token"))') [ "${actual}" = "true" ] - - local actual=$(echo $object | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-file=/vault/secrets/bootstrap-token"))') - [ "${actual}" = "true" ] } #-------------------------------------------------------------------- @@ -953,7 +989,7 @@ load _helpers #-------------------------------------------------------------------- # Vault agent annotations -@test "serverACLInit/Job: no vault agent annotations defined by default" { +@test "serverACLInit/Job: default vault agent annotations" { cd `chart_dir` local actual=$(helm template \ -s templates/server-acl-init-job.yaml \ @@ -967,8 +1003,21 @@ load _helpers --set 'global.secretsBackend.vault.consulCARole=carole' \ --set 'global.secretsBackend.vault.manageSystemACLsRole=aclrole' \ . | tee /dev/stderr | - yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."vault.hashicorp.com/agent-inject") | del(."vault.hashicorp.com/agent-pre-populate-only") | del(."vault.hashicorp.com/role") | del(."vault.hashicorp.com/agent-inject-secret-bootstrap-token") | del(."vault.hashicorp.com/agent-inject-template-bootstrap-token")' | tee /dev/stderr) - [ "${actual}" = "{}" ] + yq -r .spec.template.metadata.annotations | tee /dev/stderr) + + local expected=$(echo '{ + "consul.hashicorp.com/connect-inject": "false", + "vault.hashicorp.com/agent-inject": "true", + "vault.hashicorp.com/agent-pre-populate": "true", + "vault.hashicorp.com/agent-pre-populate-only": "false", + "vault.hashicorp.com/agent-cache-enable": "true", + "vault.hashicorp.com/agent-cache-listener-port": "8200", + "vault.hashicorp.com/agent-enable-quit": "true", + "vault.hashicorp.com/role": "aclrole" + }' | tee /dev/stderr) + + local equal=$(jq -n --argjson a "$actual" --argjson b "$expected" '$a == $b') + [ "$equal" = "true" ] } @test "serverACLInit/Job: vault agent annotations can be set" { @@ -1807,55 +1856,23 @@ load _helpers [[ "$output" =~ "both global.acls.bootstrapToken.secretKey and global.acls.bootstrapToken.secretName must be set if one of them is provided" ]] } -@test "serverACLInit/Job: -bootstrap-token-file is not set by default" { - cd `chart_dir` - local object=$(helm template \ - -s templates/server-acl-init-job.yaml \ - --set 'global.acls.manageSystemACLs=true' \ - . | tee /dev/stderr) - - # Test the flag is not set. - local actual=$(echo "$object" | - yq '.spec.template.spec.containers[0].command | any(contains("-bootstrap-token-file"))' | tee /dev/stderr) - [ "${actual}" = "false" ] - - # Test the volume doesn't exist - local actual=$(echo "$object" | - yq '.spec.template.spec.volumes | length == 0' | tee /dev/stderr) - [ "${actual}" = "true" ] - - # Test the volume mount doesn't exist - local actual=$(echo "$object" | - yq '.spec.template.spec.containers[0].volumeMounts | length == 0' | tee /dev/stderr) - [ "${actual}" = "true" ] -} - -@test "serverACLInit/Job: -bootstrap-token-file is set when acls.bootstrapToken.secretKey and secretName are set" { +@test "serverACLInit/Job: bootstrap token secret is passed when acls.bootstrapToken.secretKey and secretName are set" { cd `chart_dir` local object=$(helm template \ -s templates/server-acl-init-job.yaml \ --set 'global.acls.manageSystemACLs=true' \ --set 'global.acls.bootstrapToken.secretName=name' \ --set 'global.acls.bootstrapToken.secretKey=key' \ - . | tee /dev/stderr) - - # Test the -bootstrap-token-file flag is set. - local actual=$(echo "$object" | - yq '.spec.template.spec.containers[0].command | any(contains("-bootstrap-token-file=/consul/acl/tokens/bootstrap-token"))' | tee /dev/stderr) - [ "${actual}" = "true" ] + . | yq .spec.template | tee /dev/stderr) - # Test the volume exists - local actual=$(echo "$object" | - yq '.spec.template.spec.volumes | map(select(.name == "bootstrap-token")) | length == 1' | tee /dev/stderr) + local actual=$(echo "$object" | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-name=name"))') [ "${actual}" = "true" ] - # Test the volume mount exists - local actual=$(echo "$object" | - yq '.spec.template.spec.containers[0].volumeMounts | map(select(.name == "bootstrap-token")) | length == 1' | tee /dev/stderr) + local actual=$(echo "$object" | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-key=key"))') [ "${actual}" = "true" ] } -@test "serverACLInit/Job: -bootstrap-token-file is preferred when both acls.bootstrapToken and acls.replicationToken are set" { +@test "serverACLInit/Job: bootstrap token secret is passed when both acl.bootstrapToken and acls.replicationToken are set" { cd `chart_dir` local object=$(helm template \ -s templates/server-acl-init-job.yaml \ @@ -1864,21 +1881,12 @@ load _helpers --set 'global.acls.bootstrapToken.secretKey=key' \ --set 'global.acls.replicationToken.secretName=replication' \ --set 'global.acls.replicationToken.secretKey=token' \ - . | tee /dev/stderr) + . | yq .spec.template | tee /dev/stderr) - # Test the -bootstrap-token-file flag is set. - local actual=$(echo "$object" | - yq '.spec.template.spec.containers[0].command | any(contains("-bootstrap-token-file=/consul/acl/tokens/bootstrap-token"))' | tee /dev/stderr) + local actual=$(echo "$object" | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-name=name"))') [ "${actual}" = "true" ] - # Test the volume exists - local actual=$(echo "$object" | - yq '.spec.template.spec.volumes | map(select(.name == "bootstrap-token")) | length == 1' | tee /dev/stderr) - [ "${actual}" = "true" ] - - # Test the volume mount exists - local actual=$(echo "$object" | - yq '.spec.template.spec.containers[0].volumeMounts | map(select(.name == "bootstrap-token")) | length == 1' | tee /dev/stderr) + local actual=$(echo "$object" | jq -r '.spec.containers[] | select(.name=="server-acl-init-job").command | any(contains("-bootstrap-token-secret-key=key"))') [ "${actual}" = "true" ] } @@ -2038,7 +2046,7 @@ load _helpers --set 'global.cloud.authUrl.secretName=auth-url-name' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] } @@ -2058,7 +2066,7 @@ load _helpers --set 'global.cloud.authUrl.secretKey=auth-url-key' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] } @@ -2078,7 +2086,7 @@ load _helpers --set 'global.cloud.apiHost.secretName=auth-url-name' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] } @@ -2098,7 +2106,7 @@ load _helpers --set 'global.cloud.apiHost.secretKey=auth-url-key' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] } @@ -2118,7 +2126,7 @@ load _helpers --set 'global.cloud.scadaAddress.secretName=scada-address-name' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] } @@ -2138,7 +2146,7 @@ load _helpers --set 'global.cloud.scadaAddress.secretKey=scada-address-key' \ . [ "$status" -eq 1 ] - + [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] } @@ -2210,3 +2218,81 @@ load _helpers [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "serverACLInit/Job: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "serverACLInit/Job: override global.logLevel when global.acls.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.acls.logLevel=debug' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# resources + +@test "serverACLInit/Job: resources defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -rc '.spec.template.spec.containers[0].resources' | tee /dev/stderr) + [ "${actual}" = '{"limits":{"cpu":"50m","memory":"50Mi"},"requests":{"cpu":"50m","memory":"50Mi"}}' ] +} + +@test "serverACLInit/Job: resources can be overridden" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.acls.resources.foo=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +#-------------------------------------------------------------------- +# annotations + +@test "serverACLInit/Job: no annotations defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."consul.hashicorp.com/config-checksum")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "serverACLInit/Job: annotations can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-acl-init-job.yaml \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.acls.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/charts/consul/test/unit/server-config-configmap.bats b/charts/consul/test/unit/server-config-configmap.bats index 5e849d5a4f..ad1c79d248 100755 --- a/charts/consul/test/unit/server-config-configmap.bats +++ b/charts/consul/test/unit/server-config-configmap.bats @@ -965,3 +965,164 @@ load _helpers [ "${actual}" = "true" ] } + +#-------------------------------------------------------------------- +# server.auditLogs + +@test "server/ConfigMap: server.auditLogs is disabled by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.auditLogs.enabled=false' \ + . | tee /dev/stderr | + yq -r '.data["audit-logging.json"]' | jq -r .audit | tee /dev/stderr) + + [ "${actual}" = "null" ] +} + +@test "server/ConfigMap: server.auditLogs is enabled but ACLs are disabled" { + cd `chart_dir` + run helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.auditLogs.enabled=true' \ + --set 'server.auditLogs.sinks[0].name=MySink' \ + --set 'server.auditLogs.sinks[0].type=file' \ + --set 'server.auditLogs.sinks[0].format=json' \ + --set 'server.auditLogs.sinks[0].delivery_guarantee=best-effort' \ + --set 'server.auditLogs.sinks[0].rotate_duration=24h' \ + --set 'server.auditLogs.sinks[0].path=/tmp/audit.json' \ + . + + [ "$status" -eq 1 ] + [[ "$output" =~ "ACLs must be enabled inorder to configure audit logs" ]] +} + +@test "server/ConfigMap: server.auditLogs is enabled without sink inputs" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.auditLogs.enabled=true' \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.data["audit-logging.json"]' | jq -r .audit.sink | tee /dev/stderr) + + [ "${actual}" = "{}" ] +} + +@test "server/ConfigMap: server.auditLogs is enabled with 1 sink input object" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.auditLogs.enabled=true' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'server.auditLogs.sinks[0].name=MySink' \ + --set 'server.auditLogs.sinks[0].type=file' \ + --set 'server.auditLogs.sinks[0].format=json' \ + --set 'server.auditLogs.sinks[0].delivery_guarantee=best-effort' \ + --set 'server.auditLogs.sinks[0].rotate_duration=24h' \ + --set 'server.auditLogs.sinks[0].path=/tmp/audit.json' \ + . | tee /dev/stderr | + yq -r '.data["audit-logging.json"]' | tee /dev/stderr) + + local actual=$(echo $object | jq -r .audit.sink.MySink.path | tee /dev/stderr) + [ "${actual}" = "/tmp/audit.json" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink.delivery_guarantee | tee /dev/stderr) + [ "${actual}" = "best-effort" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink.rotate_duration | tee /dev/stderr) + [ "${actual}" = "24h" ] +} + +@test "server/ConfigMap: server.auditLogs is enabled with 1 sink input object and it does not contain the name attribute" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.auditLogs.enabled=true' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'server.auditLogs.sinks[0].name=MySink' \ + --set 'server.auditLogs.sinks[0].type=file' \ + --set 'server.auditLogs.sinks[0].format=json' \ + --set 'server.auditLogs.sinks[0].delivery_guarantee=best-effort' \ + --set 'server.auditLogs.sinks[0].rotate_duration=24h' \ + --set 'server.auditLogs.sinks[0].path=/tmp/audit.json' \ + . | tee /dev/stderr | + yq -r '.data["audit-logging.json"]' | jq -r .audit.sink.name | tee /dev/stderr) + + [ "${actual}" = "null" ] +} + +@test "server/ConfigMap: server.auditLogs is enabled with multiple sink input objects" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.auditLogs.enabled=true' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'server.auditLogs.sinks[0].name=MySink1' \ + --set 'server.auditLogs.sinks[0].type=file' \ + --set 'server.auditLogs.sinks[0].format=json' \ + --set 'server.auditLogs.sinks[0].delivery_guarantee=best-effort' \ + --set 'server.auditLogs.sinks[0].rotate_duration=24h' \ + --set 'server.auditLogs.sinks[0].path=/tmp/audit.json' \ + --set 'server.auditLogs.sinks[1].name=MySink2' \ + --set 'server.auditLogs.sinks[1].type=file' \ + --set 'server.auditLogs.sinks[1].format=json' \ + --set 'server.auditLogs.sinks[1].delivery_guarantee=best-effort' \ + --set 'server.auditLogs.sinks[1].rotate_max_files=15' \ + --set 'server.auditLogs.sinks[1].rotate_duration=24h' \ + --set 'server.auditLogs.sinks[1].path=/tmp/audit-2.json' \ + --set 'server.auditLogs.sinks[2].name=MySink3' \ + --set 'server.auditLogs.sinks[2].type=file' \ + --set 'server.auditLogs.sinks[2].format=json' \ + --set 'server.auditLogs.sinks[2].delivery_guarantee=best-effort' \ + --set 'server.auditLogs.sinks[2].rotate_max_files=20' \ + --set 'server.auditLogs.sinks[2].rotate_duration=18h' \ + --set 'server.auditLogs.sinks[2].path=/tmp/audit-3.json' \ + . | tee /dev/stderr | + yq -r '.data["audit-logging.json"]' | tee /dev/stderr) + + local actual=$(echo $object | jq -r .audit.sink.MySink1.path | tee /dev/stderr) + [ "${actual}" = "/tmp/audit.json" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink3.path | tee /dev/stderr) + [ "${actual}" = "/tmp/audit-3.json" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink2.path | tee /dev/stderr) + [ "${actual}" = "/tmp/audit-2.json" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink1.name | tee /dev/stderr) + [ "${actual}" = "null" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink3.delivery_guarantee | tee /dev/stderr) + [ "${actual}" = "best-effort" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink2.rotate_duration | tee /dev/stderr) + [ "${actual}" = "24h" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink1.format | tee /dev/stderr) + [ "${actual}" = "json" ] + + local actual=$(echo $object | jq -r .audit.sink.MySink3.type | tee /dev/stderr) + [ "${actual}" = "file" ] +} + +@test "server/ConfigMap: server.logLevel is empty" { + cd `chart_dir` + local configmap=$(helm template \ + -s templates/server-config-configmap.yaml \ + . | tee /dev/stderr | + yq -r '.data["server.json"]' | jq -r .log_level | tee /dev/stderr) + + [ "${configmap}" = "null" ] +} + +@test "server/ConfigMap: server.logLevel is non empty" { + cd `chart_dir` + local configmap=$(helm template \ + -s templates/server-config-configmap.yaml \ + --set 'server.logLevel=debug' \ + . | tee /dev/stderr | + yq -r '.data["server.json"]' | jq -r .log_level | tee /dev/stderr) + + [ "${configmap}" = "DEBUG" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/server-statefulset.bats b/charts/consul/test/unit/server-statefulset.bats index 2d21cf7c1e..1310fdb181 100755 --- a/charts/consul/test/unit/server-statefulset.bats +++ b/charts/consul/test/unit/server-statefulset.bats @@ -99,7 +99,7 @@ load _helpers -s templates/server-statefulset.yaml \ . | tee /dev/stderr | yq -rc '.spec.template.spec.containers[0].resources' | tee /dev/stderr) - [ "${actual}" = '{"limits":{"cpu":"100m","memory":"100Mi"},"requests":{"cpu":"100m","memory":"100Mi"}}' ] + [ "${actual}" = '{"limits":{"cpu":"100m","memory":"200Mi"},"requests":{"cpu":"100m","memory":"200Mi"}}' ] } @test "server/StatefulSet: resources can be overridden" { @@ -677,6 +677,41 @@ load _helpers [ "${actual}" = "/v1/agent/metrics" ] } +@test "server/StatefulSet: when global.metrics.enableAgentMetrics=true, adds prometheus scheme=http annotation" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations."prometheus.io/scheme"' | tee /dev/stderr) + [ "${actual}" = "http" ] +} + +@test "server/StatefulSet: when global.metrics.enableAgentMetrics=true and global.tls.enabled=true, adds prometheus port=8501 annotation" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations."prometheus.io/port"' | tee /dev/stderr) + [ "${actual}" = "8501" ] +} + +@test "server/StatefulSet: when global.metrics.enableAgentMetrics=true and global.tls.enabled=true, adds prometheus scheme=https annotation" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.metrics.enabled=true' \ + --set 'global.metrics.enableAgentMetrics=true' \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations."prometheus.io/scheme"' | tee /dev/stderr) + [ "${actual}" = "https" ] +} + #-------------------------------------------------------------------- # config-configmap @@ -686,7 +721,7 @@ load _helpers -s templates/server-statefulset.yaml \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = 251dd23c6cc44bf8362acddc24c78440b6a65c4618785d027fae526958af5dde ] + [ "${actual}" = dc165411861bb45d37e20a0a337697336f333407f5fb29fca9252cdba652339c ] } @test "server/StatefulSet: adds config-checksum annotation when extraConfig is provided" { @@ -696,7 +731,7 @@ load _helpers --set 'server.extraConfig="{\"hello\": \"world\"}"' \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = 473d54d05b794be1526d42ef04fdc049f4f979a75d3394c897eef149d399207d ] + [ "${actual}" = d69874f33a862f6728265246a3e38b3f64702e013ecd4afc5dcdc33d34a66954 ] } @test "server/StatefulSet: adds config-checksum annotation when config is updated" { @@ -706,7 +741,7 @@ load _helpers --set 'global.acls.manageSystemACLs=true' \ . | tee /dev/stderr | yq -r '.spec.template.metadata.annotations."consul.hashicorp.com/config-checksum"' | tee /dev/stderr) - [ "${actual}" = 6acd3761c0981d4d6194b3375b0f7a291e3927602ce7857344c26010381d3a61 ] + [ "${actual}" = 95a3d3b4816a0f183b8a9aac41ff386e659cd2a465f3030f01c5c4a2b9052a6c ] } #-------------------------------------------------------------------- @@ -829,9 +864,9 @@ load _helpers } #-------------------------------------------------------------------- -# global.openshift.enabled & client.containerSecurityContext +# global.openshift.enabled && server.containerSecurityContext -@test "server/StatefulSet: container level securityContexts are not set when global.openshift.enabled=true" { +@test "server/StatefulSet: Can set container level securityContexts when global.openshift.enabled=true" { cd `chart_dir` local manifest=$(helm template \ -s templates/server-statefulset.yaml \ @@ -839,8 +874,80 @@ load _helpers --set 'server.containerSecurityContext.server.privileged=false' \ . | tee /dev/stderr) + local actual=$(echo "$manifest" | yq -r '.spec.template.spec.containers | map(select(.name == "consul")) | .[0].securityContext.privileged') + [ "${actual}" = "false" ] +} + +#-------------------------------------------------------------------- +# global.openshift.enabled + +@test "server/StatefulSet: restricted container securityContexts are set when global.openshift.enabled=true on OpenShift >= 4.11" { + cd `chart_dir` + # OpenShift 4.11 == Kube 1.24 + local manifest=$(helm template \ + --kube-version '1.24' \ + -s templates/server-statefulset.yaml \ + --set 'global.openshift.enabled=true' \ + . | tee /dev/stderr) + + local expected=$(echo '{ + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"], + "add": ["NET_BIND_SERVICE"] + }, + "runAsNonRoot": true, + "seccompProfile": { + "type": "RuntimeDefault" + } + }') + + # Check consul container local actual=$(echo "$manifest" | yq -r '.spec.template.spec.containers | map(select(.name == "consul")) | .[0].securityContext') - [ "${actual}" = "null" ] + local equal=$(jq -n --argjson a "$actual" --argjson b "$expected" '$a == $b') + [ "$equal" == "true" ] +} + +@test "server/StatefulSet: restricted container securityContexts are not set when global.openshift.enabled=true on OpenShift < 4.11" { + cd `chart_dir` + # OpenShift 4.11 == Kube 1.24 + local manifest=$(helm template \ + --kube-version '1.23' \ + -s templates/server-statefulset.yaml \ + --set 'global.openshift.enabled=true' \ + . | tee /dev/stderr) + + # Check consul container + local actual=$(echo "$manifest" | yq -r '.spec.template.spec.containers | map(select(.name == "consul")) | .[0].securityContext') + [ "$actual" == "null" ] +} + +#-------------------------------------------------------------------- +# global.openshift.enabled = false + +@test "server/StatefulSet: restricted container securityContexts are set by default" { + cd `chart_dir` + local manifest=$(helm template \ + -s templates/server-statefulset.yaml \ + . | tee /dev/stderr) + + local expected=$(echo '{ + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"], + "add": ["NET_BIND_SERVICE"] + }, + "runAsNonRoot": true, + "seccompProfile": { + "type": "RuntimeDefault" + }, + "runAsUser": 100 + }') + + # Check consul container + local actual=$(echo "$manifest" | yq -r '.spec.template.spec.containers | map(select(.name == "consul")) | .[0].securityContext') + local equal=$(jq -n --argjson a "$actual" --argjson b "$expected" '$a == $b') + [ "$equal" == "true" ] } #-------------------------------------------------------------------- @@ -2586,6 +2693,64 @@ MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ } +#-------------------------------------------------------------------- +# global.trustedCAs + +@test "server/StatefulSet: trustedCAs: if trustedCAs is set command is modified correctly" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].command[2] | contains("cat < /trusted-cas/custom-ca-0.pem")' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/StatefulSet: trustedCAs: if tustedCAs multiple are set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + --set 'global.trustedCAs[1]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0]' | tee /dev/stderr) + + + local actual=$(echo $object | jq '.command[2] | contains("cat < /trusted-cas/custom-ca-0.pem")' | tee /dev/stderr) + [ "${actual}" = "true" ] + local actual=$(echo $object | jq '.command[2] | contains("cat < /trusted-cas/custom-ca-1.pem")' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +# global.trustedCAs +@test "server/StatefulSet: trustedCAs: if trustedCAs is set /trusted-cas volumeMount is added" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | yq -r '.spec.template.spec' | tee /dev/stderr) + local actual=$(echo $object | jq -r '.volumes[] | select(.name == "trusted-cas") | .name' | tee /dev/stderr) + [ "${actual}" = "trusted-cas" ] +} + +@test "server/StatefulSet: trustedCAs: if trustedCAs is set SSL_CERT_DIR env var is set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | yq -r '.spec.template.spec.containers[0].env[] | select(.name == "SSL_CERT_DIR")' | tee /dev/stderr) + + local actual=$(echo $object | jq -r '.name' | tee /dev/stderr) + [ "${actual}" = "SSL_CERT_DIR" ] + local actual=$(echo $object | jq -r '.value' | tee /dev/stderr) + [ "${actual}" = "/etc/ssl/certs:/trusted-cas" ] +} + #-------------------------------------------------------------------- # snapshotAgent license-autoload @@ -2773,4 +2938,4 @@ MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ . | tee /dev/stderr | yq -r '.spec.template.spec.containers[1].command[2] | contains("-interval=10h34m5s")' | tee /dev/stderr) [ "${actual}" = "true" ] -} \ No newline at end of file +} diff --git a/charts/consul/test/unit/sync-catalog-deployment.bats b/charts/consul/test/unit/sync-catalog-deployment.bats index ae1fe1a854..318b4d3d3c 100755 --- a/charts/consul/test/unit/sync-catalog-deployment.bats +++ b/charts/consul/test/unit/sync-catalog-deployment.bats @@ -338,7 +338,7 @@ load _helpers --set 'syncCatalog.enabled=true' \ --set 'syncCatalog.aclSyncToken.secretKey=bar' \ . | tee /dev/stderr | - yq '[.spec.template.spec.containers[0].env[].name] | any(contains("CONSUL_HTTP_TOKEN"))' | tee /dev/stderr) + yq '[.spec.template.spec.containers[0].env[].name] | any(contains("CONSUL_ACL_TOKEN"))' | tee /dev/stderr) [ "${actual}" = "false" ] } @@ -349,7 +349,7 @@ load _helpers --set 'syncCatalog.enabled=true' \ --set 'syncCatalog.aclSyncToken.secretName=foo' \ . | tee /dev/stderr | - yq '[.spec.template.spec.containers[0].env[].name] | any(contains("CONSUL_HTTP_TOKEN"))' | tee /dev/stderr) + yq '[.spec.template.spec.containers[0].env[].name] | any(contains("CONSUL_ACL_TOKEN"))' | tee /dev/stderr) [ "${actual}" = "false" ] } @@ -361,7 +361,55 @@ load _helpers --set 'syncCatalog.aclSyncToken.secretName=foo' \ --set 'syncCatalog.aclSyncToken.secretKey=bar' \ . | tee /dev/stderr | - yq '[.spec.template.spec.containers[0].env[].name] | any(contains("CONSUL_HTTP_TOKEN"))' | tee /dev/stderr) + yq '[.spec.template.spec.containers[0].env[].name] | any(contains("CONSUL_ACL_TOKEN"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# sync ingress + +@test "syncCatalog/Deployment: enable ingress sync flag not passed when disabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.ingress.enabled=false' \ + --set 'syncCatalog.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command | any(contains("-enable-ingress=true"))' | tee /dev/stderr) + [ "${actual}" = "false" ] +} +@test "syncCatalog/Deployment: enable ingress sync flag passed when enabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.enabled=true' \ + --set 'syncCatalog.ingress.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command | any(contains("-enable-ingress=true"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "syncCatalog/Deployment: enable loadbalancer IP sync flag not passed when syncIngress disabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.enabled=true' \ + --set 'syncCatalog.ingress.enabled=false' \ + --set 'syncCatalog.ingress.loadBalancerIPs=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command | any(contains("-loadBalancer-ips=true"))' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "syncCatalog/Deployment: enable loadbalancer IP sync flag passed when enabled with ingress sync" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/sync-catalog-deployment.yaml \ + --set 'syncCatalog.enabled=true' \ + --set 'syncCatalog.ingress.enabled=true' \ + --set 'syncCatalog.ingress.loadBalancerIPs=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command | any(contains("-loadBalancer-ips=true"))' | tee /dev/stderr) [ "${actual}" = "true" ] } diff --git a/charts/consul/test/unit/telemetry-collector-configmap.bats b/charts/consul/test/unit/telemetry-collector-configmap.bats new file mode 100644 index 0000000000..c866f0d3e1 --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-configmap.bats @@ -0,0 +1,29 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/ConfigMap: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-configmap.yaml \ + . +} + +@test "telemetryCollector/ConfigMap: enabled with telemetryCollector enabled and config has content" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-configmap.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.customExporterConfig="{}"' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/ConfigMap: disabled with telemetryCollector enabled and config is empty content" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-configmap.yaml \ + --set 'telemetryCollector.enabled=true' \ + . +} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-deployment.bats b/charts/consul/test/unit/telemetry-collector-deployment.bats new file mode 100755 index 0000000000..7809039e11 --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-deployment.bats @@ -0,0 +1,1133 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/Deployment: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-deployment.yaml \ + . +} + +@test "telemetryCollector/Deployment: fails if no image is set" { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=null' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "telemetryCollector.image must be set to enable consul-telemetry-collector" ]] +} + +@test "telemetryCollector/Deployment: disable with telemetry-collector.enabled" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=false' \ + . +} + +@test "telemetryCollector/Deployment: disable with global.enabled" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'global.enabled=false' \ + . +} + +@test "telemetryCollector/Deployment: container image overrides" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].image' | tee /dev/stderr) + [ "${actual}" = "\"bar\"" ] +} + +#-------------------------------------------------------------------- +# nodeSelector + +@test "telemetryCollector/Deployment: nodeSelector is not set by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + . | tee /dev/stderr | + yq '.spec.template.spec.nodeSelector' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "telemetryCollector/Deployment: specified nodeSelector" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.nodeSelector=testing' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.nodeSelector' | tee /dev/stderr) + [ "${actual}" = "testing" ] +} + +#-------------------------------------------------------------------- +# consul.name + +@test "telemetryCollector/Deployment: name is constant regardless of consul name" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'consul.name=foobar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].name' | tee /dev/stderr) + [ "${actual}" = "consul-telemetry-collector" ] +} + +#-------------------------------------------------------------------- +# global.tls.enabled + +@test "telemetryCollector/Deployment: Adds tls-ca-cert volume when global.tls.enabled is true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.volumes[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" != "" ] +} + +@test "telemetryCollector/Deployment: Adds tls-ca-cert volumeMounts when global.tls.enabled is true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[1].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" != "" ] +} + +@test "telemetryCollector/Deployment: can overwrite CA secret with the provided one" { + cd `chart_dir` + local ca_cert_volume=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.caCert.secretName=foo-ca-cert' \ + --set 'global.tls.caCert.secretKey=key' \ + --set 'global.tls.caKey.secretName=foo-ca-key' \ + --set 'global.tls.caKey.secretKey=key' \ + . | tee /dev/stderr | + yq '.spec.template.spec.volumes[] | select(.name=="consul-ca-cert")' | tee /dev/stderr) + + # check that the provided ca cert secret is attached as a volume + local actual + actual=$(echo $ca_cert_volume | jq -r '.secret.secretName' | tee /dev/stderr) + [ "${actual}" = "foo-ca-cert" ] + + # check that the volume uses the provided secret key + actual=$(echo $ca_cert_volume | jq -r '.secret.items[0].key' | tee /dev/stderr) + [ "${actual}" = "key" ] +} + +#-------------------------------------------------------------------- +# global.tls.enableAutoEncrypt + +@test "telemetryCollector/Deployment: consul-ca-cert volumeMount is added when TLS with auto-encrypt is enabled without clients" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'client.enabled=false' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[1].volumeMounts[] | select(.name == "consul-ca-cert") | length > 0' | tee + /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: consul-ca-cert volume is not added if externalServers.enabled=true and externalServers.useSystemRoots=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.hosts[0]=foo.com' \ + --set 'externalServers.useSystemRoots=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.volumes[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" = "" ] +} + +#-------------------------------------------------------------------- +# resources + +@test "telemetryCollector/Deployment: resources has default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources' | tee /dev/stderr) + + [ $(echo "${actual}" | yq -r '.requests.memory') = "512Mi" ] + [ $(echo "${actual}" | yq -r '.requests.cpu') = "1000m" ] + [ $(echo "${actual}" | yq -r '.limits.memory') = "512Mi" ] + [ $(echo "${actual}" | yq -r '.limits.cpu') = "1000m" ] +} + +@test "telemetryCollector/Deployment: resources can be overridden" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'telemetryCollector.resources.foo=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].resources.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +#-------------------------------------------------------------------- +# init container resources + +@test "telemetryCollector/Deployment: init container has default resources" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].resources' | tee /dev/stderr) + + [ $(echo "${actual}" | yq -r '.requests.memory') = "25Mi" ] + [ $(echo "${actual}" | yq -r '.requests.cpu') = "50m" ] + [ $(echo "${actual}" | yq -r '.limits.memory') = "150Mi" ] + [ $(echo "${actual}" | yq -r '.limits.cpu') = "50m" ] +} + +@test "telemetryCollector/Deployment: init container resources can be set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'telemetryCollector.initContainer.resources.requests.memory=memory' \ + --set 'telemetryCollector.initContainer.resources.requests.cpu=cpu' \ + --set 'telemetryCollector.initContainer.resources.limits.memory=memory2' \ + --set 'telemetryCollector.initContainer.resources.limits.cpu=cpu2' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].resources' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.requests.memory' | tee /dev/stderr) + [ "${actual}" = "memory" ] + + local actual=$(echo $object | yq -r '.requests.cpu' | tee /dev/stderr) + [ "${actual}" = "cpu" ] + + local actual=$(echo $object | yq -r '.limits.memory' | tee /dev/stderr) + [ "${actual}" = "memory2" ] + + local actual=$(echo $object | yq -r '.limits.cpu' | tee /dev/stderr) + [ "${actual}" = "cpu2" ] +} + +#-------------------------------------------------------------------- +# priorityClassName + +@test "telemetryCollector/Deployment: no priorityClassName by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.priorityClassName' | tee /dev/stderr) + + [ "${actual}" = "null" ] +} + +@test "telemetryCollector/Deployment: can set a priorityClassName" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'telemetryCollector.priorityClassName=name' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.priorityClassName' | tee /dev/stderr) + + [ "${actual}" = "name" ] +} + +#-------------------------------------------------------------------- +# replicas + +@test "telemetryCollector/Deployment: replicas defaults to 1" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + . | tee /dev/stderr | + yq '.spec.replicas' | tee /dev/stderr) + + [ "${actual}" = "1" ] +} + +@test "telemetryCollector/Deployment: replicas can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'telemetryCollector.replicas=3' \ + . | tee /dev/stderr | + yq '.spec.replicas' | tee /dev/stderr) + + [ "${actual}" = "3" ] +} + +#-------------------------------------------------------------------- +# Vault + +@test "telemetryCollector/Deployment: vault CA is not configured by default" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.secretsBackend.vault.consulCARole=test' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/agent-extra-secret")') + [ "${actual}" = "false" ] + local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/ca-cert")') + [ "${actual}" = "false" ] +} + +@test "telemetryCollector/Deployment: vault CA is not configured when secretName is set but secretKey is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.secretsBackend.vault.consulCARole=test' \ + --set 'global.secretsBackend.vault.ca.secretName=ca' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/agent-extra-secret")') + [ "${actual}" = "false" ] + local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/ca-cert")') + [ "${actual}" = "false" ] +} + +@test "telemetryCollector/Deployment: vault CA is not configured when secretKey is set but secretName is not" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.secretsBackend.vault.consulCARole=test' \ + --set 'global.secretsBackend.vault.ca.secretKey=tls.crt' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/agent-extra-secret")') + [ "${actual}" = "false" ] + local actual=$(echo $object | yq -r '.metadata.annotations | has("vault.hashicorp.com/ca-cert")') + [ "${actual}" = "false" ] +} + +@test "telemetryCollector/Deployment: vault CA is configured when both secretName and secretKey are set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=test' \ + --set 'global.secretsBackend.vault.consulCARole=test' \ + --set 'global.secretsBackend.vault.ca.secretName=ca' \ + --set 'global.secretsBackend.vault.ca.secretKey=tls.crt' \ + . | tee /dev/stderr | + yq -r '.spec.template' | tee /dev/stderr) + + local actual=$(echo $object | yq -r '.metadata.annotations."vault.hashicorp.com/agent-extra-secret"') + [ "${actual}" = "ca" ] + local actual=$(echo $object | yq -r '.metadata.annotations."vault.hashicorp.com/ca-cert"') + [ "${actual}" = "/vault/custom/tls.crt" ] +} + +@test "telemetryCollector/Deployment: vault tls annotations are set when tls is enabled" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=foo' \ + --set 'global.secretsBackend.vault.consulServerRole=bar' \ + --set 'global.secretsBackend.vault.consulCARole=test' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'server.serverCert.secretName=pki_int/issue/test' \ + --set 'global.tls.caCert.secretName=pki_int/cert/ca' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata' | tee /dev/stderr) + + local actual="$(echo $cmd | + yq -r '.annotations["vault.hashicorp.com/agent-inject-template-serverca.crt"]' | tee /dev/stderr)" + local expected=$'{{- with secret \"pki_int/cert/ca\" -}}\n{{- .Data.certificate -}}\n{{- end -}}' + [ "${actual}" = "${expected}" ] + + local actual="$(echo $cmd | + yq -r '.annotations["vault.hashicorp.com/agent-inject-secret-serverca.crt"]' | tee /dev/stderr)" + [ "${actual}" = "pki_int/cert/ca" ] + + local actual="$(echo $cmd | + yq -r '.annotations["vault.hashicorp.com/agent-init-first"]' | tee /dev/stderr)" + [ "${actual}" = "true" ] + + local actual="$(echo $cmd | + yq -r '.annotations["vault.hashicorp.com/agent-inject"]' | tee /dev/stderr)" + [ "${actual}" = "true" ] + + local actual="$(echo $cmd | + yq -r '.annotations["vault.hashicorp.com/role"]' | tee /dev/stderr)" + [ "${actual}" = "test" ] +} + +@test "telemetryCollector/Deployment: vault agent annotations can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=foo' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.tls.caCert.secretName=foo' \ + --set 'global.secretsBackend.vault.enabled=true' \ + --set 'global.secretsBackend.vault.consulClientRole=test' \ + --set 'global.secretsBackend.vault.consulServerRole=foo' \ + --set 'global.secretsBackend.vault.consulCARole=test' \ + --set 'global.secretsBackend.vault.agentAnnotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +#-------------------------------------------------------------------- +# telemetryCollector.cloud + +@test "telemetryCollector/Deployment: success with all cloud bits set" { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientSecret.secretName=client-secret-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-key' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.resourceId.secretName=client-resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=client-resource-id-key' \ + . +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId is set and global.cloud.resourceId is not set or global.cloud.clientSecret.secretName is not set" { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientSecret.secretName=client-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-id-key' \ + --set 'global.cloud.resourceId.secretName=client-resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=client-resource-id-key' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "When global.cloud.enabled is true, global.cloud.resourceId.secretName, global.cloud.clientId.secretName, and global.cloud.clientSecret.secretName must also be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.enabled is true and global.cloud.clientSecret.secretName is not set but global.cloud.clientId.secretName and global.cloud.resourceId.secretName is set" { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "When global.cloud.enabled is true, global.cloud.resourceId.secretName, global.cloud.clientId.secretName, and global.cloud.clientSecret.secretName must also be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.enabled is true and global.cloud.resourceId.secretName is not set but global.cloud.clientId.secretName and global.cloud.clientSecret.secretName is set" { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "When global.cloud.enabled is true, global.cloud.resourceId.secretName, global.cloud.clientId.secretName, and global.cloud.clientSecret.secretName must also be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.resourceId.secretName is set but global.cloud.resourceId.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "When either global.cloud.resourceId.secretName or global.cloud.resourceId.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.authURL.secretName is set but global.cloud.authURL.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + --set 'global.cloud.authUrl.secretName=auth-url-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.authURL.secretKey is set but global.cloud.authURL.secretName is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + --set 'global.cloud.authUrl.secretKey=auth-url-key' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either global.cloud.authUrl.secretName or global.cloud.authUrl.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.apiHost.secretName is set but global.cloud.apiHost.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + --set 'global.cloud.apiHost.secretName=auth-url-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.apiHost.secretKey is set but global.cloud.apiHost.secretName is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + --set 'global.cloud.apiHost.secretKey=auth-url-key' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either global.cloud.apiHost.secretName or global.cloud.apiHost.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.scadaAddress.secretName is set but global.cloud.scadaAddress.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + --set 'global.cloud.scadaAddress.secretName=scada-address-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when global.cloud.scadaAddress.secretKey is set but global.cloud.scadaAddress.secretName is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.enableAutoEncrypt=true' \ + --set 'global.datacenter=dc-foo' \ + --set 'global.domain=bar' \ + --set 'global.cloud.enabled=true' \ + --set 'global.cloud.clientId.secretName=client-id-name' \ + --set 'global.cloud.clientId.secretKey=client-id-key' \ + --set 'global.cloud.clientSecret.secretName=client-secret-id-name' \ + --set 'global.cloud.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + --set 'global.cloud.resourceId.secretKey=resource-id-key' \ + --set 'global.cloud.scadaAddress.secretKey=scada-address-key' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either global.cloud.scadaAddress.secretName or global.cloud.scadaAddress.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId.secretName is set but telemetryCollector.cloud.clientId.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ + --set 'telemetryCollector.clientSecret.secretName=client-secret-id-name' \ + --set 'telemetryCollector.clientSecret.secretKey=client-secret-id-key' \ + --set 'global.resourceId.secretName=resource-id-name' \ + --set 'global.resourceId.secretKey=resource-id-key' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When telemetryCollector.cloud.clientId.secretName is set, global.cloud.resourceId.secretName, telemetryCollector.cloud.clientSecret.secretName must also be set." ]] +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId.secretKey is set but telemetryCollector.cloud.clientId.secretName is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ + --set 'telemetryCollector.cloud.clientId.secretKey=client-id-key' \ + --set 'telemetryCollector.clientSecret.secretName=client-secret-id-name' \ + --set 'global.resourceId.secretName=resource-id-name' \ + --set 'global.resourceId.secretKey=resource-id-key' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When telemetryCollector.cloud.clientId.secretName is set, global.cloud.resourceId.secretName, telemetryCollector.cloud.clientSecret.secretName must also be set." ]] +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientSecret.secretName is set but telemetryCollector.cloud.clientId.secretName is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ + --set 'telemetryCollector.cloud.clientId.secretKey=client-id-key' \ + --set 'telemetryCollector.clientSecret.secretName=client-secret-id-name' \ + --set 'telemetryCollector.clientSecret.secretKey=client-secret-key-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When telemetryCollector.cloud.clientId.secretName is set, global.cloud.resourceId.secretName, telemetryCollector.cloud.clientSecret.secretName must also be set." ]] +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId.secretName is set but telemetry.cloud.clientId.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ + --set 'telemetryCollector.cloud.clientSecret.secretName=client-secret-name' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either telemetryCollector.cloud.clientId.secretName or telemetryCollector.cloud.clientId.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientSecret.secretName is set but telemetry.cloud.clientSecret.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ + --set 'telemetryCollector.cloud.clientId.secretKey=client-id-key' \ + --set 'telemetryCollector.cloud.clientSecret.secretName=client-secret-name' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When either telemetryCollector.cloud.clientSecret.secretName or telemetryCollector.cloud.clientSecret.secretKey is defined, both must be set." ]] +} + +@test "telemetryCollector/Deployment: fails when telemetryCollector.cloud.clientId and telemetryCollector.cloud.clientSecret is set but global.cloud.resourceId.secretKey is not set." { + cd `chart_dir` + run helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.cloud.clientId.secretName=client-id-name' \ + --set 'telemetryCollector.cloud.clientId.secretKey=client-id-key' \ + --set 'telemetryCollector.cloud.clientSecret.secretName=client-secret-name' \ + --set 'telemetryCollector.cloud.clientSecret.secretKey=client-secret-key' \ + --set 'global.cloud.resourceId.secretName=resource-id-name' \ + . + [ "$status" -eq 1 ] + + [[ "$output" =~ "When telemetryCollector has clientId and clientSecret .global.cloud.resourceId.secretKey must be set" ]] +} + +#-------------------------------------------------------------------- +# global.tls.enabled + +@test "telemetryCollector/Deployment: sets -tls-disabled args when when not using TLS." { + cd `chart_dir` + + local flags=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=false' \ + . | yq -r .spec.template.spec.containers[1].args) + + local actual=$(echo $flags | yq -r '. | any(contains("-tls-disabled"))') + [ "${actual}" = 'true' ] + +} + +@test "telemetryCollector/Deployment: -ca-certs set correctly when using TLS." { + cd `chart_dir` + local flags=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[1].args' | tee /dev/stderr) + + local actual=$(echo $flags | yq -r '. | any(contains("-ca-certs=/consul/tls/ca/tls.crt"))' | tee /dev/stderr) + [ "${actual}" = 'true' ] +} + +#-------------------------------------------------------------------- +# External Server + +@test "telemetryCollector/Deployment: sets external server args when global.tls.enabled and externalServers.enabled" { + cd `chart_dir` + local flags=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.tls.enabled=true' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.httpsPort=8501' \ + --set 'externalServers.tlsServerName=foo.tls.server' \ + --set 'externalServers.useSystemRoots=true' \ + --set 'server.enabled=false' \ + --set 'client.enabled=false' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[1].args' | tee /dev/stderr) + + local actual=$(echo $flags | yq -r '. | any(contains("-ca-certs=/consul/tls/ca/tls.crt"))' | tee /dev/stderr) + [ "${actual}" = 'false' ] + + local actual=$(echo $flags | yq -r '. | any(contains("-tls-server-name=foo.tls.server"))' | tee /dev/stderr) + [ "${actual}" = 'true' ] + + local actual=$(echo $flags | jq -r '. | any(contains("-addresses=external-consul.host"))' | tee /dev/stderr) + [ "${actual}" = 'true' ] +} + +#-------------------------------------------------------------------- +# Admin Partitions + +@test "telemetryCollector/Deployment: partition flags are set when using admin partitions" { + cd `chart_dir` + local flags=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.enableConsulNamespaces=true' \ + --set 'global.adminPartitions.enabled=true' \ + --set 'global.adminPartitions.name=hashi' \ + --set 'global.acls.manageSystemACLs=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[1].args' | tee /dev/stderr) + + local actual=$(echo $flags | jq -r '. | any(contains("-login-partition=hashi"))' | tee /dev/stderr) + [ "${actual}" = 'true' ] + + local actual=$(echo $flags | jq -r '. | any(contains("-service-partition=hashi"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: consul-ca-cert volume mount is not set when using externalServers and useSystemRoots" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'server.enabled=false' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.useSystemRoots=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" = "" ] +} + +@test "telemetryCollector/Deployment: config volume mount is set when config exists" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.customExporterConfig="foo"' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].volumeMounts[] | select(.name == "config") | .name' | tee /dev/stderr) + [ "${actual}" = "config" ] +} + +@test "telemetryCollector/Deployment: config flag is set when config exists" { + cd `chart_dir` + local flags=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'telemetryCollector.customExporterConfig="foo"' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[0].command') + + local actual=$(echo $flags | yq -r '. | any(contains("-config-file-path /consul/config/config.json"))') + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: consul-ca-cert volume mount is not set on acl-init when using externalServers and useSystemRoots" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.acls.manageSystemACLs=true' \ + --set 'global.tls.enabled=true' \ + --set 'server.enabled=false' \ + --set 'externalServers.hosts[0]=external-consul.host' \ + --set 'externalServers.enabled=true' \ + --set 'externalServers.useSystemRoots=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.initContainers[1].volumeMounts[] | select(.name == "consul-ca-cert")' | tee /dev/stderr) + [ "${actual}" = "" ] +} +#-------------------------------------------------------------------- +# trustedCAs + +@test "telemetryCollector/Deployment: trustedCAs: if trustedCAs is set command is modified correctly" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].command[2] | contains("cat < /trusted-cas/custom-ca-0.pem")' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: trustedCAs: if multiple Trusted cas were set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + --set 'global.trustedCAs[1]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0]' | tee /dev/stderr) + + + local actual=$(echo $object | jq '.command[2] | contains("cat < /trusted-cas/custom-ca-0.pem")' | tee /dev/stderr) + [ "${actual}" = "true" ] + local actual=$(echo $object | jq '.command[2] | contains("cat < /trusted-cas/custom-ca-1.pem")' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: trustedCAs: if trustedCAs is set /trusted-cas volumeMount is added" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | yq -r '.spec.template.spec' | tee /dev/stderr) + local actual=$(echo $object | jq -r '.volumes[] | select(.name == "trusted-cas") | .name' | tee /dev/stderr) + [ "${actual}" = "trusted-cas" ] +} + + +@test "telemetryCollector/Deployment: trustedCAs: if trustedCAs is set SSL_CERT_DIR env var is set" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.trustedCAs[0]=-----BEGIN CERTIFICATE----- +MIICFjCCAZsCCQCdwLtdjbzlYzAKBggqhkjOPQQDAjB0MQswCQYDVQQGEwJDQTEL' \ + . | tee /dev/stderr | yq -r '.spec.template.spec.containers[0].env[] | select(.name == "SSL_CERT_DIR")' | tee /dev/stderr) + + local actual=$(echo $object | jq -r '.name' | tee /dev/stderr) + [ "${actual}" = "SSL_CERT_DIR" ] + local actual=$(echo $object | jq -r '.value' | tee /dev/stderr) + [ "${actual}" = "/etc/ssl/certs:/trusted-cas" ] +} + +#-------------------------------------------------------------------- +# extraLabels + +@test "telemetryCollector/Deployment: no extra labels defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.labels | del(."app") | del(."chart") | del(."release") | del(."component") | del(."consul.hashicorp.com/connect-inject-managed-by")' \ + | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "telemetryCollector/Deployment: extra global labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.extraLabels.foo=bar' \ + . | tee /dev/stderr) + local actualBar=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + [ "${actualBar}" = "bar" ] + local actualTemplateBar=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + [ "${actualTemplateBar}" = "bar" ] +} + +@test "telemetryCollector/Deployment: multiple global extra labels can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.image=bar' \ + --set 'global.extraLabels.foo=bar' \ + --set 'global.extraLabels.baz=qux' \ + . | tee /dev/stderr) + local actualFoo=$(echo "${actual}" | yq -r '.metadata.labels.foo' | tee /dev/stderr) + local actualBaz=$(echo "${actual}" | yq -r '.metadata.labels.baz' | tee /dev/stderr) + [ "${actualFoo}" = "bar" ] + [ "${actualBaz}" = "qux" ] + local actualTemplateFoo=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.foo' | tee /dev/stderr) + local actualTemplateBaz=$(echo "${actual}" | yq -r '.spec.template.metadata.labels.baz' | tee /dev/stderr) + [ "${actualTemplateFoo}" = "bar" ] + [ "${actualTemplateBaz}" = "qux" ] +} + +#-------------------------------------------------------------------- +# extraEnvironmentVariables + +@test "telemetryCollector/Deployment: extra environment variables" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.extraEnvironmentVars.HCP_AUTH_TLS=insecure' \ + --set 'telemetryCollector.extraEnvironmentVars.foo=bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].env' | tee /dev/stderr) + + local actual=$(echo $object | + yq -r 'map(select(.name == "HCP_AUTH_TLS")) | .[0].value' | tee /dev/stderr) + [ "${actual}" = "insecure" ] + + local actual=$(echo $object | + yq -r 'map(select(.name == "foo")) | .[0].value' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + +#-------------------------------------------------------------------- +# logLevel + +@test "telemetryCollector/Deployment: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: override global.logLevel when telemetryCollector.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.logLevel=warn' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=warn"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: use global.logLevel by default for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[1].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Deployment: override global.logLevel when telemetryCollector.logLevel is set for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/telemetry-collector-deployment.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'telemetryCollector.logLevel=debug' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[1].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-podsecuritypolicy.bats b/charts/consul/test/unit/telemetry-collector-podsecuritypolicy.bats new file mode 100644 index 0000000000..90c494eca5 --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-podsecuritypolicy.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/PodSecurityPolicy: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-podsecuritypolicy.yaml \ + . +} + +@test "telemetryCollector/PodSecurityPolicy: enabled with telemetryCollector and global.enablePodSecurityPolicies=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-podsecuritypolicy.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.enablePodSecurityPolicies=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-role.bats b/charts/consul/test/unit/telemetry-collector-role.bats new file mode 100644 index 0000000000..8fa209ef4b --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-role.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/Role: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-role.yaml \ + . +} + +@test "telemetryCollector/Role: enabled with telemetryCollector and global.enablePodSecurityPolicies=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-role.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.enablePodSecurityPolicies=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-rolebinding.bats b/charts/consul/test/unit/telemetry-collector-rolebinding.bats new file mode 100644 index 0000000000..454c2569fb --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-rolebinding.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/RoleBinding: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-rolebinding.yaml \ + . +} + +@test "telemetryCollector/RoleBinding: enabled with telemetryCollector and global.enablePodSecurityPolicies=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-rolebinding.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'global.enablePodSecurityPolicies=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-service.bats b/charts/consul/test/unit/telemetry-collector-service.bats new file mode 100755 index 0000000000..c5406b8124 --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-service.bats @@ -0,0 +1,86 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/Service: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-service.yaml \ + . +} + +@test "telemetryCollector/Service: enabled by default with telemetryCollector, connectInject enabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-service.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "telemetryCollector/Service: enabled with telemetryCollector.enabled=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-service.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# consul.name + +@test "telemetryCollector/Service: name is constant regardless of consul name" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-service.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'consul.name=foobar' \ + . | tee /dev/stderr | + yq -r '.metadata.name' | tee /dev/stderr) + [ "${actual}" = "consul-telemetry-collector" ] +} + +#-------------------------------------------------------------------- +# annotations + +@test "telemetryCollector/Service: no annotations by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-service.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations' | tee /dev/stderr) + [ "${actual}" = "null" ] +} + +@test "telemetryCollector/Service: can set annotations" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-service.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'telemetryCollector.service.annotations=key: value' \ + . | tee /dev/stderr | + yq -r '.metadata.annotations.key' | tee /dev/stderr) + [ "${actual}" = "value" ] +} + +#-------------------------------------------------------------------- +# port + +@test "telemetryCollector/Service: has default port" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-service.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.ports[0].port' | tee /dev/stderr) + [ "${actual}" = "9356" ] +} \ No newline at end of file diff --git a/charts/consul/test/unit/telemetry-collector-serviceaccount.bats b/charts/consul/test/unit/telemetry-collector-serviceaccount.bats new file mode 100644 index 0000000000..589632d5b5 --- /dev/null +++ b/charts/consul/test/unit/telemetry-collector-serviceaccount.bats @@ -0,0 +1,84 @@ +#!/usr/bin/env bats + +load _helpers + +@test "telemetryCollector/ServiceAccount: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/telemetry-collector-serviceaccount.yaml \ + . +} + +@test "telemetryCollector/ServiceAccount: enabled with telemetryCollector, connectInject enabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-serviceaccount.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# global.imagePullSecrets + +@test "telemetryCollector/ServiceAccount: can set image pull secrets" { + cd `chart_dir` + local object=$(helm template \ + -s templates/telemetry-collector-serviceaccount.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set 'global.imagePullSecrets[0].name=my-secret' \ + --set 'global.imagePullSecrets[1].name=my-secret2' \ + . | tee /dev/stderr) + + local actual=$(echo "$object" | + yq -r '.imagePullSecrets[0].name' | tee /dev/stderr) + [ "${actual}" = "my-secret" ] + + local actual=$(echo "$object" | + yq -r '.imagePullSecrets[1].name' | tee /dev/stderr) + [ "${actual}" = "my-secret2" ] +} + +#-------------------------------------------------------------------- +# telemetryCollector.serviceAccount.annotations + +@test "telemetryCollector/ServiceAccount: no annotations by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-serviceaccount.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq '.metadata.annotations | length > 0' | tee /dev/stderr) + [ "${actual}" = "false" ] +} + +@test "telemetryCollector/ServiceAccount: annotations when enabled" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-serviceaccount.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'connectInject.enabled=true' \ + --set "telemetryCollector.serviceAccount.annotations=foo: bar" \ + . | tee /dev/stderr | + yq -r '.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} + + +#-------------------------------------------------------------------- +# consul.name + +@test "telemetryCollector/ServiceAccount: name is constant regardless of consul name" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/telemetry-collector-serviceaccount.yaml \ + --set 'telemetryCollector.enabled=true' \ + --set 'consul.name=foobar' \ + . | tee /dev/stderr | + yq -r '.metadata.name' | tee /dev/stderr) + [ "${actual}" = "consul-telemetry-collector" ] +} diff --git a/charts/consul/test/unit/terminating-gateways-deployment.bats b/charts/consul/test/unit/terminating-gateways-deployment.bats index 523138a351..1dc3befbdf 100644 --- a/charts/consul/test/unit/terminating-gateways-deployment.bats +++ b/charts/consul/test/unit/terminating-gateways-deployment.bats @@ -1504,3 +1504,64 @@ key2: value2' \ [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "terminatingGateways/Deployment: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "terminatingGateways/Deployment: override global.logLevel when terminatingGateways.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'terminatingGateways.logLevel=debug' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.initContainers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "terminatingGateways/Deployment: use global.logLevel by default for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "terminatingGateways/Deployment: override global.logLevel when terminatingGateways.logLevel is set for dataplane container" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/terminating-gateways-deployment.yaml \ + --set 'terminatingGateways.enabled=true' \ + --set 'terminatingGateways.logLevel=debug' \ + --set 'connectInject.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].args' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=debug"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/charts/consul/test/unit/tls-init-cleanup-job.bats b/charts/consul/test/unit/tls-init-cleanup-job.bats index 04b4a2df31..735d991780 100644 --- a/charts/consul/test/unit/tls-init-cleanup-job.bats +++ b/charts/consul/test/unit/tls-init-cleanup-job.bats @@ -119,3 +119,43 @@ load _helpers [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# server.containerSecurityContext.tlsInit + +@test "tlsInitCleanup/Job: securityContext is set when server.containerSecurityContext.tlsInit is set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-cleanup-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'server.containerSecurityContext.tlsInit.runAsUser=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + + [ "${actual}" = "100" ] +} + + +#-------------------------------------------------------------------- +# annotations + +@test "tlsInitCleanup/Job: no annotations defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-cleanup-job.yaml \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."consul.hashicorp.com/config-checksum")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "tlsInitCleanup/Job: annotations can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-cleanup-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/charts/consul/test/unit/tls-init-job.bats b/charts/consul/test/unit/tls-init-job.bats index f9294915a5..f71edc43d5 100644 --- a/charts/consul/test/unit/tls-init-job.bats +++ b/charts/consul/test/unit/tls-init-job.bats @@ -207,3 +207,72 @@ load _helpers [ "${actualTemplateFoo}" = "bar" ] [ "${actualTemplateBaz}" = "qux" ] } + +#-------------------------------------------------------------------- +# logLevel + +@test "tlsInit/Job: use global.logLevel by default" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=info"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "tlsInit/Job: override global.logLevel when global.tls.logLevel is set" { + cd `chart_dir` + local cmd=$(helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.logLevel=error' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.containers[0].command' | tee /dev/stderr) + + local actual=$(echo "$cmd" | + yq 'any(contains("-log-level=error"))' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# server.containerSecurityContext.tlsInit + +@test "tlsInit/Job: securityContext is set when server.containerSecurityContext.tlsInit is set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'server.containerSecurityContext.tlsInit.runAsUser=100' \ + . | tee /dev/stderr | + yq -r '.spec.template.spec.securityContext.runAsUser' | tee /dev/stderr) + + [ "${actual}" = "100" ] +} + +#-------------------------------------------------------------------- +# annotations + +@test "tlsInit/Job: no annotations defined by default" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations | del(."consul.hashicorp.com/connect-inject") | del(."consul.hashicorp.com/config-checksum")' | tee /dev/stderr) + [ "${actual}" = "{}" ] +} + +@test "tlsInit/Job: annotations can be set" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/tls-init-job.yaml \ + --set 'global.tls.enabled=true' \ + --set 'global.tls.annotations=foo: bar' \ + . | tee /dev/stderr | + yq -r '.spec.template.metadata.annotations.foo' | tee /dev/stderr) + [ "${actual}" = "bar" ] +} diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 63c2a63cbf..e31cd4b09e 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -1,3 +1,6 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + # Available parameters and their default values for the Consul chart. # Holds values that affect multiple components of the chart. @@ -25,7 +28,7 @@ global: name: null # The domain Consul will answer DNS queries for - # (see `-domain` (https://www.consul.io/docs/agent/config/cli-flags#_domain)) and the domain services synced from + # (Refer to [`-domain`](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_domain)) and the domain services synced from # Consul into Kubernetes will have, e.g. `service-name.service.consul`. domain: consul @@ -63,11 +66,11 @@ global: # image: "hashicorp/consul-enterprise:1.10.0-ent" # ``` # @default: hashicorp/consul: - image: "hashicorp/consul:1.14.4" + image: docker.mirror.hashicorp.services/hashicorppreview/consul:1.15-dev # Array of objects containing image pull secret names that will be applied to each service account. # This can be used to reference image pull secrets if using a custom consul or consul-k8s-control-plane Docker image. - # See https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry for reference. + # Refer to https://kubernetes.io/docs/concepts/containers/images/#using-a-private-registry. # # Example: # @@ -77,13 +80,13 @@ global: # - name: pull-secret-name-2 # ``` # @type: array - imagePullSecrets: [ ] + imagePullSecrets: [] # The name (and tag) of the consul-k8s-control-plane Docker # image that is used for functionality such as catalog sync. # This can be overridden per component. # @default: hashicorp/consul-k8s-control-plane: - imageK8S: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.1.0-dev + imageK8S: docker.mirror.hashicorp.services/hashicorppreview/consul-k8s-control-plane:1.1.5-dev # The name of the datacenter that the agents should # register as. This can't be changed once the Consul cluster is up and running @@ -92,13 +95,14 @@ global: datacenter: dc1 # Controls whether pod security policies are created for the Consul components - # created by this chart. See https://kubernetes.io/docs/concepts/policy/pod-security-policy/. + # created by this chart. Refer to https://kubernetes.io/docs/concepts/policy/pod-security-policy/. enablePodSecurityPolicies: false # secretsBackend is used to configure Vault as the secrets backend for the Consul on Kubernetes installation. # The Vault cluster needs to have the Kubernetes Auth Method, KV2 and PKI secrets engines enabled # and have necessary secrets, policies and roles created prior to installing Consul. - # See https://www.consul.io/docs/k8s/installation/vault for full instructions. + # Refer to [Vault as the Secrets Backend](https://developer.hashicorp.com/consul/docs/k8s/deployment-configurations/vault) + # documentation for full instructions. # # The Vault cluster _must_ not have the Consul cluster installed by this Helm chart as its storage backend # as that would cause a circular dependency. @@ -198,8 +202,8 @@ global: # The provider will be configured to use the Vault Kubernetes auth method # and therefore requires the role provided by `global.secretsBackend.vault.consulServerRole` # to have permissions to the root and intermediate PKI paths. - # Please see https://www.consul.io/docs/connect/ca/vault#vault-acl-policies - # for information on how to configure the Vault policies. + # Please refer to [Vault ACL policies](https://developer.hashicorp.com/consul/docs/connect/ca/vault#vault-acl-policies) + # documentation for information on how to configure the Vault policies. connectCA: # The address of the Vault server. address: "" @@ -208,15 +212,15 @@ global: authMethodPath: "kubernetes" # The path to a PKI secrets engine for the root certificate. - # For more details, please refer to [Vault Connect CA configuration](https://www.consul.io/docs/connect/ca/vault#rootpkipath). + # For more details, please refer to [Vault Connect CA configuration](https://developer.hashicorp.com/consul/docs/connect/ca/vault#rootpkipath). rootPKIPath: "" # The path to a PKI secrets engine for the generated intermediate certificate. - # For more details, please refer to [Vault Connect CA configuration](https://www.consul.io/docs/connect/ca/vault#intermediatepkipath). + # For more details, please refer to [Vault Connect CA configuration](https://developer.hashicorp.com/consul/docs/connect/ca/vault#intermediatepkipath). intermediatePKIPath: "" # Additional Connect CA configuration in JSON format. - # Please refer to [Vault Connect CA configuration](https://www.consul.io/docs/connect/ca/vault#configuration) + # Please refer to [Vault Connect CA configuration](https://developer.hashicorp.com/consul/docs/connect/ca/vault#configuration) # for all configuration options available for that provider. # # Example: @@ -255,7 +259,7 @@ global: secretName: null # Configures Consul's gossip encryption key. - # (see `-encrypt` (https://www.consul.io/docs/agent/config/cli-flags#_encrypt)). + # (Refer to [`-encrypt`](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_encrypt)). # By default, gossip encryption is not enabled. The gossip encryption key may be set automatically or manually. # The recommended method is to automatically generate the key. # To automatically generate and set a gossip encryption key, set autoGenerate to true. @@ -263,7 +267,7 @@ global: # To manually generate a gossip encryption key, set secretName and secretKey and use Consul to generate # a key, saving this as a Kubernetes secret or Vault secret path and key. # If `global.secretsBackend.vault.enabled=true`, be sure to add the "data" component of the secretName path as required by - # the Vault KV-2 secrets engine [see example]. + # the Vault KV-2 secrets engine [refer to example]. # # ```shell-session # $ kubectl create secret generic consul-gossip-encryption-key --from-literal=key=$(consul keygen) @@ -285,15 +289,18 @@ global: # The key within the Kubernetes secret or Vault secret key that holds the gossip # encryption key. secretKey: "" + # Override global log verbosity level for `gossip-encryption-autogenerate-job` pods. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" # A list of addresses of upstream DNS servers that are used to recursively resolve DNS queries. # These values are given as `-recursor` flags to Consul servers and clients. - # See https://www.consul.io/docs/agent/config/cli-flags#_recursor for more details. + # Refer to [`-recursor`](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_recursor) for more details. # If this is an empty array (the default), then Consul DNS will only resolve queries for the Consul top level domain (by default `.consul`). # @type: array - recursors: [ ] + recursors: [] - # Enables TLS (https://learn.hashicorp.com/tutorials/consul/tls-encryption-secure) + # Enables [TLS](https://developer.hashicorp.com/consul/tutorials/security/tls-encryption-secure) # across the cluster to verify authenticity of the Consul servers and clients. # Requires Consul v1.4.1+. tls: @@ -303,6 +310,10 @@ global: # This setting is required for [Cluster Peering](https://developer.hashicorp.com/consul/docs/connect/cluster-peering/k8s). enabled: false + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # If true, turns on the auto-encrypt feature on clients and servers. # It also switches consul-k8s-control-plane components to retrieve the CA from the servers # via the API. Requires Consul 1.7.1+. @@ -312,18 +323,18 @@ global: # in the server certificate. This is useful when you need to access the # Consul server(s) externally, for example, if you're using the UI. # @type: array - serverAdditionalDNSSANs: [ ] + serverAdditionalDNSSANs: [] # A list of additional IP addresses to set as Subject Alternative Names (SANs) # in the server certificate. This is useful when you need to access the # Consul server(s) externally, for example, if you're using the UI. # @type: array - serverAdditionalIPSANs: [ ] + serverAdditionalIPSANs: [] # If true, `verify_outgoing`, `verify_server_hostname`, # and `verify_incoming` for internal RPC communication will be set to `true` for Consul servers and clients. # Set this to false to incrementally roll out TLS on an existing Consul cluster. - # Please see https://consul.io/docs/k8s/operations/tls-on-existing-cluster + # Please refer to [TLS on existing clusters](https://developer.hashicorp.com/consul/docs/k8s/operations/tls-on-existing-cluster) # for more details. verify: true @@ -364,8 +375,9 @@ global: # # Note that we need the CA key so that we can generate server and client certificates. # It is particularly important for the client certificates since they need to have host IPs - # as Subject Alternative Names. In the future, we may support bringing your own server - # certificates. + # as Subject Alternative Names. If you are setting server certs yourself via `server.serverCert` + # and you are not enabling clients (or clients are enabled with autoEncrypt) then you do not + # need to provide the CA key. caKey: # The name of the Kubernetes or Vault secret that holds the CA key. # @type: string @@ -374,6 +386,18 @@ global: # @type: string secretKey: null + # This value defines additional annotations for + # tls init jobs. Format this value as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + # [Enterprise Only] `enableConsulNamespaces` indicates that you are running # Consul Enterprise v1.7+ with a valid Consul Enterprise license and would # like to make use of configuration beyond registering everything into @@ -384,20 +408,29 @@ global: # Configure ACLs. acls: - # If true, the Helm chart will automatically manage ACL tokens and policies # for all Consul and consul-k8s-control-plane components. # This requires Consul >= 1.4. manageSystemACLs: false - # A Kubernetes or Vault secret containing the bootstrap token to use for - # creating policies and tokens for all Consul and consul-k8s-control-plane components. - # If set, we will skip ACL bootstrapping of the servers and will only - # initialize ACLs for the Consul clients and consul-k8s-control-plane system components. + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # A Kubernetes or Vault secret containing the bootstrap token to use for creating policies and + # tokens for all Consul and consul-k8s-control-plane components. If `secretName` and `secretKey` + # are unset, a default secret name and secret key are used. If the secret is populated, then + # we will skip ACL bootstrapping of the servers and will only initialize ACLs for the Consul + # clients and consul-k8s-control-plane system components. + # If the secret is empty, then we will bootstrap ACLs on the Consul servers, and write the + # bootstrap token to this secret. If ACLs are already bootstrapped on the servers, then the + # secret must contain the bootstrap token. bootstrapToken: # The name of the Kubernetes or Vault secret that holds the bootstrap token. + # If unset, this defaults to `{{ global.name }}-bootstrap-acl-token`. secretName: null # The key within the Kubernetes or Vault secret that holds the bootstrap token. + # If unset, this defaults to `token`. secretKey: null # If true, an ACL token will be created that can be used in secondary @@ -420,6 +453,33 @@ global: # @type: string secretKey: null + # The resource requests (CPU, memory, etc.) for the server-acl-init and server-acl-init-cleanup pods. + # This should be a YAML map corresponding to a Kubernetes + # [`ResourceRequirements``](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#resourcerequirements-v1-core) + # object. + # + # Example: + # + # ```yaml + # resources: + # requests: + # memory: '200Mi' + # cpu: '100m' + # limits: + # memory: '200Mi' + # cpu: '100m' + # ``` + # + # @recurse: false + # @type: map + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" + # partitionToken references a Vault secret containing the ACL token to be used in non-default partitions. # This value should only be provided in the default partition and only when setting # the `global.secretsBackend.vault.enabled` value to true. @@ -436,10 +496,10 @@ global: # tolerations configures the taints and tolerations for the server-acl-init # and server-acl-init-cleanup jobs. This should be a multi-line string matching the - # Tolerations (https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. + # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. tolerations: "" - # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) # labels for the server-acl-init and server-acl-init-cleanup jobs pod assignment, formatted as a multi-line string. # # Example: @@ -452,6 +512,18 @@ global: # @type: string nodeSelector: null + # This value defines additional annotations for + # acl init jobs. Format this value as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + # [Enterprise Only] This value refers to a Kubernetes or Vault secret that you have created # that contains your enterprise license. It is required if you are using an # enterprise binary. Defining it here applies it to your cluster once a leader @@ -474,8 +546,9 @@ global: # If enabled, this datacenter will be federation-capable. Only federation # via mesh gateways is supported. # Mesh gateways and servers will be configured to allow federation. - # Requires `global.tls.enabled`, `meshGateway.enabled` and `connectInject.enabled` - # to be true. Requires Consul 1.8+. + # Requires `global.tls.enabled`, `connectInject.enabled`, and one of + # `meshGateway.enabled` or `externalServers.enabled` to be true. + # Requires Consul 1.8+. enabled: false # If true, the chart will create a Kubernetes secret that can be imported @@ -491,17 +564,20 @@ global: # @type: string primaryDatacenter: null - # A list of addresses of the primary mesh gateways in the form `:`. - # (e.g. ["1.1.1.1:443", "2.3.4.5:443"] + # A list of addresses of the primary mesh gateways in the form `:` + # (e.g. `["1.1.1.1:443", "2.3.4.5:443"]`). # @type: array - primaryGateways: [ ] + primaryGateways: [] # If you are setting `global.federation.enabled` to true and are in a secondary datacenter, # set `k8sAuthMethodHost` to the address of the Kubernetes API server of the secondary datacenter. # This address must be reachable from the Consul servers in the primary datacenter. # This auth method will be used to provision ACL tokens for Consul components and is different # from the one used by the Consul Service Mesh. - # Please see the [Kubernetes Auth Method documentation](https://consul.io/docs/acl/auth-methods/kubernetes). + # Please refer to the [Kubernetes Auth Method documentation](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods/kubernetes). + # + # If `externalServers.enabled` is set to true, `global.federation.k8sAuthMethodHost` and + # `externalServers.k8sAuthMethodHost` should be set to the same value. # # You can retrieve this value from your `kubeconfig` by running: # @@ -513,6 +589,10 @@ global: # @type: string k8sAuthMethodHost: null + # Override global log verbosity level for the `create-federation-secret-job` pods. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # Configures metrics for Consul service mesh metrics: # Configures the Helm chart’s components @@ -539,10 +619,15 @@ global: # @type: boolean enableGatewayMetrics: true + # Configures the Helm chart’s components to forward envoy metrics for the Consul service mesh to the + # consul-telemetry-collector. This includes gateway metrics and sidecar metrics. + # @type: boolean + enableTelemetryCollector: false + # The name (and tag) of the consul-dataplane Docker image used for the # connect-injected sidecar proxies and mesh, terminating, and ingress gateways. # @default: hashicorp/consul-dataplane: - imageConsulDataplane: "hashicorp/consul-dataplane:1.0.1" + imageConsulDataplane: docker.mirror.hashicorp.services/hashicorppreview/consul-dataplane:1.1-dev # Configuration for running this Helm chart on the Red Hat OpenShift platform. # This Helm chart currently supports OpenShift v4.x+. @@ -635,11 +720,25 @@ global: # @type: map extraLabels: {} + # Optional PEM-encoded CA certificates that will be added to trusted system CAs. + # + # Example: + # + # ```yaml + # trustedCAs: [ + # | + # -----BEGIN CERTIFICATE----- + # MIIC7jCCApSgAwIBAgIRAIq2zQEVexqxvtxP6J0bXAwwCgYIKoZIzj0EAwIwgbkx + # ... + # ] + # ``` + # @type: array + trustedCAs: [] + # Server, when enabled, configures a server cluster to run. This should # be disabled if you plan on connecting to a Consul cluster external to # the Kube cluster. server: - # If true, the chart will install all the resources necessary for a # Consul server cluster. If you're running Consul externally and want agents # within Kubernetes to join that cluster, this should probably be false. @@ -647,13 +746,17 @@ server: # @type: boolean enabled: "-" + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # The name of the Docker image (including any tag) for the containers running # Consul server agents. # @type: string image: null # The number of server agents to run. This determines the fault tolerance of - # the cluster. Please see the deployment table (https://consul.io/docs/internals/consensus#deployment-table) + # the cluster. Please refer to the [deployment table](https://developer.hashicorp.com/consul/docs/architecture/consensus#deployment-table) # for more information. replicas: 1 @@ -695,8 +798,8 @@ server: # Vault Secrets backend: # If you are using Vault as a secrets backend, a Vault Policy must be created which allows `["create", "update"]` # capabilities on the PKI issuing endpoint, which is usually of the form `pki/issue/consul-server`. - # Please see the following guide for steps to generate a compatible certificate: - # https://learn.hashicorp.com/tutorials/consul/vault-pki-consul-secure-tls + # Complete [this tutorial](https://developer.hashicorp.com/consul/tutorials/vault-secure/vault-pki-consul-secure-tls) + # to learn how to generate a compatible certificate. # Note: when using TLS, both the `server.serverCert` and `global.tls.caCert` which points to the CA endpoint of this PKI engine # must be provided. serverCert: @@ -739,21 +842,21 @@ server: # storage classes, the PersistentVolumeClaims would need to be manually created. # A `null` value will use the Kubernetes cluster's default StorageClass. If a default # StorageClass does not exist, you will need to create one. - # Refer to the [Read/Write Tuning](https://www.consul.io/docs/install/performance#read-write-tuning) + # Refer to the [Read/Write Tuning](https://developer.hashicorp.com/consul/docs/install/performance#read-write-tuning) # section of the Server Performance Requirements documentation for considerations # around choosing a performant storage class. # - # ~> **Note:** The [Reference Architecture](https://learn.hashicorp.com/tutorials/consul/reference-architecture#hardware-sizing-for-consul-servers) + # ~> **Note:** The [Reference Architecture](https://developer.hashicorp.com/consul/tutorials/production-deploy/reference-architecture#hardware-sizing-for-consul-servers) # contains best practices and recommendations for selecting suitable # hardware sizes for your Consul servers. # @type: string storageClass: null - # This will enable/disable Connect (https://consul.io/docs/connect). Setting this to true + # This will enable/disable [service mesh](https://developer.hashicorp.com/consul/docs/connect). Setting this to true # _will not_ automatically secure pod communication, this # setting will only enable usage of the feature. Consul will automatically initialize - # a new CA and set of certificates. Additional Connect settings can be configured - # by setting the `server.extraConfig` value. + # a new CA and set of certificates. Additional service mesh settings can be configured + # by setting the `server.extraConfig` value or by applying [configuration entries](https://developer.hashicorp.com/consul/docs/connect/config-entries). connect: true serviceAccount: @@ -771,7 +874,7 @@ server: # The resource requests (CPU, memory, etc.) # for each of the server agents. This should be a YAML map corresponding to a Kubernetes - # ResourceRequirements (https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#resourcerequirements-v1-core) + # [`ResourceRequirements``](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#resourcerequirements-v1-core) # object. NOTE: The use of a YAML string is deprecated. # # Example: @@ -779,10 +882,10 @@ server: # ```yaml # resources: # requests: - # memory: '100Mi' + # memory: '200Mi' # cpu: '100m' # limits: - # memory: '100Mi' + # memory: '200Mi' # cpu: '100m' # ``` # @@ -790,10 +893,10 @@ server: # @type: map resources: requests: - memory: "100Mi" + memory: "200Mi" cpu: "100m" limits: - memory: "100Mi" + memory: "200Mi" cpu: "100m" # The security context for the server pods. This should be a YAML map corresponding to a @@ -821,15 +924,24 @@ server: # @type: map # @recurse: false server: null + # The acl-init job + # @type: map + # @recurse: false + aclInit: null + # The tls-init job + # @type: map + # @recurse: false + tlsInit: null # This value is used to carefully # control a rolling update of Consul server agents. This value specifies the - # partition (https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions) - # for performing a rolling update. Please read the linked Kubernetes documentation - # and https://www.consul.io/docs/k8s/upgrade#upgrading-consul-servers for more information. + # [partition](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions) + # for performing a rolling update. Please read the linked Kubernetes + # and [Upgrade Consul](https://developer.hashicorp.com/consul/docs/k8s/upgrade#upgrading-consul-servers) + # documentation for more information. updatePartition: 0 - # This configures the PodDisruptionBudget (https://kubernetes.io/docs/tasks/run-application/configure-pdb/) + # This configures the [`PodDisruptionBudget`](https://kubernetes.io/docs/tasks/run-application/configure-pdb/) # for the server cluster. disruptionBudget: # Enables registering a PodDisruptionBudget for the server @@ -845,7 +957,7 @@ server: # @type: integer maxUnavailable: null - # A raw string of extra JSON configuration (https://consul.io/docs/agent/options) for Consul + # A raw string of extra [JSON configuration](https://developer.hashicorp.com/consul/docs/agent/config/config-files) for Consul # servers. This will be saved as-is into a ConfigMap that is read by the Consul # server agents. This can be used to add additional configuration that # isn't directly exposed by the chart. @@ -893,7 +1005,7 @@ server: # with `-config-dir`. This defaults to false. # # @type: array - extraVolumes: [ ] + extraVolumes: [] # A list of sidecar containers. # Example: @@ -906,9 +1018,9 @@ server: # - ... # ``` # @type: array - extraContainers: [ ] + extraContainers: [] - # This value defines the affinity (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) + # This value defines the [affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) # for server pods. It defaults to allowing only a single server pod on each node, which # minimizes risk of the cluster becoming unusable if a node is lost. If you need # to run more pods per node (for example, testing on Minikube), set this value @@ -938,13 +1050,15 @@ server: topologyKey: kubernetes.io/hostname # Toleration settings for server pods. This - # should be a multi-line string matching the Tolerations - # (https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. + # should be a multi-line string matching the + # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) + # array in a Pod spec. tolerations: "" # Pod topology spread constraints for server pods. - # This should be a multi-line YAML string matching the `topologySpreadConstraints` array - # (https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) in a Pod Spec. + # This should be a multi-line YAML string matching the + # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) + # array in a Pod Spec. # # This requires K8S >= 1.18 (beta) or 1.19 (stable). # @@ -963,7 +1077,7 @@ server: # ``` topologySpreadConstraints: "" - # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) # labels for server pod assignment, formatted as a multi-line string. # # Example: @@ -977,7 +1091,7 @@ server: nodeSelector: null # This value references an existing - # Kubernetes `priorityClassName` (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) + # Kubernetes [`priorityClassName`](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) # that can be assigned to server pods. priorityClassName: "" @@ -1060,24 +1174,24 @@ server: # feature, in case kubernetes cluster is behind egress http proxies. Additionally, # it could be used to configure custom consul parameters. # @type: map - extraEnvironmentVars: { } + extraEnvironmentVars: {} - # [Enterprise Only] Values for setting up and running snapshot agents - # (https://consul.io/commands/snapshot/agent) + # [Enterprise Only] Values for setting up and running + # [snapshot agents](https://developer.hashicorp.com/consul/commands/snapshot/agent) # within the Consul clusters. They run as a sidecar with Consul servers. snapshotAgent: # If true, the chart will install resources necessary to run the snapshot agent. enabled: false # Interval at which to perform snapshots. - # See https://www.consul.io/commands/snapshot/agent#interval + # Refer to [`interval`](https://developer.hashicorp.com/consul/commands/snapshot/agent#interval) # @type: string interval: 1h # A Kubernetes or Vault secret that should be manually created to contain the entire # config to be used on the snapshot agent. # This is the preferred method of configuration since there are usually storage - # credentials present. Please see Snapshot agent config (https://consul.io/commands/snapshot/agent#config-file-options) + # credentials present. Please refer to the [Snapshot agent config](https://developer.hashicorp.com/consul/commands/snapshot/agent#config-file-options) # for details. configSecret: # The name of the Kubernetes secret or Vault secret path that holds the snapshot agent config. @@ -1112,6 +1226,60 @@ server: # @type: string caCert: null + # [Enterprise Only] Added in Consul 1.8, the audit object allow users to enable auditing + # and configure a sink and filters for their audit logs. Please refer to + # [audit logs](https://developer.hashicorp.com/consul/docs/enterprise/audit-logging) documentation + # for further information. + auditLogs: + # Controls whether Consul logs out each time a user performs an operation. + # global.acls.manageSystemACLs must be enabled to use this feature. + enabled: false + + # A single entry of the sink object provides configuration for the destination to which Consul + # will log auditing events. + # + # Example: + # + # ```yaml + # sinks: + # - name: My Sink + # type: file + # format: json + # path: /tmp/audit.json + # delivery_guarantee: best-effort + # rotate_duration: 24h + # rotate_max_files: 15 + # rotate_bytes: 25165824 + # + # ``` + # + # The sink object supports the following keys: + # + # - `name` - Name of the sink. + # + # - `type` - Type specifies what kind of sink this is. Currently only file sinks are available + # + # - `format` - Format specifies what format the events will be emitted with. Currently only `json` + # events are emitted. + # + # - `path` - The directory and filename to write audit events to. + # + # - `delivery_guarantee` - Specifies the rules governing how audit events are written. Consul + # only supports `best-effort` event delivery. + # + # - `mode` - The permissions to set on the audit log files. + # + # - `rotate_duration` - Specifies the interval by which the system rotates to a new log file. + # At least one of `rotate_duration` or `rotate_bytes` must be configured to enable audit logging. + # + # - `rotate_bytes` - Specifies how large an individual log file can grow before Consul rotates to a new file. + # At least one of rotate_bytes or rotate_duration must be configured to enable audit logging. + # + # - `rotate_max_files` - Defines the limit that Consul should follow before it deletes old log files. + # + # @type: array + sinks: [] + # Configuration for Consul servers when the servers are running outside of Kubernetes. # When running external servers, configuring these values is recommended # if setting `global.tls.enableAutoEncrypt` to true @@ -1131,7 +1299,7 @@ externalServers: # should be the same, however, they may be different if you # wish to use separate hosts for the HTTPS connections. # @type: array - hosts: [ ] + hosts: [] # The HTTPS port of the Consul servers. httpsPort: 8501 @@ -1155,7 +1323,10 @@ externalServers: # If you are setting `global.acls.manageSystemACLs` and # `connectInject.enabled` to true, set `k8sAuthMethodHost` to the address of the Kubernetes API server. # This address must be reachable from the Consul servers. - # Please see the Kubernetes Auth Method documentation (https://consul.io/docs/acl/auth-methods/kubernetes). + # Please refer to the [Kubernetes Auth Method documentation](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods/kubernetes). + # + # If `global.federation.enabled` is set to true, `global.federation.k8sAuthMethodHost` and + # `externalServers.k8sAuthMethodHost` should be set to the same value. # # You could retrieve this value from your `kubeconfig` by running: # @@ -1179,12 +1350,16 @@ client: # @type: boolean enabled: false + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # The name of the Docker image (including any tag) for the containers # running Consul client agents. # @type: string image: null - # A list of valid `-retry-join` values (https://www.consul.io/docs/agent/config/cli-flags#_retry_join). + # A list of valid [`-retry-join` values](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_retry_join). # If this is `null` (default), then the clients will attempt to automatically # join the server cluster running within Kubernetes. # This means that with `server.enabled` set to true, clients will automatically @@ -1210,7 +1385,7 @@ client: grpc: true # nodeMeta specifies an arbitrary metadata key/value pair to associate with the node - # (see https://www.consul.io/docs/agent/config/cli-flags#_node_meta) + # (refer to [`-node-meta`](https://developer.hashicorp.com/consul/docs/agent/config/cli-flags#_node_meta)) nodeMeta: pod-name: ${HOSTNAME} host-ip: ${HOST_IP} @@ -1281,7 +1456,7 @@ client: # @recurse: false tlsInit: null - # A raw string of extra JSON configuration (https://consul.io/docs/agent/options) for Consul + # A raw string of extra [JSON configuration](https://developer.hashicorp.com/consul/docs/agent/config/config-files) for Consul # clients. This will be saved as-is into a ConfigMap that is read by the Consul # client agents. This can be used to add additional configuration that # isn't directly exposed by the chart. @@ -1329,7 +1504,7 @@ client: # with `-config-dir`. This defaults to false. # # @type: array - extraVolumes: [ ] + extraVolumes: [] # A list of sidecar containers. # Example: @@ -1342,7 +1517,7 @@ client: # - ... # ``` # @type: array - extraContainers: [ ] + extraContainers: [] # Toleration Settings for Client pods # This should be a multi-line string matching the Toleration array @@ -1386,7 +1561,7 @@ client: affinity: null # This value references an existing - # Kubernetes `priorityClassName` (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) + # Kubernetes [`priorityClassName`](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) # that can be assigned to client pods. priorityClassName: "" @@ -1420,9 +1595,9 @@ client: # feature, in case kubernetes cluster is behind egress http proxies. Additionally, # it could be used to configure custom consul parameters. # @type: map - extraEnvironmentVars: { } + extraEnvironmentVars: {} - # This value defines the Pod DNS policy (https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) + # This value defines the [Pod DNS policy](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy) # for client pods to use. # @type: string dnsPolicy: null @@ -1435,7 +1610,8 @@ client: hostNetwork: false # updateStrategy for the DaemonSet. - # See https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy. + # Refer to the Kubernetes [Daemonset upgrade strategy](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy) + # documentation. # This should be a multi-line string mapping directly to the updateStrategy # # Example: @@ -1460,7 +1636,7 @@ dns: # @type: boolean enabled: "-" - # If true, services using Consul Connect will use Consul DNS + # If true, services using Consul service mesh will use Consul DNS # for default DNS resolution. The DNS lookups fall back to the nameserver IPs # listed in /etc/resolv.conf if not found in Consul. # @type: boolean @@ -1513,7 +1689,6 @@ ui: # Set the port value of the UI service. port: - # HTTP port. http: 80 @@ -1524,7 +1699,6 @@ ui: # If not set and using a NodePort service, Kubernetes will automatically assign # a port. nodePort: - # HTTP node port # @type: integer http: null @@ -1563,7 +1737,7 @@ ui: # Optionally set the ingressClassName. ingressClassName: "" - # pathType override - see: https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types + # pathType override - refer to: https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types pathType: Prefix # hosts is a list of host name to create Ingress rules. @@ -1577,7 +1751,7 @@ ui: # ``` # # @type: array - hosts: [ ] + hosts: [] # tls is a list of hosts and secret name in an Ingress # which tells the Ingress controller to secure the channel. @@ -1589,7 +1763,7 @@ ui: # secretName: testsecret-tls # ``` # @type: array - tls: [ ] + tls: [] # Annotations to apply to the UI ingress. # @@ -1609,8 +1783,8 @@ ui: # @type: boolean # @default: global.metrics.enabled enabled: "-" - # Provider for metrics. See - # https://www.consul.io/docs/agent/options#ui_config_metrics_provider + # Provider for metrics. Refer to + # [`metrics_provider`](https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_metrics_provider) # This value is only used if `ui.enabled` is set to true. # @type: string provider: "prometheus" @@ -1620,9 +1794,10 @@ ui: # @type: string baseURL: http://prometheus-server - # Corresponds to https://www.consul.io/docs/agent/options#ui_config_dashboard_url_templates configuration. + # Corresponds to [`dashboard_url_templates`](https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_dashboard_url_templates) + # configuration. dashboardURLTemplates: - # Sets https://www.consul.io/docs/agent/options#ui_config_dashboard_url_templates_service. + # Sets [`dashboardURLTemplates.service`](https://developer.hashicorp.com/consul/docs/agent/config/config-files#ui_config_dashboard_url_templates_service). service: "" # Configure the catalog sync process to sync K8S with Consul @@ -1644,8 +1819,8 @@ syncCatalog: image: null # If true, all valid services in K8S are - # synced by default. If false, the service must be annotated - # (https://consul.io/docs/k8s/service-sync#sync-enable-disable) properly to sync. + # synced by default. If false, the service must be [annotated](https://developer.hashicorp.com/consul/docs/k8s/service-sync#enable-and-disable-sync) + # properly to sync. # In either case an annotation can override the default. default: true @@ -1679,7 +1854,7 @@ syncCatalog: # # Note: `k8sDenyNamespaces` takes precedence over values defined here. # @type: array - k8sAllowNamespaces: [ "*" ] + k8sAllowNamespaces: ["*"] # List of k8s namespaces that should not have their # services synced. This list takes precedence over `k8sAllowNamespaces`. @@ -1689,7 +1864,7 @@ syncCatalog: # `["namespace1", "namespace2"]`, then all k8s namespaces besides `namespace1` # and `namespace2` will be synced. # @type: array - k8sDenyNamespaces: [ "kube-system", "kube-public" ] + k8sDenyNamespaces: ["kube-system", "kube-public"] # [DEPRECATED] Use k8sAllowNamespaces and k8sDenyNamespaces instead. For # backwards compatibility, if both this and the allow/deny lists are set, @@ -1764,6 +1939,19 @@ syncCatalog: # Set this to false to skip syncing ClusterIP services. syncClusterIPServices: true + ingress: + # Syncs the hostname from a Kubernetes Ingress resource to service registrations + # when a rule matched a service. Currently only supports host based routing and + # not path based routing. The only supported path on an ingress rule is "/". + # Set this to false to skip syncing Ingress services. + # + # Currently, port 80 is synced if there is not TLS entry for the hostname. Syncs the port + # 443 if there is a TLS entry that matches the hostname. + enabled: false + # Requires syncIngress to be `true`. syncs the LoadBalancer IP from a Kubernetes Ingress + # resource instead of the hostname to service registrations when a rule matched a service. + loadBalancerIPs: false + # Configures the type of syncing that happens for NodePort # services. The valid options are: ExternalOnly, InternalOnly, ExternalFirst. # @@ -1784,7 +1972,7 @@ syncCatalog: # @type: string secretKey: null - # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) # labels for catalog sync pod assignment, formatted as a multi-line string. # # Example: @@ -1880,7 +2068,7 @@ connectInject: # If true, the injector will inject the # Connect sidecar into all pods by default. Otherwise, pods must specify the - # injection annotation (https://consul.io/docs/k8s/connect#consul-hashicorp-com-connect-inject) + # [injection annotation](https://developer.hashicorp.com/consul/docs/k8s/connect#consul-hashicorp-com-connect-inject) # to opt-in to Connect injection. If this is true, pods can use the same annotation # to explicitly opt-out of injection. default: false @@ -1901,7 +2089,7 @@ connectInject: # Note: This value has no effect if transparent proxy is disabled on the pod. defaultOverwriteProbes: true - # This configures the PodDisruptionBudget (https://kubernetes.io/docs/tasks/run-application/configure-pdb/) + # This configures the [`PodDisruptionBudget`](https://kubernetes.io/docs/tasks/run-application/configure-pdb/) # for the service mesh sidecar injector. disruptionBudget: # This will enable/disable registering a PodDisruptionBudget for the @@ -1934,7 +2122,7 @@ connectInject: logLevel: null # Set the namespace to install the CNI plugin into. Overrides global namespace settings for CNI resources. - # Ex: "kube-system" + # Ex: "kube-system" # @type: string namespace: null @@ -1992,7 +2180,8 @@ connectInject: runAsUser: 0 # updateStrategy for the CNI installer DaemonSet. - # See https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy. + # Refer to the Kubernetes [Daemonset upgrade strategy](https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/#daemonset-update-strategy) + # documentation. # This should be a multi-line string mapping directly to the updateStrategy # # Example: @@ -2021,8 +2210,7 @@ connectInject: # @type: map meta: null - - # Configures metrics for Consul Connect services. All values are overridable + # Configures metrics for Consul service mesh services. All values are overridable # via annotations on a per-pod basis. metrics: # If true, the connect-injector will automatically @@ -2116,15 +2304,22 @@ connectInject: # @type: string annotations: null - # The resource settings for connect inject pods. - # @recurse: false + # The resource settings for connect inject pods. The defaults, are optimized for getting started worklows on developer deployments. The settings should be tweaked for production deployments. # @type: map resources: requests: - memory: "50Mi" + # Recommended production default: 500Mi + # @type: string + memory: "200Mi" + # Recommended production default: 250m + # @type: string cpu: "50m" limits: - memory: "50Mi" + # Recommended production default: 500Mi + # @type: string + memory: "200Mi" + # Recommended production default: 250m + # @type: string cpu: "50m" # Sets the failurePolicy for the mutating webhook. By default this will cause pods not part of the consul installation to fail scheduling while the webhook @@ -2137,12 +2332,12 @@ connectInject: # Selector for restricting the webhook to only specific namespaces. # Use with `connectInject.default: true` to automatically inject all pods in namespaces that match the selector. This should be set to a multiline string. - # See https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector + # Refer to https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#matching-requests-namespaceselector # for more details. # # By default, we exclude kube-system since usually users won't # want those pods injected and local-path-storage and openebs so that - # Kind (Kubernetes In Docker) and OpenEBS (https://openebs.io/) respectively can provision Pods used to create PVCs. + # Kind (Kubernetes In Docker) and [OpenEBS](https://openebs.io/) respectively can provision Pods used to create PVCs. # Note that this exclusion is only supported in Kubernetes v1.21.1+. # # Example: @@ -2165,7 +2360,7 @@ connectInject: # annotated. Use `["*"]` to automatically allow all k8s namespaces. # # For example, `["namespace1", "namespace2"]` will only allow pods in the k8s - # namespaces `namespace1` and `namespace2` to have Connect sidecars injected + # namespaces `namespace1` and `namespace2` to have Consul service mesh sidecars injected # and registered with Consul. All other k8s namespaces will be ignored. # # To deny all namespaces, set this to `[]`. @@ -2174,7 +2369,7 @@ connectInject: # `namespaceSelector` takes precedence over both since it is applied first. # `kube-system` and `kube-public` are never injected, even if included here. # @type: array - k8sAllowNamespaces: [ "*" ] + k8sAllowNamespaces: ["*"] # List of k8s namespaces that should not allow Connect # sidecar injection. This list takes precedence over `k8sAllowNamespaces`. @@ -2187,7 +2382,7 @@ connectInject: # Note: `namespaceSelector` takes precedence over this since it is applied first. # `kube-system` and `kube-public` are never injected. # @type: array - k8sDenyNamespaces: [ ] + k8sDenyNamespaces: [] # [Enterprise Only] These settings manage the connect injector's interaction with # Consul namespaces (requires consul-ent v1.7+). @@ -2243,8 +2438,8 @@ connectInject: # If set to an empty string all service accounts can log in. # This only has effect if ACLs are enabled. # - # See https://www.consul.io/docs/acl/acl-auth-methods.html#binding-rules - # and https://www.consul.io/docs/acl/auth-methods/kubernetes.html#trusted-identity-attributes + # Refer to Auth methods [Binding rules](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods#binding-rules) + # and [Trusted identiy attributes](https://developer.hashicorp.com/consul/docs/security/acl/auth-methods/kubernetes#trusted-identity-attributes) # for more details. # Requires Consul >= v1.5. aclBindingRuleSelector: "serviceaccount.name!=default" @@ -2274,7 +2469,7 @@ connectInject: # leads to unnecessary thread and memory usage and leaves unnecessary idle connections open. It is # advised to keep this number low for sidecars and high for edge proxies. # This will control the `--concurrency` flag to Envoy. - # For additional information see also: https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310 + # For additional information, refer to https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310 # # This setting can be overridden on a per-pod basis via this annotation: # - `consul.hashicorp.com/consul-envoy-proxy-concurrency` @@ -2292,40 +2487,73 @@ connectInject: # @type: map resources: requests: - # Recommended default: 100Mi + # Recommended production default: 100Mi # @type: string memory: null - # Recommended default: 100m + # Recommended production default: 100m # @type: string cpu: null limits: - # Recommended default: 100Mi + # Recommended production default: 100Mi # @type: string memory: null - # Recommended default: 100m + # Recommended production default: 100m # @type: string cpu: null + # Set default lifecycle management configuration for sidecar proxy. + # These settings can be overridden on a per-pod basis via these annotations: + # + # - `consul.hashicorp.com/enable-sidecar-proxy-lifecycle` + # - `consul.hashicorp.com/enable-sidecar-proxy-shutdown-drain-listeners` + # - `consul.hashicorp.com/sidecar-proxy-lifecycle-shutdown-grace-period-seconds` + # - `consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-port` + # - `consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-shutdown-path` + # @type: map + lifecycle: + # @type: boolean + defaultEnabled: true + # @type: boolean + defaultEnableShutdownDrainListeners: true + # @type: integer + defaultShutdownGracePeriodSeconds: 30 + # @type: integer + defaultGracefulPort: 20600 + # @type: string + defaultGracefulShutdownPath: "/graceful_shutdown" - # The resource settings for the Connect injected init container. - # @recurse: false + # The resource settings for the Connect injected init container. If null, the resources + # won't be set for the initContainer. The defaults are optimized for developer instances of + # Kubernetes, however they should be tweaked with the recommended defaults as shown below to speed up service registration times. # @type: map initContainer: resources: requests: + # Recommended production default: 150Mi + # @type: string memory: "25Mi" + # Recommended production default: 250m + # @type: string cpu: "50m" limits: + # Recommended production default: 150Mi + # @type: string memory: "150Mi" - cpu: "50m" + # Recommended production default: 500m + # @type: string + cpu: null # [Mesh Gateways](https://developer.hashicorp.com/consul/docs/connect/gateways/mesh-gateway) enable Consul Connect to work across Consul datacenters. meshGateway: # If [mesh gateways](https://developer.hashicorp.com/consul/docs/connect/gateways/mesh-gateway) are enabled, a Deployment will be created that runs - # gateways and Consul Connect will be configured to use gateways. + # gateways and Consul service mesh will be configured to use gateways. # This setting is required for [Cluster Peering](https://developer.hashicorp.com/consul/docs/connect/cluster-peering/k8s). # Requirements: consul 1.6.0+ if using `global.acls.manageSystemACLs``. enabled: false + # Override global log verbosity level for `mesh-gateway-deployment` pods. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # Number of replicas for the Deployment. replicas: 1 @@ -2359,7 +2587,7 @@ meshGateway: # Port that gets registered for WAN traffic. # If source is set to "Service" then this setting will have no effect. - # See the documentation for source as to which port will be used in that + # Refer to the documentation for source as to which port will be used in that # case. port: 443 @@ -2462,7 +2690,7 @@ meshGateway: memory: "50Mi" cpu: "50m" - # This value defines the affinity (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) + # This value defines the [affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) # for mesh gateway pods. It defaults to `null` thereby allowing multiple gateway pods on each node. But if one would prefer # a mode which minimizes risk of the cluster becoming unusable if a node is lost, set this value # to the value in the example below. @@ -2488,8 +2716,9 @@ meshGateway: tolerations: null # Pod topology spread constraints for mesh gateway pods. - # This should be a multi-line YAML string matching the `topologySpreadConstraints` array - # (https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) in a Pod Spec. + # This should be a multi-line YAML string matching the + # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) + # array in a Pod Spec. # # This requires K8S >= 1.18 (beta) or 1.19 (stable). # @@ -2538,6 +2767,10 @@ ingressGateways: # and `client.enabled=true`. enabled: false + # Override global log verbosity level for `ingress-gateways-deployment` pods. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # Defaults sets default values for all gateway fields. With the exception # of annotations, defining any of these values in the `gateways` list # will override the default values provided here. Annotations will @@ -2563,10 +2796,10 @@ ingressGateways: # @default: [{port: 8080, port: 8443}] # @recurse: false ports: - - port: 8080 - nodePort: null - - port: 8443 - nodePort: null + - port: 8080 + nodePort: null + - port: 8443 + nodePort: null # Annotations to apply to the ingress gateway service. Annotations defined # here will be applied to all ingress gateway services in addition to any @@ -2609,7 +2842,7 @@ ingressGateways: memory: "100Mi" cpu: "100m" - # This value defines the affinity (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) + # This value defines the [affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) # for ingress gateway pods. It defaults to `null` thereby allowing multiple gateway pods on each node. But if one would prefer # a mode which minimizes risk of the cluster becoming unusable if a node is lost, set this value # to the value in the example below. @@ -2635,8 +2868,9 @@ ingressGateways: tolerations: null # Pod topology spread constraints for ingress gateway pods. - # This should be a multi-line YAML string matching the `topologySpreadConstraints` array - # (https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) in a Pod Spec. + # This should be a multi-line YAML string matching the + # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) + # array in a Pod Spec. # # This requires K8S >= 1.18 (beta) or 1.19 (stable). # @@ -2690,7 +2924,7 @@ ingressGateways: # case of annotations where both will be applied. # @type: array gateways: - - name: ingress-gateway + - name: ingress-gateway # Configuration options for terminating gateways. Default values for all # terminating gateways are defined in `terminatingGateways.defaults`. Any of @@ -2704,6 +2938,10 @@ terminatingGateways: # and `client.enabled=true`. enabled: false + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + # Defaults sets default values for all gateway fields. With the exception # of annotations, defining any of these values in the `gateways` list # will override the default values provided here. Annotations will @@ -2726,7 +2964,7 @@ terminatingGateways: # path: path # secret will now mount to /consul/userconfig/my-secret/path # ``` # @type: array - extraVolumes: [ ] + extraVolumes: [] # Resource limits for all terminating gateway pods # @recurse: false @@ -2739,7 +2977,7 @@ terminatingGateways: memory: "100Mi" cpu: "100m" - # This value defines the affinity (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) + # This value defines the [affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) # for terminating gateway pods. It defaults to `null` thereby allowing multiple gateway pods on each node. But if one would prefer # a mode which minimizes risk of the cluster becoming unusable if a node is lost, set this value # to the value in the example below. @@ -2765,8 +3003,9 @@ terminatingGateways: tolerations: null # Pod topology spread constraints for terminating gateway pods. - # This should be a multi-line YAML string matching the `topologySpreadConstraints` array - # (https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) in a Pod Spec. + # This should be a multi-line YAML string matching the + # [`topologySpreadConstraints`](https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/) + # array in a Pod Spec. # # This requires K8S >= 1.18 (beta) or 1.19 (stable). # @@ -2831,7 +3070,7 @@ terminatingGateways: # case of annotations where both will be applied. # @type: array gateways: - - name: terminating-gateway + - name: terminating-gateway # Configuration settings for the Consul API Gateway integration apiGateway: @@ -2847,7 +3086,7 @@ apiGateway: # The name (and tag) of the Envoy Docker image used for the # apiGateway. For other Consul compoenents, imageEnvoy has been replaced with Consul Dataplane. # @default: envoyproxy/envoy: - imageEnvoy: "envoyproxy/envoy:v1.23.1" + imageEnvoy: "envoyproxy/envoy:v1.25.9" # Override global log verbosity level for api-gateway-controller pods. One of "debug", "info", "warn", or "error". # @type: string @@ -2858,7 +3097,7 @@ apiGateway: # When true a GatewayClass is configured to automatically work with Consul as installed by helm. enabled: true - # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) # labels for gateway pod assignment, formatted as a multi-line string. # # Example: @@ -2871,7 +3110,7 @@ apiGateway: # @type: string nodeSelector: null - # Toleration settings for gateway pods created with the managed gateway class. + # Toleration settings for gateway pods created with the managed gateway class. # This should be a multi-line string matching the # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. # @@ -2893,7 +3132,7 @@ apiGateway: # ```yaml # service: # annotations: | - # - external-dns.alpha.kubernetes.io/hostname + # - external-dns.alpha.kubernetes.io/hostname # ``` # # @type: string @@ -2943,11 +3182,11 @@ apiGateway: annotations: null # This value references an existing - # Kubernetes `priorityClassName` (https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) + # Kubernetes [`priorityClassName`](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#pod-priority) # that can be assigned to api-gateway-controller pods. priorityClassName: "" - # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) # labels for api-gateway-controller pod assignment, formatted as a multi-line string. # # Example: @@ -2961,7 +3200,7 @@ apiGateway: nodeSelector: null # This value defines the tolerations for api-gateway-controller pod, this should be a multi-line string matching the - # Tolerations (https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. + # [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) array in a Pod spec. # # @type: string tolerations: null @@ -3004,14 +3243,13 @@ apiGateway: # Configuration settings for the webhook-cert-manager # `webhook-cert-manager` ensures that cert bundles are up to date for the mutating webhook. webhookCertManager: - # Toleration Settings # This should be a multi-line string matching the Toleration array # in a PodSpec. # @type: string tolerations: null - # This value defines `nodeSelector` (https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) + # This value defines [`nodeSelector`](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector) # labels for the webhook-cert-manager pod assignment, formatted as a multi-line string. # # Example: @@ -3035,3 +3273,97 @@ prometheus: # is only useful when running helm template. tests: enabled: true + +telemetryCollector: + # Enables the consul-telemetry-collector deployment + # @type: boolean + enabled: false + + # Override global log verbosity level. One of "trace", "debug", "info", "warn", or "error". + # @type: string + logLevel: "" + + # The name of the Docker image (including any tag) for the containers running + # the consul-telemetry-collector + # @type: string + image: "hashicorp/consul-telemetry-collector:0.0.1" + + # The resource settings for consul-telemetry-collector pods. + # @recurse: false + # @type: map + resources: + requests: + memory: "512Mi" + cpu: "1000m" + limits: + memory: "512Mi" + cpu: "1000m" + + # This value sets the number of consul-telemetry-collector replicas to deploy. + replicas: 1 + + # This value defines additional configuration for the telemetry collector. It should be formatted as a multi-line + # json blob string + # + # ```yaml + # customExporterConfig: | + # {"http_collector_endpoint": "other-otel-collector"} + # ``` + # + # @type: string + customExporterConfig: null + + service: + # This value defines additional annotations for the server service account. This should be formatted as a multi-line + # string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + serviceAccount: + # This value defines additional annotations for the telemetry-collector's service account. This should be formatted + # as a multi-line string. + # + # ```yaml + # annotations: | + # "sample/annotation1": "foo" + # "sample/annotation2": "bar" + # ``` + # + # @type: string + annotations: null + + cloud: + clientId: + secretName: null + secretKey: null + clientSecret: + secretName: null + secretKey: null + + initContainer: + # The resource settings for consul-telemetry-collector initContainer. + # @recurse: false + # @type: map + resources: {} + + # Optional YAML string to specify a nodeSelector config. + # @type: string + nodeSelector: null + + # Optional priorityClassName. + # @type: string + priorityClassName: "" + + # A list of extra environment variables to set within the stateful set. + # These could be used to include proxy settings required for cloud auto-join + # feature, in case kubernetes cluster is behind egress http proxies. Additionally, + # it could be used to configure custom consul parameters. + # @type: map + extraEnvironmentVars: {} diff --git a/charts/demo/templates/intentions.yaml b/charts/demo/templates/intentions.yaml index e0a0a0a5b1..f6f71d7911 100644 --- a/charts/demo/templates/intentions.yaml +++ b/charts/demo/templates/intentions.yaml @@ -55,6 +55,17 @@ spec: --- apiVersion: consul.hashicorp.com/v1alpha1 kind: ServiceIntentions +metadata: + name: consul-telemetry-collector +spec: + destination: + name: 'consul-telemetry-collector' + sources: + - name: '*' + action: allow +--- +apiVersion: consul.hashicorp.com/v1alpha1 +kind: ServiceIntentions metadata: name: deny-all spec: diff --git a/charts/go.mod b/charts/go.mod index cdb23e46b0..f76282d756 100644 --- a/charts/go.mod +++ b/charts/go.mod @@ -1,3 +1,3 @@ module github.com/hashicorp/consul-k8s/charts -go 1.19 +go 1.20 diff --git a/cli/cmd/config/command.go b/cli/cmd/config/command.go new file mode 100644 index 0000000000..5e44677ff6 --- /dev/null +++ b/cli/cmd/config/command.go @@ -0,0 +1,26 @@ +package config + +import ( + "fmt" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/mitchellh/cli" +) + +// ConfigCommand provides a synopsis for the config subcommands (e.g. read). +type ConfigCommand struct { + *common.BaseCommand +} + +// Run prints out information about the subcommands. +func (c *ConfigCommand) Run([]string) int { + return cli.RunResultHelp +} + +func (c *ConfigCommand) Help() string { + return fmt.Sprintf("%s\n\nUsage: consul-k8s config ", c.Synopsis()) +} + +func (c *ConfigCommand) Synopsis() string { + return "Operate on configuration" +} diff --git a/cli/cmd/config/read/command.go b/cli/cmd/config/read/command.go new file mode 100644 index 0000000000..e2258bd013 --- /dev/null +++ b/cli/cmd/config/read/command.go @@ -0,0 +1,199 @@ +package read + +import ( + "errors" + "fmt" + "sync" + + "github.com/posener/complete" + + "github.com/hashicorp/consul-k8s/cli/common" + "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/hashicorp/consul-k8s/cli/helm" + "helm.sh/helm/v3/pkg/action" + helmCLI "helm.sh/helm/v3/pkg/cli" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/yaml" +) + +const ( + flagNameKubeConfig = "kubeconfig" + flagNameKubeContext = "context" +) + +type ReadCommand struct { + *common.BaseCommand + + helmActionsRunner helm.HelmActionsRunner + + kubernetes kubernetes.Interface + + set *flag.Sets + + flagKubeConfig string + flagKubeContext string + + once sync.Once + help string +} + +func (c *ReadCommand) init() { + c.set = flag.NewSets() + + f := c.set.NewSet("Global Options") + f.StringVar(&flag.StringVar{ + Name: "kubeconfig", + Aliases: []string{"c"}, + Target: &c.flagKubeConfig, + Default: "", + Usage: "Path to kubeconfig file.", + }) + f.StringVar(&flag.StringVar{ + Name: "context", + Target: &c.flagKubeContext, + Default: "", + Usage: "Kubernetes context to use.", + }) + + c.help = c.set.Help() +} + +// Run checks the status of a Consul installation on Kubernetes. +func (c *ReadCommand) Run(args []string) int { + c.once.Do(c.init) + if c.helmActionsRunner == nil { + c.helmActionsRunner = &helm.ActionRunner{} + } + + c.Log.ResetNamed("config read") + defer common.CloseWithError(c.BaseCommand) + + if err := c.set.Parse(args); err != nil { + c.UI.Output(err.Error()) + return 1 + } + + if err := c.validateFlags(); err != nil { + c.UI.Output(err.Error()) + return 1 + } + + // helmCLI.New() will create a settings object which is used by the Helm Go SDK calls. + settings := helmCLI.New() + if c.flagKubeConfig != "" { + settings.KubeConfig = c.flagKubeConfig + } + if c.flagKubeContext != "" { + settings.KubeContext = c.flagKubeContext + } + + if err := c.setupKubeClient(settings); err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + // Setup logger to stream Helm library logs. + var uiLogger = func(s string, args ...interface{}) { + logMsg := fmt.Sprintf(s, args...) + c.UI.Output(logMsg, terminal.WithLibraryStyle()) + } + + _, releaseName, namespace, err := c.helmActionsRunner.CheckForInstallations(&helm.CheckForInstallationsOptions{ + Settings: settings, + ReleaseName: common.DefaultReleaseName, + DebugLog: uiLogger, + }) + if err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + if err := c.checkHelmInstallation(settings, uiLogger, releaseName, namespace); err != nil { + c.UI.Output(err.Error(), terminal.WithErrorStyle()) + return 1 + } + + return 0 +} + +// validateFlags checks the command line flags and values for errors. +func (c *ReadCommand) validateFlags() error { + if len(c.set.Args()) > 0 { + return errors.New("should have no non-flag arguments") + } + return nil +} + +// AutocompleteFlags returns a mapping of supported flags and autocomplete +// options for this command. The map key for the Flags map should be the +// complete flag such as "-foo" or "--foo". +func (c *ReadCommand) AutocompleteFlags() complete.Flags { + return complete.Flags{ + fmt.Sprintf("-%s", flagNameKubeConfig): complete.PredictFiles("*"), + fmt.Sprintf("-%s", flagNameKubeContext): complete.PredictNothing, + } +} + +// AutocompleteArgs returns the argument predictor for this command. +// Since argument completion is not supported, this will return +// complete.PredictNothing. +func (c *ReadCommand) AutocompleteArgs() complete.Predictor { + return complete.PredictNothing +} + +// checkHelmInstallation uses the helm Go SDK to depict the status of a named release. This function then prints +// the version of the release, it's status (unknown, deployed, uninstalled, ...), and the overwritten values. +func (c *ReadCommand) checkHelmInstallation(settings *helmCLI.EnvSettings, uiLogger action.DebugLog, releaseName, namespace string) error { + // Need a specific action config to call helm status, where namespace comes from the previous call to list. + statusConfig := new(action.Configuration) + statusConfig, err := helm.InitActionConfig(statusConfig, namespace, settings, uiLogger) + if err != nil { + return err + } + + statuser := action.NewStatus(statusConfig) + rel, err := c.helmActionsRunner.GetStatus(statuser, releaseName) + if err != nil { + return fmt.Errorf("couldn't check for installations: %s", err) + } + + valuesYaml, err := yaml.Marshal(rel.Config) + if err != nil { + return err + } + c.UI.Output(string(valuesYaml)) + + return nil +} + +// setupKubeClient to use for non Helm SDK calls to the Kubernetes API The Helm SDK will use +// settings.RESTClientGetter for its calls as well, so this will use a consistent method to +// target the right cluster for both Helm SDK and non Helm SDK calls. +func (c *ReadCommand) setupKubeClient(settings *helmCLI.EnvSettings) error { + if c.kubernetes == nil { + restConfig, err := settings.RESTClientGetter().ToRESTConfig() + if err != nil { + c.UI.Output("Error retrieving Kubernetes authentication: %v", err, terminal.WithErrorStyle()) + return err + } + c.kubernetes, err = kubernetes.NewForConfig(restConfig) + if err != nil { + c.UI.Output("Error initializing Kubernetes client: %v", err, terminal.WithErrorStyle()) + return err + } + } + + return nil +} + +// Help returns a description of the command and how it is used. +func (c *ReadCommand) Help() string { + c.once.Do(c.init) + return c.Synopsis() + "\n\nUsage: consul-k8s config read [flags]\n\n" + c.help +} + +// Synopsis returns a one-line command summary. +func (c *ReadCommand) Synopsis() string { + return "Returns the helm config of a Consul installation on Kubernetes." +} diff --git a/cli/cmd/config/read/command_test.go b/cli/cmd/config/read/command_test.go new file mode 100644 index 0000000000..a3716cf3c1 --- /dev/null +++ b/cli/cmd/config/read/command_test.go @@ -0,0 +1,149 @@ +package read + +import ( + "bytes" + "context" + "errors" + "flag" + "fmt" + "io" + "os" + "testing" + + "github.com/hashicorp/consul-k8s/cli/common" + cmnFlag "github.com/hashicorp/consul-k8s/cli/common/flag" + "github.com/hashicorp/consul-k8s/cli/common/terminal" + "github.com/hashicorp/consul-k8s/cli/helm" + "github.com/hashicorp/go-hclog" + "github.com/posener/complete" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + helmRelease "helm.sh/helm/v3/pkg/release" + helmTime "helm.sh/helm/v3/pkg/time" + "k8s.io/client-go/kubernetes/fake" +) + +func TestConfigRead(t *testing.T) { + nowTime := helmTime.Now() + cases := map[string]struct { + messages []string + helmActionsRunner *helm.MockActionRunner + expectedReturnCode int + }{ + "empty config": { + messages: []string{"\n"}, + + helmActionsRunner: &helm.MockActionRunner{ + GetStatusFunc: func(status *action.Status, name string) (*helmRelease.Release, error) { + return &helmRelease.Release{ + Name: "consul", Namespace: "consul", + Info: &helmRelease.Info{LastDeployed: nowTime, Status: "READY"}, + Chart: &chart.Chart{Metadata: &chart.Metadata{Version: "1.0.0"}}, + Config: make(map[string]interface{})}, nil + }, + }, + expectedReturnCode: 0, + }, + "error": { + messages: []string{"error", "\n"}, + + helmActionsRunner: &helm.MockActionRunner{ + GetStatusFunc: func(status *action.Status, name string) (*helmRelease.Release, error) { + return nil, errors.New("error") + }, + }, + expectedReturnCode: 1, + }, + "some config": { + messages: []string{"global: \"true\"", "\n"}, + + helmActionsRunner: &helm.MockActionRunner{ + GetStatusFunc: func(status *action.Status, name string) (*helmRelease.Release, error) { + return &helmRelease.Release{ + Name: "consul", Namespace: "consul", + Info: &helmRelease.Info{LastDeployed: nowTime, Status: "READY"}, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{ + Version: "1.0.0", + }, + }, + Config: map[string]interface{}{"global": "true"}, + }, nil + }, + }, + expectedReturnCode: 0, + }, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + buf := new(bytes.Buffer) + c := getInitializedCommand(t, buf) + c.kubernetes = fake.NewSimpleClientset() + c.helmActionsRunner = tc.helmActionsRunner + returnCode := c.Run([]string{}) + require.Equal(t, tc.expectedReturnCode, returnCode) + output := buf.String() + for _, msg := range tc.messages { + require.Contains(t, output, msg) + } + }) + } +} + +func TestTaskCreateCommand_AutocompleteFlags(t *testing.T) { + t.Parallel() + cmd := getInitializedCommand(t, nil) + + predictor := cmd.AutocompleteFlags() + + // Test that we get the expected number of predictions + args := complete.Args{Last: "-"} + res := predictor.Predict(args) + + // Grab the list of flags from the Flag object + flags := make([]string, 0) + cmd.set.VisitSets(func(name string, set *cmnFlag.Set) { + set.VisitAll(func(flag *flag.Flag) { + flags = append(flags, fmt.Sprintf("-%s", flag.Name)) + }) + }) + + // Verify that there is a prediction for each flag associated with the command + assert.Equal(t, len(flags), len(res)) + assert.ElementsMatch(t, flags, res, "flags and predictions didn't match, make sure to add "+ + "new flags to the command AutoCompleteFlags function") +} + +func TestTaskCreateCommand_AutocompleteArgs(t *testing.T) { + cmd := getInitializedCommand(t, nil) + c := cmd.AutocompleteArgs() + assert.Equal(t, complete.PredictNothing, c) +} + +// getInitializedCommand sets up a command struct for tests. +func getInitializedCommand(t *testing.T, buf io.Writer) *ReadCommand { + t.Helper() + log := hclog.New(&hclog.LoggerOptions{ + Name: "cli", + Level: hclog.Info, + Output: os.Stdout, + }) + var ui terminal.UI + if buf != nil { + ui = terminal.NewUI(context.Background(), buf) + } else { + ui = terminal.NewBasicUI(context.Background()) + } + baseCommand := &common.BaseCommand{ + Log: log, + UI: ui, + } + + c := &ReadCommand{ + BaseCommand: baseCommand, + } + c.init() + return c +} diff --git a/cli/cmd/troubleshoot/proxy/proxy.go b/cli/cmd/troubleshoot/proxy/proxy.go index d17c491f5d..8e0793eecd 100644 --- a/cli/cmd/troubleshoot/proxy/proxy.go +++ b/cli/cmd/troubleshoot/proxy/proxy.go @@ -228,8 +228,8 @@ func (c *ProxyCommand) Troubleshoot() error { c.UI.Output(o.Message, terminal.WithSuccessStyle()) } else { c.UI.Output(o.Message, terminal.WithErrorStyle()) - if o.PossibleActions != "" { - c.UI.Output(fmt.Sprintf("possible actions: %v", o.PossibleActions), terminal.WithInfoStyle()) + for _, action := range o.PossibleActions { + c.UI.Output(fmt.Sprintf("-> %v", action), terminal.WithInfoStyle()) } } } diff --git a/cli/cmd/troubleshoot/upstreams/upstreams.go b/cli/cmd/troubleshoot/upstreams/upstreams.go index 8b20928c7a..7cc1cc0acf 100644 --- a/cli/cmd/troubleshoot/upstreams/upstreams.go +++ b/cli/cmd/troubleshoot/upstreams/upstreams.go @@ -3,6 +3,7 @@ package upstreams import ( "fmt" "net" + "sort" "strconv" "strings" "sync" @@ -117,7 +118,6 @@ func (c *UpstreamsCommand) Run(args []string) int { // validateFlags ensures that the flags passed in by the can be used. func (c *UpstreamsCommand) validateFlags() error { - if c.flagPod == "" { return fmt.Errorf("-pod flag is required") } @@ -195,7 +195,7 @@ func (c *UpstreamsCommand) Troubleshoot() error { return fmt.Errorf("error getting upstreams: %v", err) } - c.UI.Output(fmt.Sprintf("Envoy Identifiers (explicit upstreams only) (%v)", len(envoyIDs)), terminal.WithHeaderStyle()) + c.UI.Output(fmt.Sprintf("Upstreams (explicit upstreams only) (%v)", len(envoyIDs)), terminal.WithHeaderStyle()) for _, e := range envoyIDs { c.UI.Output(e) } @@ -203,10 +203,20 @@ func (c *UpstreamsCommand) Troubleshoot() error { c.UI.Output(fmt.Sprintf("Upstream IPs (transparent proxy only) (%v)", len(upstreamIPs)), terminal.WithHeaderStyle()) table := terminal.NewTable("IPs ", "Virtual ", "Cluster Names") for _, u := range upstreamIPs { - table.AddRow([]string{formatIPs(u.IPs), strconv.FormatBool(u.IsVirtual), formatClusterNames(u.ClusterNames)}, []string{}) + table.AddRow( + []string{formatIPs(u.IPs), strconv.FormatBool(u.IsVirtual), formatClusterNames(u.ClusterNames)}, + []string{}, + ) } c.UI.Table(table) + c.UI.Output("\nIf you cannot find the upstream address or cluster for a transparent proxy upstream:", terminal.WithInfoStyle()) + c.UI.Output("-> Check intentions: Transparent proxy upstreams are configured based on intentions. Make sure you "+ + "have configured intentions to allow traffic to your upstream.", terminal.WithInfoStyle()) + c.UI.Output("-> To check that the right cluster is being dialed, run a DNS lookup "+ + "for the upstream you are dialing. For example, run `dig backend.svc.consul` to return the IP address for the `backend` service. If the address you get from that is missing "+ + "from the upstream IPs, it means that your proxy may be misconfigured.", terminal.WithInfoStyle()) + return nil } @@ -245,6 +255,7 @@ func formatClusterNames(names map[string]struct{}) string { for k := range names { out = append(out, k) } + sort.Strings(out) return strings.Join(out, ", ") } diff --git a/cli/commands.go b/cli/commands.go index fe4c47400e..fa8e320451 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -3,6 +3,8 @@ package main import ( "context" + "github.com/hashicorp/consul-k8s/cli/cmd/config" + config_read "github.com/hashicorp/consul-k8s/cli/cmd/config/read" "github.com/hashicorp/consul-k8s/cli/cmd/install" "github.com/hashicorp/consul-k8s/cli/cmd/proxy" "github.com/hashicorp/consul-k8s/cli/cmd/proxy/list" @@ -76,6 +78,16 @@ func initializeCommands(ctx context.Context, log hclog.Logger) (*common.BaseComm BaseCommand: baseCommand, }, nil }, + "config": func() (cli.Command, error) { + return &config.ConfigCommand{ + BaseCommand: baseCommand, + }, nil + }, + "config read": func() (cli.Command, error) { + return &config_read.ReadCommand{ + BaseCommand: baseCommand, + }, nil + }, "troubleshoot": func() (cli.Command, error) { return &troubleshoot.TroubleshootCommand{ BaseCommand: baseCommand, diff --git a/cli/go.mod b/cli/go.mod index 7c1a5af4b8..98f41c29b9 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -1,6 +1,6 @@ module github.com/hashicorp/consul-k8s/cli -go 1.19 +go 1.20 require ( github.com/bgentry/speakeasy v0.1.0 @@ -8,7 +8,7 @@ require ( github.com/fatih/color v1.13.0 github.com/google/go-cmp v0.5.8 github.com/hashicorp/consul-k8s/charts v0.0.0-00010101000000-000000000000 - github.com/hashicorp/consul/troubleshoot v0.0.0-20230210154717-4f2ce606547b + github.com/hashicorp/consul/troubleshoot v0.1.2 github.com/hashicorp/go-hclog v1.2.1 github.com/hashicorp/hcp-sdk-go v0.23.1-0.20220921131124-49168300a7dc github.com/kr/text v0.2.0 @@ -17,7 +17,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/posener/complete v1.2.3 github.com/stretchr/testify v1.8.0 - golang.org/x/text v0.5.0 + golang.org/x/text v0.11.0 helm.sh/helm/v3 v3.9.4 k8s.io/api v0.25.0 k8s.io/apiextensions-apiserver v0.25.0 @@ -99,8 +99,8 @@ require ( github.com/gorilla/mux v1.8.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect - github.com/hashicorp/consul/api v1.10.1-0.20230209203402-db2bd404bf72 // indirect - github.com/hashicorp/consul/envoyextensions v0.0.0-20230210154717-4f2ce606547b // indirect + github.com/hashicorp/consul/api v1.20.0 // indirect + github.com/hashicorp/consul/envoyextensions v0.1.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect @@ -164,12 +164,12 @@ require ( github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect go.mongodb.org/mongo-driver v1.11.1 // indirect go.starlark.net v0.0.0-20230128213706-3f75dec8e403 // indirect - golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect - golang.org/x/net v0.4.0 // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/net v0.13.0 // indirect golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/term v0.3.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/term v0.10.0 // indirect golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220921223823-23cae91e6737 // indirect diff --git a/cli/go.sum b/cli/go.sum index d736c0176a..5293b7fba8 100644 --- a/cli/go.sum +++ b/cli/go.sum @@ -437,14 +437,14 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWet github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 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-0.20230209203402-db2bd404bf72 h1:O+z5m5kNtu6NHBMwMsRb1S0P7giqNu5vBBeCzgiAesg= -github.com/hashicorp/consul/api v1.10.1-0.20230209203402-db2bd404bf72/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= -github.com/hashicorp/consul/envoyextensions v0.0.0-20230210154717-4f2ce606547b h1:T+El0UxZP7h2mGL+EPBJejS4gKM/w0KAYOSpTs7hrbY= -github.com/hashicorp/consul/envoyextensions v0.0.0-20230210154717-4f2ce606547b/go.mod h1:oJKG0zAMtq6ZmZNYQyeKh6kIJmi01rZSZDSgnjzZ15w= +github.com/hashicorp/consul/api v1.20.0 h1:9IHTjNVSZ7MIwjlW3N3a7iGiykCMDpxZu8jsxFJh0yc= +github.com/hashicorp/consul/api v1.20.0/go.mod h1:nR64eD44KQ59Of/ECwt2vUmIK2DKsDzAwTmwmLl8Wpo= +github.com/hashicorp/consul/envoyextensions v0.1.2 h1:PvPqJ/td3UpOeIKQl5ycFPUy46XZP9awfhAUCduDeI4= +github.com/hashicorp/consul/envoyextensions v0.1.2/go.mod h1:N94DQQkgITiA40zuTQ/UdPOLAAWobgHfVT5u7wxE/aU= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= -github.com/hashicorp/consul/troubleshoot v0.0.0-20230210154717-4f2ce606547b h1:I5zDW3o7KwW4cX5kkerhm7bZOEknlSjdnIgtxnhBxOk= -github.com/hashicorp/consul/troubleshoot v0.0.0-20230210154717-4f2ce606547b/go.mod h1:rskvju2tK8XvHYTAILHjO7lpV1/uViHs3Q3mg9Rkwlg= +github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= +github.com/hashicorp/consul/troubleshoot v0.1.2 h1:c6uMTSt/qTMhK3e18nl4xW4j7JcANdQNHOEYhoXH1P8= +github.com/hashicorp/consul/troubleshoot v0.1.2/go.mod h1:q35QOtN7K5kFLPm2SXHBDD+PzsuBekcqTZuuoOTzbWA= 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= @@ -877,8 +877,9 @@ golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/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-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= 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= @@ -965,8 +966,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx 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.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= @@ -1087,14 +1088,14 @@ 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-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.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.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.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= 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= @@ -1104,8 +1105,8 @@ 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.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.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.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= diff --git a/cli/helm/values.go b/cli/helm/values.go index e7042f85e1..1f55ecfdf3 100644 --- a/cli/helm/values.go +++ b/cli/helm/values.go @@ -408,7 +408,7 @@ type TransparentProxy struct { } type Metrics struct { - DefaultEnabled string `yaml:"defaultEnabled"` + DefaultEnabled bool `yaml:"defaultEnabled"` DefaultEnableMerging bool `yaml:"defaultEnableMerging"` DefaultMergedMetricsPort int `yaml:"defaultMergedMetricsPort"` DefaultPrometheusScrapePort int `yaml:"defaultPrometheusScrapePort"` @@ -422,12 +422,21 @@ type ACLInjectToken struct { type SidecarProxy struct { Resources Resources `yaml:"resources"` + Lifecycle Lifecycle `yaml:"lifecycle"` } type InitContainer struct { Resources Resources `yaml:"resources"` } +type Lifecycle struct { + DefaultEnabled bool `yaml:"defaultEnabled"` + DefaultEnableShutdownDrainListeners bool `yaml:"defaultEnableShutdownDrainListeners"` + DefaultShutdownGracePeriodSeconds int `yaml:"defaultShutdownGracePeriodSeconds"` + DefaultGracefulPort int `yaml:"defaultGracefulPort"` + DefaultGracefulShutdownPath string `yaml:"defaultGracefulShutdownPath"` +} + type ConnectInject struct { Enabled bool `yaml:"enabled"` Replicas int `yaml:"replicas"` diff --git a/cli/preset/cloud_preset.go b/cli/preset/cloud_preset.go index 95219cb378..84af7cf8cf 100644 --- a/cli/preset/cloud_preset.go +++ b/cli/preset/cloud_preset.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "net/http" + "strings" "github.com/hashicorp/consul-k8s/cli/common" "github.com/hashicorp/consul-k8s/cli/common/terminal" @@ -198,6 +199,8 @@ global: bootstrapToken: secretName: %s secretKey: %s + metrics: + enableTelemetryCollector: true cloud: enabled: true resourceId: @@ -212,6 +215,15 @@ global: %s %s %s +telemetryCollector: + enabled: true + cloud: + clientId: + secretName: %s + secretKey: %s + clientSecret: + secretName: %s + secretKey: %s server: replicas: %d affinity: null @@ -221,13 +233,15 @@ connectInject: enabled: true controller: enabled: true -`, cfg.BootstrapResponse.Cluster.ID, secretNameServerCA, corev1.TLSCertKey, +`, strings.ToLower(cfg.BootstrapResponse.Cluster.ID), secretNameServerCA, corev1.TLSCertKey, secretNameGossipKey, secretKeyGossipKey, secretNameBootstrapToken, secretKeyBootstrapToken, secretNameHCPResourceID, secretKeyHCPResourceID, secretNameHCPClientID, secretKeyHCPClientID, secretNameHCPClientSecret, secretKeyHCPClientSecret, apiHostCfg, authURLCfg, scadaAddressCfg, + secretNameHCPClientID, secretKeyHCPClientID, + secretNameHCPClientSecret, secretKeyHCPClientSecret, cfg.BootstrapResponse.Cluster.BootstrapExpect, secretNameServerCert) valuesMap := config.ConvertToMap(values) return valuesMap diff --git a/cli/preset/cloud_preset_test.go b/cli/preset/cloud_preset_test.go index 946e1ca158..f2dbb5fe8e 100644 --- a/cli/preset/cloud_preset_test.go +++ b/cli/preset/cloud_preset_test.go @@ -40,7 +40,7 @@ const ( { "cluster": { - "id": "dc1", + "id": "Dc1", "bootstrap_expect" : 3 }, "bootstrap": @@ -60,7 +60,7 @@ const ( var validBootstrapReponse *models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse = &models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{ Bootstrap: &models.HashicorpCloudGlobalNetworkManager20220215ClusterBootstrap{ - ID: "dc1", + ID: "Dc1", GossipKey: "Wa6/XFAnYy0f9iqVH2iiG+yore3CqHSemUy4AIVTa/w=", BootstrapExpect: 3, ServerTLS: &models.HashicorpCloudGlobalNetworkManager20220215ServerTLS{ @@ -480,6 +480,8 @@ global: gossipEncryption: secretKey: key secretName: consul-gossip-key + metrics: + enableTelemetryCollector: true tls: caCert: secretKey: tls.crt @@ -491,6 +493,15 @@ server: replicas: 3 serverCert: secretName: consul-server-cert +telemetryCollector: + cloud: + clientId: + secretKey: client-id + secretName: consul-hcp-client-id + clientSecret: + secretKey: client-secret + secretName: consul-hcp-client-secret + enabled: true ` const expectedWithoutOptional = `connectInject: @@ -518,6 +529,8 @@ global: gossipEncryption: secretKey: key secretName: consul-gossip-key + metrics: + enableTelemetryCollector: true tls: caCert: secretKey: tls.crt @@ -529,16 +542,43 @@ server: replicas: 3 serverCert: secretName: consul-server-cert +telemetryCollector: + cloud: + clientId: + secretKey: client-id + secretName: consul-hcp-client-id + clientSecret: + secretKey: client-secret + secretName: consul-hcp-client-secret + enabled: true ` cloudPreset := &CloudPreset{} - testCases := []struct { - description string + testCases := map[string]struct { config *CloudBootstrapConfig expectedYaml string }{ - {"Config including optional parameters", + "Config_including_optional_parameters_with_mixedcase_DC": { + &CloudBootstrapConfig{ + BootstrapResponse: &models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{ + Cluster: &models.HashicorpCloudGlobalNetworkManager20220215Cluster{ + BootstrapExpect: 3, + ID: "Dc1", + }, + }, + HCPConfig: HCPConfig{ + ResourceID: "consul-hcp-resource-id", + ClientID: "consul-hcp-client-id", + ClientSecret: "consul-hcp-client-secret", + AuthURL: "consul-hcp-auth-url", + APIHostname: "consul-hcp-api-host", + ScadaAddress: "consul-hcp-scada-address", + }, + }, + expectedFull, + }, + "Config_including_optional_parameters": { &CloudBootstrapConfig{ BootstrapResponse: &models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{ Cluster: &models.HashicorpCloudGlobalNetworkManager20220215Cluster{ @@ -557,7 +597,7 @@ server: }, expectedFull, }, - {"Config without optional parameters", + "Config_without_optional_parameters": { &CloudBootstrapConfig{ BootstrapResponse: &models.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{ Cluster: &models.HashicorpCloudGlobalNetworkManager20220215Cluster{ @@ -574,8 +614,8 @@ server: expectedWithoutOptional, }, } - for _, tc := range testCases { - t.Run(tc.description, func(t *testing.T) { + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { cloudHelmValues := cloudPreset.getHelmConfigWithMapSecretNames(tc.config) require.NotNil(t, cloudHelmValues) valuesYaml, err := yaml.Marshal(cloudHelmValues) diff --git a/cli/version/version.go b/cli/version/version.go index 933f072f35..5cf34d7c8c 100644 --- a/cli/version/version.go +++ b/cli/version/version.go @@ -14,7 +14,7 @@ var ( // // Version must conform to the format expected by // github.com/hashicorp/go-version for tests to work. - Version = "1.1.0" + Version = "1.1.5" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release diff --git a/control-plane/Dockerfile b/control-plane/Dockerfile index 2989712a5f..844c487e49 100644 --- a/control-plane/Dockerfile +++ b/control-plane/Dockerfile @@ -14,12 +14,12 @@ # go-discover builds the discover binary (which we don't currently publish # either). FROM golang:1.19.2-alpine as go-discover -RUN CGO_ENABLED=0 go install github.com/hashicorp/go-discover/cmd/discover@49f60c093101c9c5f6b04d5b1c80164251a761a6 +RUN CGO_ENABLED=0 go install github.com/hashicorp/go-discover/cmd/discover@214571b6a5309addf3db7775f4ee8cf4d264fd5f # dev copies the binary from a local build # ----------------------------------- # BIN_NAME is a requirement in the hashicorp docker github action -FROM alpine:3.16 AS dev +FROM alpine:3.18 AS dev # NAME and VERSION are the name of the software in releases.hashicorp.com # and the version to download. Example: NAME=consul VERSION=1.2.3. @@ -71,7 +71,7 @@ CMD /bin/${BIN_NAME} # We don't rebuild the software because we want the exact checksums and # binary signatures to match the software and our builds aren't fully # reproducible currently. -FROM alpine:3.16 AS release-default +FROM alpine:3.18 AS release-default ARG BIN_NAME=consul-k8s-control-plane ARG CNI_BIN_NAME=consul-cni @@ -117,7 +117,7 @@ CMD /bin/${BIN_NAME} # We don't rebuild the software because we want the exact checksums and # binary signatures to match the software and our builds aren't fully # reproducible currently. -FROM registry.access.redhat.com/ubi9-minimal:9.1.0 as ubi +FROM registry.access.redhat.com/ubi9-minimal:9.2 as ubi ARG PRODUCT_NAME ARG PRODUCT_VERSION diff --git a/control-plane/api/common/configentry_webhook_test.go b/control-plane/api/common/configentry_webhook_test.go index cf79efea85..3a2dc9098b 100644 --- a/control-plane/api/common/configentry_webhook_test.go +++ b/control-plane/api/common/configentry_webhook_test.go @@ -6,7 +6,7 @@ import ( "errors" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" capi "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" "gomodules.xyz/jsonpatch/v2" @@ -112,7 +112,7 @@ func TestValidateConfigEntry(t *testing.T) { }, }, }, - logrtest.TestLogger{T: t}, + logrtest.New(t), lister, c.newResource, ConsulMeta{ diff --git a/control-plane/api/v1alpha1/exportedservices_webhook_test.go b/control-plane/api/v1alpha1/exportedservices_webhook_test.go index 6548c131f7..3a66fbdd9c 100644 --- a/control-plane/api/v1alpha1/exportedservices_webhook_test.go +++ b/control-plane/api/v1alpha1/exportedservices_webhook_test.go @@ -5,7 +5,7 @@ import ( "encoding/json" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/stretchr/testify/require" admissionv1 "k8s.io/api/admission/v1" @@ -177,7 +177,7 @@ func TestValidateExportedServices(t *testing.T) { validator := &ExportedServicesWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.New(t), decoder: decoder, ConsulMeta: c.consulMeta, } diff --git a/control-plane/api/v1alpha1/mesh_webhook_test.go b/control-plane/api/v1alpha1/mesh_webhook_test.go index 55b0c3a77d..83cedb0ba4 100644 --- a/control-plane/api/v1alpha1/mesh_webhook_test.go +++ b/control-plane/api/v1alpha1/mesh_webhook_test.go @@ -5,7 +5,7 @@ import ( "encoding/json" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/stretchr/testify/require" admissionv1 "k8s.io/api/admission/v1" @@ -94,7 +94,7 @@ func TestValidateMesh(t *testing.T) { validator := &MeshWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.New(t), decoder: decoder, } response := validator.Handle(ctx, admission.Request{ diff --git a/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go b/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go index a65966881a..02ddbca588 100644 --- a/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go +++ b/control-plane/api/v1alpha1/peeringacceptor_webhook_test.go @@ -5,7 +5,7 @@ import ( "encoding/json" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/stretchr/testify/require" admissionv1 "k8s.io/api/admission/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -135,7 +135,7 @@ func TestValidatePeeringAcceptor(t *testing.T) { validator := &PeeringAcceptorWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.New(t), decoder: decoder, } response := validator.Handle(ctx, admission.Request{ diff --git a/control-plane/api/v1alpha1/peeringdialer_webhook_test.go b/control-plane/api/v1alpha1/peeringdialer_webhook_test.go index e8b206e3e6..df69923a92 100644 --- a/control-plane/api/v1alpha1/peeringdialer_webhook_test.go +++ b/control-plane/api/v1alpha1/peeringdialer_webhook_test.go @@ -5,7 +5,7 @@ import ( "encoding/json" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/stretchr/testify/require" admissionv1 "k8s.io/api/admission/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -135,7 +135,7 @@ func TestValidatePeeringDialer(t *testing.T) { validator := &PeeringDialerWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.New(t), decoder: decoder, } response := validator.Handle(ctx, admission.Request{ diff --git a/control-plane/api/v1alpha1/proxydefaults_webhook_test.go b/control-plane/api/v1alpha1/proxydefaults_webhook_test.go index 3136540089..2dc7966a6d 100644 --- a/control-plane/api/v1alpha1/proxydefaults_webhook_test.go +++ b/control-plane/api/v1alpha1/proxydefaults_webhook_test.go @@ -5,7 +5,7 @@ import ( "encoding/json" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/stretchr/testify/require" admissionv1 "k8s.io/api/admission/v1" @@ -119,7 +119,7 @@ func TestValidateProxyDefault(t *testing.T) { validator := &ProxyDefaultsWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.New(t), decoder: decoder, } response := validator.Handle(ctx, admission.Request{ diff --git a/control-plane/api/v1alpha1/servicedefaults_types.go b/control-plane/api/v1alpha1/servicedefaults_types.go index 06da8b1d2c..3a8bb94b7e 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types.go +++ b/control-plane/api/v1alpha1/servicedefaults_types.go @@ -4,9 +4,11 @@ import ( "fmt" "net" "strings" + "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/hashicorp/consul-k8s/control-plane/api/common" capi "github.com/hashicorp/consul/api" "github.com/miekg/dns" corev1 "k8s.io/api/core/v1" @@ -14,8 +16,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" - - "github.com/hashicorp/consul-k8s/control-plane/api/common" ) const ( @@ -180,7 +180,14 @@ type PassiveHealthCheck struct { // EnforcingConsecutive5xx is the % chance that a host will be actually ejected // when an outlier status is detected through consecutive 5xx. // This setting can be used to disable ejection or to ramp it up slowly. - EnforcingConsecutive5xx *uint32 `json:"enforcing_consecutive_5xx,omitempty"` + EnforcingConsecutive5xx *uint32 `json:"enforcingConsecutive5xx,omitempty"` + // The maximum % of an upstream cluster that can be ejected due to outlier detection. + // Defaults to 10% but will eject at least one host regardless of the value. + MaxEjectionPercent *uint32 `json:"maxEjectionPercent,omitempty"` + // The base time that a host is ejected for. The real time is equal to the base time + // multiplied by the number of times the host has been ejected and is capped by + // max_ejection_time (Default 300s). Defaults to 30000ms or 30s. + BaseEjectionTime *metav1.Duration `json:"baseEjectionTime,omitempty"` } type ServiceDefaultsDestination struct { @@ -440,11 +447,20 @@ func (in *PassiveHealthCheck) toConsul() *capi.PassiveHealthCheck { if in == nil { return nil } + var baseEjectiontime *time.Duration + if in.BaseEjectionTime == nil { + dur := time.Second * 30 + baseEjectiontime = &dur + } else { + baseEjectiontime = &in.BaseEjectionTime.Duration + } return &capi.PassiveHealthCheck{ Interval: in.Interval.Duration, MaxFailures: in.MaxFailures, EnforcingConsecutive5xx: in.EnforcingConsecutive5xx, + MaxEjectionPercent: in.MaxEjectionPercent, + BaseEjectionTime: baseEjectiontime, } } diff --git a/control-plane/api/v1alpha1/servicedefaults_types_test.go b/control-plane/api/v1alpha1/servicedefaults_types_test.go index 33ec6d2f40..05f1bdd3d9 100644 --- a/control-plane/api/v1alpha1/servicedefaults_types_test.go +++ b/control-plane/api/v1alpha1/servicedefaults_types_test.go @@ -87,6 +87,10 @@ func TestServiceDefaults_ToConsul(t *testing.T) { }, MaxFailures: uint32(20), EnforcingConsecutive5xx: pointer.Uint32(100), + MaxEjectionPercent: pointer.Uint32(10), + BaseEjectionTime: &metav1.Duration{ + Duration: 10 * time.Second, + }, }, MeshGateway: MeshGateway{ Mode: "local", @@ -112,6 +116,10 @@ func TestServiceDefaults_ToConsul(t *testing.T) { }, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(20), + BaseEjectionTime: &metav1.Duration{ + Duration: 20 * time.Second, + }, }, MeshGateway: MeshGateway{ Mode: "remote", @@ -136,6 +144,10 @@ func TestServiceDefaults_ToConsul(t *testing.T) { }, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(30), + BaseEjectionTime: &metav1.Duration{ + Duration: 30 * time.Second, + }, }, MeshGateway: MeshGateway{ Mode: "remote", @@ -212,6 +224,8 @@ func TestServiceDefaults_ToConsul(t *testing.T) { Interval: 2 * time.Second, MaxFailures: uint32(20), EnforcingConsecutive5xx: pointer.Uint32(100), + MaxEjectionPercent: pointer.Uint32(10), + BaseEjectionTime: pointer.Duration(10 * time.Second), }, MeshGateway: capi.MeshGatewayConfig{ Mode: "local", @@ -235,6 +249,8 @@ func TestServiceDefaults_ToConsul(t *testing.T) { Interval: 2 * time.Second, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(20), + BaseEjectionTime: pointer.Duration(20 * time.Second), }, MeshGateway: capi.MeshGatewayConfig{ Mode: "remote", @@ -257,6 +273,8 @@ func TestServiceDefaults_ToConsul(t *testing.T) { Interval: 2 * time.Second, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(30), + BaseEjectionTime: pointer.Duration(30 * time.Second), }, MeshGateway: capi.MeshGatewayConfig{ Mode: "remote", @@ -307,6 +325,91 @@ func TestServiceDefaults_ToConsul(t *testing.T) { } } +func TestPasstiveHealthCheckConsul(t *testing.T) { + baseDur := time.Second * 30 + baseEjection := time.Second * 60 + baseInt := uint32(1) + for name, tc := range map[string]struct { + input *PassiveHealthCheck + output *capi.PassiveHealthCheck + }{ + "basenil": {}, + "base": { + input: &PassiveHealthCheck{}, + output: &capi.PassiveHealthCheck{BaseEjectionTime: &baseDur}, + }, + "with_interval": { + input: &PassiveHealthCheck{ + Interval: metav1.Duration{Duration: baseDur}, + }, + output: &capi.PassiveHealthCheck{ + Interval: time.Second * 30, + BaseEjectionTime: &baseDur, + }, + }, + "with_interval_maxfailures": { + input: &PassiveHealthCheck{ + Interval: metav1.Duration{Duration: baseDur}, + MaxFailures: 100, + }, + output: &capi.PassiveHealthCheck{ + MaxFailures: 100, + Interval: time.Second * 30, + BaseEjectionTime: &baseDur, + }, + }, + "with_interval_maxfailures_enforcing": { + input: &PassiveHealthCheck{ + Interval: metav1.Duration{Duration: baseDur}, + MaxFailures: 100, + EnforcingConsecutive5xx: &baseInt, + }, + output: &capi.PassiveHealthCheck{ + MaxFailures: 100, + Interval: time.Second * 30, + BaseEjectionTime: &baseDur, + EnforcingConsecutive5xx: &baseInt, + }, + }, + "with_interval_maxfailures_enforcing_maxejection": { + input: &PassiveHealthCheck{ + Interval: metav1.Duration{Duration: baseDur}, + MaxFailures: 100, + EnforcingConsecutive5xx: &baseInt, + MaxEjectionPercent: &baseInt, + }, + output: &capi.PassiveHealthCheck{ + MaxFailures: 100, + Interval: time.Second * 30, + BaseEjectionTime: &baseDur, + EnforcingConsecutive5xx: &baseInt, + MaxEjectionPercent: &baseInt, + }, + }, + "with_interval_maxfailures_enforcing_maxejection_baseejection": { + input: &PassiveHealthCheck{ + Interval: metav1.Duration{Duration: baseDur}, + MaxFailures: 100, + EnforcingConsecutive5xx: &baseInt, + MaxEjectionPercent: &baseInt, + BaseEjectionTime: &metav1.Duration{Duration: baseEjection}, + }, + output: &capi.PassiveHealthCheck{ + MaxFailures: 100, + Interval: time.Second * 30, + BaseEjectionTime: &baseEjection, + EnforcingConsecutive5xx: &baseInt, + MaxEjectionPercent: &baseInt, + }, + }, + } { + t.Run(name, func(t *testing.T) { + output := tc.input.toConsul() + require.Equal(t, tc.output, output) + }) + } +} + func TestServiceDefaults_MatchesConsul(t *testing.T) { cases := map[string]struct { internal *ServiceDefaults @@ -383,6 +486,10 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { }, MaxFailures: uint32(20), EnforcingConsecutive5xx: pointer.Uint32(100), + MaxEjectionPercent: pointer.Uint32(10), + BaseEjectionTime: &metav1.Duration{ + Duration: 10 * time.Second, + }, }, MeshGateway: MeshGateway{ Mode: "local", @@ -407,6 +514,10 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { }, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(20), + BaseEjectionTime: &metav1.Duration{ + Duration: 20 * time.Second, + }, }, MeshGateway: MeshGateway{ Mode: "remote", @@ -430,6 +541,10 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { }, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(30), + BaseEjectionTime: &metav1.Duration{ + Duration: 30 * time.Second, + }, }, MeshGateway: MeshGateway{ Mode: "remote", @@ -501,6 +616,8 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { Interval: 2 * time.Second, MaxFailures: uint32(20), EnforcingConsecutive5xx: pointer.Uint32(100), + MaxEjectionPercent: pointer.Uint32(10), + BaseEjectionTime: pointer.Duration(10 * time.Second), }, MeshGateway: capi.MeshGatewayConfig{ Mode: "local", @@ -523,6 +640,8 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { Interval: 2 * time.Second, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(20), + BaseEjectionTime: pointer.Duration(20 * time.Second), }, MeshGateway: capi.MeshGatewayConfig{ Mode: "remote", @@ -544,6 +663,8 @@ func TestServiceDefaults_MatchesConsul(t *testing.T) { Interval: 2 * time.Second, MaxFailures: uint32(10), EnforcingConsecutive5xx: pointer.Uint32(60), + MaxEjectionPercent: pointer.Uint32(30), + BaseEjectionTime: pointer.Duration(30 * time.Second), }, MeshGateway: capi.MeshGatewayConfig{ Mode: "remote", diff --git a/control-plane/api/v1alpha1/serviceintentions_types.go b/control-plane/api/v1alpha1/serviceintentions_types.go index a0a240639a..1a41caa289 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types.go +++ b/control-plane/api/v1alpha1/serviceintentions_types.go @@ -228,18 +228,34 @@ func (in *ServiceIntentions) ConsulGlobalResource() bool { return false } +func normalizeEmptyToDefault(value string) string { + if value == "" { + return "default" + } + return value +} + func (in *ServiceIntentions) MatchesConsul(candidate api.ConfigEntry) bool { configEntry, ok := candidate.(*capi.ServiceIntentionsConfigEntry) if !ok { return false } + specialEquality := cmp.Options{ + cmp.FilterPath(func(path cmp.Path) bool { + return path.String() == "Sources.Namespace" + }, cmp.Transformer("NormalizeNamespace", normalizeEmptyToDefault)), + cmp.FilterPath(func(path cmp.Path) bool { + return path.String() == "Sources.Partition" + }, cmp.Transformer("NormalizePartition", normalizeEmptyToDefault)), + } + // No datacenter is passed to ToConsul as we ignore the Meta field when checking for equality. return cmp.Equal( in.ToConsul(""), configEntry, cmpopts.IgnoreFields(capi.ServiceIntentionsConfigEntry{}, "Partition", "Namespace", "Meta", "ModifyIndex", "CreateIndex"), - cmpopts.IgnoreFields(capi.SourceIntention{}, "Partition", "Namespace", "LegacyID", "LegacyMeta", "LegacyCreateTime", "LegacyUpdateTime", "Precedence", "Type"), + cmpopts.IgnoreFields(capi.SourceIntention{}, "LegacyID", "LegacyMeta", "LegacyCreateTime", "LegacyUpdateTime", "Precedence", "Type"), cmpopts.IgnoreUnexported(), cmpopts.EquateEmpty(), // Consul will sort the sources by precedence when returning the resource @@ -249,6 +265,7 @@ func (in *ServiceIntentions) MatchesConsul(candidate api.ConfigEntry) bool { // piggyback on strings.Compare that returns -1 if a < b. return strings.Compare(sourceIntentionSortKey(a), sourceIntentionSortKey(b)) == -1 }), + specialEquality, ) } diff --git a/control-plane/api/v1alpha1/serviceintentions_types_test.go b/control-plane/api/v1alpha1/serviceintentions_types_test.go index f9be4120f2..01199f74d1 100644 --- a/control-plane/api/v1alpha1/serviceintentions_types_test.go +++ b/control-plane/api/v1alpha1/serviceintentions_types_test.go @@ -37,6 +37,78 @@ func TestServiceIntentions_MatchesConsul(t *testing.T) { }, Matches: true, }, + "namespaces and partitions equate `default` and empty strings": { + Ours: ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + }, + Spec: ServiceIntentionsSpec{ + Destination: IntentionDestination{ + Name: "svc-name", + Namespace: "ns1", + }, + Sources: []*SourceIntention{ + { + Name: "svc1", + Namespace: "", + Partition: "default", + Action: "allow", + }, + }, + }, + }, + Theirs: &capi.ServiceIntentionsConfigEntry{ + Kind: capi.ServiceIntentions, + Name: "svc-name", + Namespace: "ns1", + Sources: []*capi.SourceIntention{ + { + Name: "svc1", + Namespace: "default", + Partition: "", + Action: "allow", + Precedence: 0, + }, + }, + }, + Matches: true, + }, + "source namespaces and partitions are compared": { + Ours: ServiceIntentions{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + }, + Spec: ServiceIntentionsSpec{ + Destination: IntentionDestination{ + Name: "svc-name", + Namespace: "test", + }, + Sources: []*SourceIntention{ + { + Name: "svc1", + Namespace: "test", + Partition: "test", + Action: "allow", + }, + }, + }, + }, + Theirs: &capi.ServiceIntentionsConfigEntry{ + Kind: capi.ServiceIntentions, + Name: "svc-name", + Namespace: "test", + Sources: []*capi.SourceIntention{ + { + Name: "svc1", + Namespace: "not-test", + Partition: "not-test", + Action: "allow", + Precedence: 0, + }, + }, + }, + Matches: false, + }, "all fields set matches": { Ours: ServiceIntentions{ ObjectMeta: metav1.ObjectMeta{ diff --git a/control-plane/api/v1alpha1/serviceintentions_webhook_test.go b/control-plane/api/v1alpha1/serviceintentions_webhook_test.go index e6095e8351..2df10ad11d 100644 --- a/control-plane/api/v1alpha1/serviceintentions_webhook_test.go +++ b/control-plane/api/v1alpha1/serviceintentions_webhook_test.go @@ -6,7 +6,7 @@ import ( "fmt" "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/stretchr/testify/require" "gomodules.xyz/jsonpatch/v2" @@ -250,7 +250,7 @@ func TestHandle_ServiceIntentions_Create(t *testing.T) { validator := &ServiceIntentionsWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.New(t), decoder: decoder, ConsulMeta: common.ConsulMeta{ NamespacesEnabled: true, @@ -439,7 +439,7 @@ func TestHandle_ServiceIntentions_Update(t *testing.T) { validator := &ServiceIntentionsWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.New(t), decoder: decoder, ConsulMeta: common.ConsulMeta{ NamespacesEnabled: true, @@ -599,7 +599,7 @@ func TestHandle_ServiceIntentions_Patches(t *testing.T) { validator := &ServiceIntentionsWebhook{ Client: client, - Logger: logrtest.TestLogger{T: t}, + Logger: logrtest.New(t), decoder: decoder, ConsulMeta: common.ConsulMeta{ NamespacesEnabled: namespacesEnabled, diff --git a/control-plane/api/v1alpha1/serviceresolver_types.go b/control-plane/api/v1alpha1/serviceresolver_types.go index 4fc637b35f..fc5c952ed7 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types.go +++ b/control-plane/api/v1alpha1/serviceresolver_types.go @@ -1,8 +1,10 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package v1alpha1 import ( "encoding/json" - "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/consul-k8s/control-plane/api/common" @@ -70,6 +72,9 @@ type ServiceResolverSpec struct { // ConnectTimeout is the timeout for establishing new network connections // to this service. ConnectTimeout metav1.Duration `json:"connectTimeout,omitempty"` + // RequestTimeout is the timeout for receiving an HTTP response from this + // service before the connection is terminated. + RequestTimeout metav1.Duration `json:"requestTimeout,omitempty"` // LoadBalancer determines the load balancing policy and configuration for services // issuing requests to this upstream service. LoadBalancer *LoadBalancer `json:"loadBalancer,omitempty"` @@ -295,6 +300,7 @@ func (in *ServiceResolver) ToConsul(datacenter string) capi.ConfigEntry { Redirect: in.Spec.Redirect.toConsul(), Failover: in.Spec.Failover.toConsul(), ConnectTimeout: in.Spec.ConnectTimeout.Duration, + RequestTimeout: in.Spec.RequestTimeout.Duration, LoadBalancer: in.Spec.LoadBalancer.toConsul(), Meta: meta(datacenter), } @@ -324,7 +330,6 @@ func (in *ServiceResolver) Validate(consulMeta common.ConsulMeta) error { } errs = append(errs, in.Spec.LoadBalancer.validate(path.Child("loadBalancer"))...) - errs = append(errs, in.validateEnterprise(consulMeta)...) if len(errs) > 0 { diff --git a/control-plane/api/v1alpha1/serviceresolver_types_test.go b/control-plane/api/v1alpha1/serviceresolver_types_test.go index fd4fc25a60..e216add619 100644 --- a/control-plane/api/v1alpha1/serviceresolver_types_test.go +++ b/control-plane/api/v1alpha1/serviceresolver_types_test.go @@ -82,6 +82,7 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { }, }, ConnectTimeout: metav1.Duration{Duration: 1 * time.Second}, + RequestTimeout: metav1.Duration{Duration: 1 * time.Second}, LoadBalancer: &LoadBalancer{ Policy: "policy", RingHashConfig: &RingHashConfig{ @@ -149,6 +150,7 @@ func TestServiceResolver_MatchesConsul(t *testing.T) { }, }, ConnectTimeout: 1 * time.Second, + RequestTimeout: 1 * time.Second, LoadBalancer: &capi.LoadBalancer{ Policy: "policy", RingHashConfig: &capi.RingHashConfig{ @@ -265,6 +267,7 @@ func TestServiceResolver_ToConsul(t *testing.T) { }, }, ConnectTimeout: metav1.Duration{Duration: 1 * time.Second}, + RequestTimeout: metav1.Duration{Duration: 1 * time.Second}, LoadBalancer: &LoadBalancer{ Policy: "policy", RingHashConfig: &RingHashConfig{ @@ -332,6 +335,7 @@ func TestServiceResolver_ToConsul(t *testing.T) { }, }, ConnectTimeout: 1 * time.Second, + RequestTimeout: 1 * time.Second, LoadBalancer: &capi.LoadBalancer{ Policy: "policy", RingHashConfig: &capi.RingHashConfig{ diff --git a/control-plane/api/v1alpha1/servicerouter_types.go b/control-plane/api/v1alpha1/servicerouter_types.go index 9f8f7fc3fd..931d5ccb3a 100644 --- a/control-plane/api/v1alpha1/servicerouter_types.go +++ b/control-plane/api/v1alpha1/servicerouter_types.go @@ -137,6 +137,9 @@ type ServiceRouteDestination struct { // This requires that either match.http.pathPrefix or match.http.pathExact // be configured on this route. PrefixRewrite string `json:"prefixRewrite,omitempty"` + // IdleTimeout is total amount of time permitted + // for the request stream to be idle. + IdleTimeout metav1.Duration `json:"idleTimeout,omitempty"` // RequestTimeout is the total amount of time permitted for the entire // downstream request (and retries) to be processed. RequestTimeout metav1.Duration `json:"requestTimeout,omitempty"` @@ -334,6 +337,7 @@ func (in *ServiceRouteDestination) toConsul() *capi.ServiceRouteDestination { Namespace: in.Namespace, Partition: in.Partition, PrefixRewrite: in.PrefixRewrite, + IdleTimeout: in.IdleTimeout.Duration, RequestTimeout: in.RequestTimeout.Duration, NumRetries: in.NumRetries, RetryOnConnectFailure: in.RetryOnConnectFailure, diff --git a/control-plane/api/v1alpha1/servicerouter_types_test.go b/control-plane/api/v1alpha1/servicerouter_types_test.go index eb0568db81..3110922210 100644 --- a/control-plane/api/v1alpha1/servicerouter_types_test.go +++ b/control-plane/api/v1alpha1/servicerouter_types_test.go @@ -79,6 +79,7 @@ func TestServiceRouter_MatchesConsul(t *testing.T) { ServiceSubset: "serviceSubset", Namespace: "namespace", PrefixRewrite: "prefixRewrite", + IdleTimeout: metav1.Duration{Duration: 1 * time.Second}, RequestTimeout: metav1.Duration{Duration: 1 * time.Second}, NumRetries: 1, RetryOnConnectFailure: true, @@ -155,6 +156,7 @@ func TestServiceRouter_MatchesConsul(t *testing.T) { ServiceSubset: "serviceSubset", Namespace: "namespace", PrefixRewrite: "prefixRewrite", + IdleTimeout: 1 * time.Second, RequestTimeout: 1 * time.Second, NumRetries: 1, RetryOnConnectFailure: true, @@ -280,6 +282,7 @@ func TestServiceRouter_ToConsul(t *testing.T) { ServiceSubset: "serviceSubset", Namespace: "namespace", PrefixRewrite: "prefixRewrite", + IdleTimeout: metav1.Duration{Duration: 1 * time.Second}, RequestTimeout: metav1.Duration{Duration: 1 * time.Second}, NumRetries: 1, RetryOnConnectFailure: true, @@ -356,6 +359,7 @@ func TestServiceRouter_ToConsul(t *testing.T) { ServiceSubset: "serviceSubset", Namespace: "namespace", PrefixRewrite: "prefixRewrite", + IdleTimeout: 1 * time.Second, RequestTimeout: 1 * time.Second, NumRetries: 1, RetryOnConnectFailure: true, @@ -714,7 +718,7 @@ func TestServiceRouter_Validate(t *testing.T) { }, namespacesEnabled: false, expectedErrMsgs: []string{ - `servicerouter.consul.hashicorp.com "foo" is invalid: spec.routes[0]: Invalid value: "{\"match\":{\"http\":{}},\"destination\":{\"prefixRewrite\":\"prefixRewrite\",\"requestTimeout\":\"0s\"}}": destination.prefixRewrite requires that either match.http.pathPrefix or match.http.pathExact be configured on this route`, + `servicerouter.consul.hashicorp.com "foo" is invalid: spec.routes[0]: Invalid value: "{\"match\":{\"http\":{}},\"destination\":{\"prefixRewrite\":\"prefixRewrite\",\"idleTimeout\":\"0s\",\"requestTimeout\":\"0s\"}}": destination.prefixRewrite requires that either match.http.pathPrefix or match.http.pathExact be configured on this route`, }, }, "namespaces disabled: single destination namespace specified": { diff --git a/control-plane/api/v1alpha1/zz_generated.deepcopy.go b/control-plane/api/v1alpha1/zz_generated.deepcopy.go index d12db29d14..6436936b27 100644 --- a/control-plane/api/v1alpha1/zz_generated.deepcopy.go +++ b/control-plane/api/v1alpha1/zz_generated.deepcopy.go @@ -7,6 +7,7 @@ package v1alpha1 import ( "encoding/json" + "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -904,6 +905,16 @@ func (in *PassiveHealthCheck) DeepCopyInto(out *PassiveHealthCheck) { *out = new(uint32) **out = **in } + if in.MaxEjectionPercent != nil { + in, out := &in.MaxEjectionPercent, &out.MaxEjectionPercent + *out = new(uint32) + **out = **in + } + if in.BaseEjectionTime != nil { + in, out := &in.BaseEjectionTime, &out.BaseEjectionTime + *out = new(v1.Duration) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PassiveHealthCheck. @@ -1712,6 +1723,7 @@ func (in *ServiceResolverSpec) DeepCopyInto(out *ServiceResolverSpec) { } } out.ConnectTimeout = in.ConnectTimeout + out.RequestTimeout = in.RequestTimeout if in.LoadBalancer != nil { in, out := &in.LoadBalancer, &out.LoadBalancer *out = new(LoadBalancer) @@ -1793,6 +1805,7 @@ func (in *ServiceRoute) DeepCopy() *ServiceRoute { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceRouteDestination) DeepCopyInto(out *ServiceRouteDestination) { *out = *in + out.IdleTimeout = in.IdleTimeout out.RequestTimeout = in.RequestTimeout if in.RetryOnStatusCodes != nil { in, out := &in.RetryOnStatusCodes, &out.RetryOnStatusCodes diff --git a/control-plane/build-support/functions/10-util.sh b/control-plane/build-support/functions/10-util.sh index b807d35397..2706c69be8 100644 --- a/control-plane/build-support/functions/10-util.sh +++ b/control-plane/build-support/functions/10-util.sh @@ -1,1057 +1,962 @@ function err { - if test "${COLORIZE}" -eq 1 - then - tput bold - tput setaf 1 - fi - - echo "$@" 1>&2 - - if test "${COLORIZE}" -eq 1 - then - tput sgr0 - fi + if test "${COLORIZE}" -eq 1; then + tput bold + tput setaf 1 + fi + + echo "$@" 1>&2 + + if test "${COLORIZE}" -eq 1; then + tput sgr0 + fi } function status { - if test "${COLORIZE}" -eq 1 - then - tput bold - tput setaf 4 - fi - - echo "$@" - - if test "${COLORIZE}" -eq 1 - then - tput sgr0 - fi + if test "${COLORIZE}" -eq 1; then + tput bold + tput setaf 4 + fi + + echo "$@" + + if test "${COLORIZE}" -eq 1; then + tput sgr0 + fi } function status_stage { - if test "${COLORIZE}" -eq 1 - then - tput bold - tput setaf 2 - fi - - echo "$@" - - if test "${COLORIZE}" -eq 1 - then - tput sgr0 - fi + if test "${COLORIZE}" -eq 1; then + tput bold + tput setaf 2 + fi + + echo "$@" + + if test "${COLORIZE}" -eq 1; then + tput sgr0 + fi } function debug { - if is_set "${BUILD_DEBUG}" - then - if test "${COLORIZE}" -eq 1 - then - tput setaf 6 - fi - echo "$@" - if test "${COLORIZE}" -eq 1 - then - tput sgr0 - fi - fi + if is_set "${BUILD_DEBUG}"; then + if test "${COLORIZE}" -eq 1; then + tput setaf 6 + fi + echo "$@" + if test "${COLORIZE}" -eq 1; then + tput sgr0 + fi + fi } function sed_i { - if test "$(uname)" == "Darwin" - then - sed -i '' "$@" - return $? - else - sed -i "$@" - return $? - fi + if test "$(uname)" == "Darwin"; then + sed -i '' "$@" + return $? + else + sed -i "$@" + return $? + fi } function is_set { - # Arguments: - # $1 - string value to check its truthiness - # - # Return: - # 0 - is truthy (backwards I know but allows syntax like `if is_set ` to work) - # 1 - is not truthy - - local val=$(tr '[:upper:]' '[:lower:]' <<< "$1") - case $val in - 1 | t | true | y | yes) - return 0 - ;; - *) - return 1 - ;; - esac + # Arguments: + # $1 - string value to check its truthiness + # + # Return: + # 0 - is truthy (backwards I know but allows syntax like `if is_set ` to work) + # 1 - is not truthy + + local val=$(tr '[:upper:]' '[:lower:]' <<<"$1") + case $val in + 1 | t | true | y | yes) + return 0 + ;; + *) + return 1 + ;; + esac } function have_gpg_key { - # Arguments: - # $1 - GPG Key id to check if we have installed - # - # Return: - # 0 - success (we can use this key for signing) - # * - failure (key cannot be used) - - gpg --list-secret-keys $1 > /dev/null 2>&1 - return $? + # Arguments: + # $1 - GPG Key id to check if we have installed + # + # Return: + # 0 - success (we can use this key for signing) + # * - failure (key cannot be used) + + gpg --list-secret-keys $1 >/dev/null 2>&1 + return $? } function parse_version { - # Arguments: - # $1 - Path to the top level Consul source - # $2 - boolean value for whether the release version should be parsed from the source - # $3 - boolean whether to use GIT_DESCRIBE and GIT_COMMIT environment variables - # $4 - boolean whether to omit the version part of the version string. (optional) - # - # Return: - # 0 - success (will write the version to stdout) - # * - error (no version output) - # - # Notes: - # If the GOTAGS environment variable is present then it is used to determine which - # version file to use for parsing. - - local vfile="${1}/version/version.go" - - # ensure the version file exists - if ! test -f "${vfile}" - then - err "Error - File not found: ${vfile}" - return 1 - fi - - local include_release="$2" - local use_git_env="$3" - local omit_version="$4" - - local git_version="" - local git_commit="" - - if test -z "${include_release}" - then - include_release=true - fi - - if test -z "${use_git_env}" - then - use_git_env=true - fi - - if is_set "${use_git_env}" - then - git_version="${GIT_DESCRIBE}" - git_commit="${GIT_COMMIT}" - fi - - # Get the main version out of the source file - version_main=$(awk '$1 == "Version" && $2 == "=" { gsub(/"/, "", $3); print $3 }' < ${vfile}) - release_main=$(awk '$1 == "VersionPrerelease" && $2 == "=" { gsub(/"/, "", $3); print $3 }' < ${vfile}) - - - # try to determine the version if we have build tags - for tag in "$GOTAGS" - do - for vfile in $(find "${1}/version" -name "version_*.go" 2> /dev/null| sort) - do - if grep -q "// +build $tag" "${vfile}" - then - version_main=$(awk '$1 == "Version" && $2 == "=" { gsub(/"/, "", $3); print $3 }' < ${vfile}) - release_main=$(awk '$1 == "VersionPrerelease" && $2 == "=" { gsub(/"/, "", $3); print $3 }' < ${vfile}) - fi - done - done - - local version="${version_main}" - # override the version from source with the value of the GIT_DESCRIBE env var if present - if test -n "${git_version}" - then - version="${git_version}" - fi - - local rel_ver="" - if is_set "${include_release}" - then - # Default to pre-release from the source - rel_ver="${release_main}" - - # When no GIT_DESCRIBE env var is present and no release is in the source then we - # are definitely in dev mode - if test -z "${git_version}" -a -z "${rel_ver}" && is_set "${use_git_env}" - then - rel_ver="dev" - fi - - # Add the release to the version - if test -n "${rel_ver}" -a -n "${git_commit}" - then - rel_ver="${rel_ver} (${git_commit})" - fi - fi - - if test -n "${rel_ver}" - then - if is_set "${omit_version}" - then - echo "${rel_ver}" | tr -d "'" - else - echo "${version}-${rel_ver}" | tr -d "'" - fi - return 0 - elif ! is_set "${omit_version}" - then - echo "${version}" | tr -d "'" - return 0 - else - return 1 - fi + # Arguments: + # $1 - Path to the top level Consul source + # $2 - boolean value for whether the release version should be parsed from the source + # $3 - boolean whether to use GIT_DESCRIBE and GIT_COMMIT environment variables + # $4 - boolean whether to omit the version part of the version string. (optional) + # + # Return: + # 0 - success (will write the version to stdout) + # * - error (no version output) + # + # Notes: + # If the GOTAGS environment variable is present then it is used to determine which + # version file to use for parsing. + + local vfile="${1}/version/version.go" + + # ensure the version file exists + if ! test -f "${vfile}"; then + err "Error - File not found: ${vfile}" + return 1 + fi + + local include_release="$2" + local use_git_env="$3" + local omit_version="$4" + + local git_version="" + local git_commit="" + + if test -z "${include_release}"; then + include_release=true + fi + + if test -z "${use_git_env}"; then + use_git_env=true + fi + + if is_set "${use_git_env}"; then + git_version="${GIT_DESCRIBE}" + git_commit="${GIT_COMMIT}" + fi + + # Get the main version out of the source file + version_main=$(awk '$1 == "Version" && $2 == "=" { gsub(/"/, "", $3); print $3 }' <${vfile}) + release_main=$(awk '$1 == "VersionPrerelease" && $2 == "=" { gsub(/"/, "", $3); print $3 }' <${vfile}) + + # try to determine the version if we have build tags + for tag in "$GOTAGS"; do + for vfile in $(find "${1}/version" -name "version_*.go" 2>/dev/null | sort); do + if grep -q "// +build $tag" "${vfile}"; then + version_main=$(awk '$1 == "Version" && $2 == "=" { gsub(/"/, "", $3); print $3 }' <${vfile}) + release_main=$(awk '$1 == "VersionPrerelease" && $2 == "=" { gsub(/"/, "", $3); print $3 }' <${vfile}) + fi + done + done + + local version="${version_main}" + # override the version from source with the value of the GIT_DESCRIBE env var if present + if test -n "${git_version}"; then + version="${git_version}" + fi + + local rel_ver="" + if is_set "${include_release}"; then + # Default to pre-release from the source + rel_ver="${release_main}" + + # When no GIT_DESCRIBE env var is present and no release is in the source then we + # are definitely in dev mode + if test -z "${git_version}" -a -z "${rel_ver}" && is_set "${use_git_env}"; then + rel_ver="dev" + fi + + # Add the release to the version + if test -n "${rel_ver}" -a -n "${git_commit}"; then + rel_ver="${rel_ver} (${git_commit})" + fi + fi + + if test -n "${rel_ver}"; then + if is_set "${omit_version}"; then + echo "${rel_ver}" | tr -d "'" + else + echo "${version}-${rel_ver}" | tr -d "'" + fi + return 0 + elif ! is_set "${omit_version}"; then + echo "${version}" | tr -d "'" + return 0 + else + return 1 + fi } function get_version { - # Arguments: - # $1 - Path to the top level Consul source - # $2 - Whether the release version should be parsed from source (optional) - # $3 - Whether to use GIT_DESCRIBE and GIT_COMMIT environment variables - # - # Returns: - # 0 - success (the version is also echoed to stdout) - # 1 - error - # - # Notes: - # If a VERSION environment variable is present it will override any parsing of the version from the source - # In addition to processing the main version.go, version_*.go files will be processed if they have - # a Go build tag that matches the one in the GOTAGS environment variable. This tag processing is - # primitive though and will not match complex build tags in the files with negation etc. - - local vers="$VERSION" - if test -z "$vers" - then - # parse the OSS version from version.go - vers="$(parse_version ${1} ${2} ${3})" - fi - - if test -z "$vers" - then - return 1 - else - echo $vers - return 0 - fi + # Arguments: + # $1 - Path to the top level Consul source + # $2 - Whether the release version should be parsed from source (optional) + # $3 - Whether to use GIT_DESCRIBE and GIT_COMMIT environment variables + # + # Returns: + # 0 - success (the version is also echoed to stdout) + # 1 - error + # + # Notes: + # If a VERSION environment variable is present it will override any parsing of the version from the source + # In addition to processing the main version.go, version_*.go files will be processed if they have + # a Go build tag that matches the one in the GOTAGS environment variable. This tag processing is + # primitive though and will not match complex build tags in the files with negation etc. + + local vers="$VERSION" + if test -z "$vers"; then + # parse the OSS version from version.go + vers="$(parse_version ${1} ${2} ${3})" + fi + + if test -z "$vers"; then + return 1 + else + echo $vers + return 0 + fi } function git_branch { - # Arguments: - # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) - # - # Returns: - # 0 - success - # * - failure - # - # Notes: - # Echos the current branch to stdout when successful - - local gdir="$(pwd)" - if test -d "$1" - then - gdir="$1" - fi - - pushd "${gdir}" > /dev/null - - local ret=0 - local head="$(git status -b --porcelain=v2 | awk '{if ($1 == "#" && $2 =="branch.head") { print $3 }}')" || ret=1 - - popd > /dev/null - - test ${ret} -eq 0 && echo "$head" - return ${ret} + # Arguments: + # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) + # + # Returns: + # 0 - success + # * - failure + # + # Notes: + # Echos the current branch to stdout when successful + + local gdir="$(pwd)" + if test -d "$1"; then + gdir="$1" + fi + + pushd "${gdir}" >/dev/null + + local ret=0 + local head="$(git status -b --porcelain=v2 | awk '{if ($1 == "#" && $2 =="branch.head") { print $3 }}')" || ret=1 + + popd >/dev/null + + test ${ret} -eq 0 && echo "$head" + return ${ret} } function git_upstream { - # Arguments: - # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) - # - # Returns: - # 0 - success - # * - failure - # - # Notes: - # Echos the current upstream branch to stdout when successful - - local gdir="$(pwd)" - if test -d "$1" - then - gdir="$1" - fi - - pushd "${gdir}" > /dev/null - - local ret=0 - local head="$(git status -b --porcelain=v2 | awk '{if ($1 == "#" && $2 =="branch.upstream") { print $3 }}')" || ret=1 - - popd > /dev/null - - test ${ret} -eq 0 && echo "$head" - return ${ret} + # Arguments: + # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) + # + # Returns: + # 0 - success + # * - failure + # + # Notes: + # Echos the current upstream branch to stdout when successful + + local gdir="$(pwd)" + if test -d "$1"; then + gdir="$1" + fi + + pushd "${gdir}" >/dev/null + + local ret=0 + local head="$(git status -b --porcelain=v2 | awk '{if ($1 == "#" && $2 =="branch.upstream") { print $3 }}')" || ret=1 + + popd >/dev/null + + test ${ret} -eq 0 && echo "$head" + return ${ret} } function git_log_summary { - # Arguments: - # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) - # - # Returns: - # 0 - success - # * - failure - # - - local gdir="$(pwd)" - if test -d "$1" - then - gdir="$1" - fi - - pushd "${gdir}" > /dev/null - - local ret=0 - - local head=$(git_branch) || ret=1 - local upstream=$(git_upstream) || ret=1 - local rev_range="${head}...${upstream}" - - if test ${ret} -eq 0 - then - status "Git Changes:" - git log --pretty=oneline ${rev_range} || ret=1 - - fi - return $ret + # Arguments: + # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) + # + # Returns: + # 0 - success + # * - failure + # + + local gdir="$(pwd)" + if test -d "$1"; then + gdir="$1" + fi + + pushd "${gdir}" >/dev/null + + local ret=0 + + local head=$(git_branch) || ret=1 + local upstream=$(git_upstream) || ret=1 + local rev_range="${head}...${upstream}" + + if test ${ret} -eq 0; then + status "Git Changes:" + git log --pretty=oneline ${rev_range} || ret=1 + + fi + return $ret } function git_diff { - # Arguments: - # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) - # $2 .. $N - Optional path specification - # - # Returns: - # 0 - success - # * - failure - # - - local gdir="$(pwd)" - if test -d "$1" - then - gdir="$1" - fi - - shift - - pushd "${gdir}" > /dev/null - - local ret=0 - - local head=$(git_branch) || ret=1 - local upstream=$(git_upstream) || ret=1 - - if test ${ret} -eq 0 - then - status "Git Diff - Paths: $@" - git diff ${HEAD} ${upstream} -- "$@" || ret=1 - fi - return $ret + # Arguments: + # $1 - Path to the git repo (optional - assumes pwd is git repo otherwise) + # $2 .. $N - Optional path specification + # + # Returns: + # 0 - success + # * - failure + # + + local gdir="$(pwd)" + if test -d "$1"; then + gdir="$1" + fi + + shift + + pushd "${gdir}" >/dev/null + + local ret=0 + + local head=$(git_branch) || ret=1 + local upstream=$(git_upstream) || ret=1 + + if test ${ret} -eq 0; then + status "Git Diff - Paths: $@" + git diff ${HEAD} ${upstream} -- "$@" || ret=1 + fi + return $ret } function normalize_git_url { - url="${1#https://}" - url="${url#git@}" - url="${url%.git}" - url="$(sed ${SED_EXT} -e 's/([^\/:]*)[:\/](.*)/\1:\2/' <<< "${url}")" - echo "$url" - return 0 + url="${1#https://}" + url="${url#git@}" + url="${url%.git}" + url="$(sed ${SED_EXT} -e 's/([^\/:]*)[:\/](.*)/\1:\2/' <<<"${url}")" + echo "$url" + return 0 } function git_remote_url { - # Arguments: - # $1 - Path to the top level Consul source - # $2 - Remote name - # - # Returns: - # 0 - success - # * - error - # - # Note: - # The push url for the git remote will be echoed to stdout - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. git_remote_url must be called with the path to the top level source as the first argument'" - return 1 - fi - - if test -z "$2" - then - err "ERROR: git_remote_url must be called with a second argument that is the name of the remote" - return 1 - fi - - local ret=0 - - pushd "$1" > /dev/null - - local url=$(git remote get-url --push $2 2>&1) || ret=1 - - popd > /dev/null - - if test "${ret}" -eq 0 - then - echo "${url}" - return 0 - fi + # Arguments: + # $1 - Path to the top level Consul source + # $2 - Remote name + # + # Returns: + # 0 - success + # * - error + # + # Note: + # The push url for the git remote will be echoed to stdout + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. git_remote_url must be called with the path to the top level source as the first argument'" + return 1 + fi + + if test -z "$2"; then + err "ERROR: git_remote_url must be called with a second argument that is the name of the remote" + return 1 + fi + + local ret=0 + + pushd "$1" >/dev/null + + local url=$(git remote get-url --push $2 2>&1) || ret=1 + + popd >/dev/null + + if test "${ret}" -eq 0; then + echo "${url}" + return 0 + fi } function find_git_remote { - # Arguments: - # $1 - Path to the top level Consul source - # - # Returns: - # 0 - success - # * - error - # - # Note: - # The remote name to use for publishing will be echoed to stdout upon success - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. find_git_remote must be called with the path to the top level source as the first argument'" - return 1 - fi - - need_url=$(normalize_git_url "${PUBLISH_GIT_HOST}:${PUBLISH_GIT_REPO}") - debug "Required normalized remote: ${need_url}" - - pushd "$1" > /dev/null - - local ret=1 - for remote in $(git remote) - do - url=$(git remote get-url --push ${remote}) || continue - url=$(normalize_git_url "${url}") - - debug "Testing Remote: ${remote}: ${url}" - if test "${url}" == "${need_url}" - then - echo "${remote}" - ret=0 - break - fi - done - - popd > /dev/null - return ${ret} + # Arguments: + # $1 - Path to the top level Consul source + # + # Returns: + # 0 - success + # * - error + # + # Note: + # The remote name to use for publishing will be echoed to stdout upon success + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. find_git_remote must be called with the path to the top level source as the first argument'" + return 1 + fi + + need_url=$(normalize_git_url "${PUBLISH_GIT_HOST}:${PUBLISH_GIT_REPO}") + debug "Required normalized remote: ${need_url}" + + pushd "$1" >/dev/null + + local ret=1 + for remote in $(git remote); do + url=$(git remote get-url --push ${remote}) || continue + url=$(normalize_git_url "${url}") + + debug "Testing Remote: ${remote}: ${url}" + if test "${url}" == "${need_url}"; then + echo "${remote}" + ret=0 + break + fi + done + + popd >/dev/null + return ${ret} } function git_remote_not_blacklisted { - # Arguments: - # $1 - path to the repo - # $2 - the remote name - # - # Returns: - # 0 - not blacklisted - # * - blacklisted - return 0 + # Arguments: + # $1 - path to the repo + # $2 - the remote name + # + # Returns: + # 0 - not blacklisted + # * - blacklisted + return 0 } function is_git_clean { - # Arguments: - # $1 - Path to git repo - # $2 - boolean whether the git status should be output when not clean - # - # Returns: - # 0 - success - # * - error - # - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. is_git_clean must be called with the path to a git repo as the first argument'" - return 1 - fi - - local output_status="$2" - - pushd "${1}" > /dev/null - - local ret=0 - test -z "$(git status --porcelain=v2 2> /dev/null)" || ret=1 - - if is_set "${output_status}" && test "$ret" -ne 0 - then - err "Git repo is not clean" - # --porcelain=v1 is the same as --short except uncolorized - git status --porcelain=v1 - fi - popd > /dev/null - return ${ret} + # Arguments: + # $1 - Path to git repo + # $2 - boolean whether the git status should be output when not clean + # + # Returns: + # 0 - success + # * - error + # + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. is_git_clean must be called with the path to a git repo as the first argument'" + return 1 + fi + + local output_status="$2" + + pushd "${1}" >/dev/null + + local ret=0 + test -z "$(git status --porcelain=v2 2>/dev/null)" || ret=1 + + if is_set "${output_status}" && test "$ret" -ne 0; then + err "Git repo is not clean" + # --porcelain=v1 is the same as --short except uncolorized + git status --porcelain=v1 + fi + popd >/dev/null + return ${ret} } function update_git_env { - # Arguments: - # $1 - Path to git repo - # - # Returns: - # 0 - success - # * - error - # - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. is_git_clean must be called with the path to a git repo as the first argument'" - return 1 - fi - - export GIT_COMMIT=$(git rev-parse --short HEAD) - export GIT_DIRTY=$(test -n "$(git status --porcelain)" && echo "+CHANGES") - export GIT_DESCRIBE=$(git describe --tags --always) - export GIT_IMPORT=github.com/hashicorp/consul-k8s/version - export GOLDFLAGS="-X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}${GIT_DIRTY} -X ${GIT_IMPORT}.GitDescribe=${GIT_DESCRIBE}" - return 0 + # Arguments: + # $1 - Path to git repo + # + # Returns: + # 0 - success + # * - error + # + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. is_git_clean must be called with the path to a git repo as the first argument'" + return 1 + fi + + export GIT_COMMIT=$(git rev-parse --short HEAD) + export GIT_DIRTY=$(test -n "$(git status --porcelain)" && echo "+CHANGES") + export GIT_DESCRIBE=$(git describe --tags --always) + export GIT_IMPORT=github.com/hashicorp/consul-k8s/version + export GOLDFLAGS="-X ${GIT_IMPORT}.GitCommit=${GIT_COMMIT}${GIT_DIRTY} -X ${GIT_IMPORT}.GitDescribe=${GIT_DESCRIBE}" + return 0 } function git_push_ref { - # Arguments: - # $1 - Path to the top level Consul source - # $2 - Git ref (optional) - # $3 - remote (optional - if not specified we will try to determine it) - # - # Returns: - # 0 - success - # * - error - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. push_git_release must be called with the path to the top level source as the first argument'" - return 1 - fi - - local sdir="$1" - local ret=0 - local remote="$3" - - # find the correct remote corresponding to the desired repo (basically prevent pushing enterprise to oss or oss to enterprise) - if test -z "${remote}" - then - local remote=$(find_git_remote "${sdir}") || return 1 - status "Using git remote: ${remote}" - fi - - local ref="" - - pushd "${sdir}" > /dev/null - - if test -z "$2" - then - # If no git ref was provided we lookup the current local branch and its tracking branch - # It must have a tracking upstream and it must be tracking the sanctioned git remote - local head=$(git_branch "${sdir}") || return 1 - local upstream=$(git_upstream "${sdir}") || return 1 - - # upstream branch for this branch does not track the remote we need to push to - # basically this checks that the upstream (could be something like origin/main) references the correct remote - # if it doesn't then the string modification wont apply and the var will reamin unchanged and equal to itself. - if test "${upstream#${remote}/}" == "${upstream}" - then - err "ERROR: Upstream branch '${upstream}' does not track the correct remote '${remote}' - cannot push" - ret=1 - fi - ref="refs/heads/${head}" - else - # A git ref was provided - get the full ref and make sure it isn't ambiguous and also to - # be able to determine whether its a branch or tag we are pushing - ref_out=$(git rev-parse --symbolic-full-name "$2" --) - - # -ne 2 because it should have the ref on one line followed by a line with '--' - if test "$(wc -l <<< "${ref_out}")" -ne 2 - then - err "ERROR: Git ref '$2' is ambiguous" - debug "${ref_out}" - ret=1 - else - ref=$(head -n 1 <<< "${ref_out}") - fi - fi - - if test ${ret} -eq 0 - then - case "${ref}" in - refs/tags/*) - status "Pushing tag ${ref#refs/tags/} to ${remote}" - ;; - refs/heads/*) - status "Pushing local branch ${ref#refs/tags/} to ${remote}" - ;; - *) - err "ERROR: git_push_ref func is refusing to push ref that isn't a branch or tag" - return 1 - esac - - if ! git push "${remote}" "${ref}" - then - err "ERROR: Failed to push ${ref} to remote: ${remote}" - ret=1 - fi - fi - - popd > /dev/null - - return $ret + # Arguments: + # $1 - Path to the top level Consul source + # $2 - Git ref (optional) + # $3 - remote (optional - if not specified we will try to determine it) + # + # Returns: + # 0 - success + # * - error + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. push_git_release must be called with the path to the top level source as the first argument'" + return 1 + fi + + local sdir="$1" + local ret=0 + local remote="$3" + + # find the correct remote corresponding to the desired repo (basically prevent pushing enterprise to oss or oss to enterprise) + if test -z "${remote}"; then + local remote=$(find_git_remote "${sdir}") || return 1 + status "Using git remote: ${remote}" + fi + + local ref="" + + pushd "${sdir}" >/dev/null + + if test -z "$2"; then + # If no git ref was provided we lookup the current local branch and its tracking branch + # It must have a tracking upstream and it must be tracking the sanctioned git remote + local head=$(git_branch "${sdir}") || return 1 + local upstream=$(git_upstream "${sdir}") || return 1 + + # upstream branch for this branch does not track the remote we need to push to + # basically this checks that the upstream (could be something like origin/main) references the correct remote + # if it doesn't then the string modification wont apply and the var will reamin unchanged and equal to itself. + if test "${upstream#${remote}/}" == "${upstream}"; then + err "ERROR: Upstream branch '${upstream}' does not track the correct remote '${remote}' - cannot push" + ret=1 + fi + ref="refs/heads/${head}" + else + # A git ref was provided - get the full ref and make sure it isn't ambiguous and also to + # be able to determine whether its a branch or tag we are pushing + ref_out=$(git rev-parse --symbolic-full-name "$2" --) + + # -ne 2 because it should have the ref on one line followed by a line with '--' + if test "$(wc -l <<<"${ref_out}")" -ne 2; then + err "ERROR: Git ref '$2' is ambiguous" + debug "${ref_out}" + ret=1 + else + ref=$(head -n 1 <<<"${ref_out}") + fi + fi + + if test ${ret} -eq 0; then + case "${ref}" in + refs/tags/*) + status "Pushing tag ${ref#refs/tags/} to ${remote}" + ;; + refs/heads/*) + status "Pushing local branch ${ref#refs/tags/} to ${remote}" + ;; + *) + err "ERROR: git_push_ref func is refusing to push ref that isn't a branch or tag" + return 1 + ;; + esac + + if ! git push "${remote}" "${ref}"; then + err "ERROR: Failed to push ${ref} to remote: ${remote}" + ret=1 + fi + fi + + popd >/dev/null + + return $ret } function update_version { - # Arguments: - # $1 - Path to the version file - # $2 - Version string - # $3 - PreRelease version (if unset will become an empty string) - # - # Returns: - # 0 - success - # * - error - - if ! test -f "$1" - then - err "ERROR: '$1' is not a regular file. update_version must be called with the path to a go version file" - return 1 - fi - - if test -z "$2" - then - err "ERROR: The version specified was empty" - return 1 - fi - - local vfile="$1" - local version="$2" - local prerelease="$3" - - sed_i ${SED_EXT} -e "s/(Version[[:space:]]*=[[:space:]]*)\"[^\"]*\"/\1\"${version}\"/g" -e "s/(VersionPrerelease[[:space:]]*=[[:space:]]*)\"[^\"]*\"/\1\"${prerelease}\"/g" "${vfile}" - return $? -} - -function update_version_helm { - # Arguments: - # $1 - Path to the directory where the root of the Helm chart is - # $2 - Version string - # $3 - PreRelease version (if unset will become an empty string) - # $4 - Image base path - # - # Returns: - # 0 - success - # * - error - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. update_version_helm must be called with the path to the Helm chart" - return 1 - fi - - if test -z "$2" - then - err "ERROR: The version specified was empty" - return 1 - fi - - local vfile="$1/values.yaml" - local cfile="$1/Chart.yaml" - local version="$2" - local prerelease="$3" - local full_version="$2" - if ! test -z "$3" - then - full_version="$2-$3" - fi - - sed_i ${SED_EXT} -e "s/(imageK8S:.*\/consul-k8s-control-plane:)[^\"]*/imageK8S: $4${full_version}/g" "${vfile}" - sed_i ${SED_EXT} -e "s/(version:[[:space:]]*)[^\"]*/\1${full_version}/g" "${cfile}" - sed_i ${SED_EXT} -e "s/(image:.*\/consul-k8s-control-plane:)[^\"]*/image: $4${full_version}/g" "${cfile}" - - if test -z "$3" - then - sed_i ${SED_EXT} -e "s/(artifacthub.io\/prerelease:[[:space:]]*)[^\"]*/\1false/g" "${cfile}" - else - sed_i ${SED_EXT} -e "s/(artifacthub.io\/prerelease:[[:space:]]*)[^\"]*/\1true/g" "${cfile}" - fi - return $? -} - -function set_changelog_version { - # Arguments: - # $1 - Path to top level Consul source - # $2 - Version to put into the Changelog - # $3 - Release Date - # - # Returns: - # 0 - success - # * - error - - local changelog="${1}/CHANGELOG.md" - local version="$2" - local rel_date="$3" - - if ! test -f "${changelog}" - then - err "ERROR: File not found: ${changelog}" - return 1 - fi - - if test -z "${version}" - then - err "ERROR: Must specify a version to put into the changelog" - return 1 - fi - - if test -z "${rel_date}" - then - rel_date=$(date +"%B %d, %Y") - fi - - sed_i ${SED_EXT} -e "s/## UNRELEASED/## ${version} (${rel_date})/" "${changelog}" - return $? -} + # Arguments: + # $1 - Path to the version file + # $2 - Version string + # $3 - PreRelease version (if unset will become an empty string) + # + # Returns: + # 0 - success + # * - error -function unset_changelog_version { - # Arguments: - # $1 - Path to top level Consul source - # - # Returns: - # 0 - success - # * - error + if ! test -f "$1"; then + err "ERROR: '$1' is not a regular file. update_version must be called with the path to a go version file" + return 1 + fi - local changelog="${1}/CHANGELOG.md" + if test -z "$2"; then + err "ERROR: The version specified was empty" + return 1 + fi - if ! test -f "${changelog}" - then - err "ERROR: File not found: ${changelog}" - return 1 - fi + local vfile="$1" + local version="$2" + local prerelease="$3" - sed_i ${SED_EXT} -e "1 s/^## [0-9]+\.[0-9]+\.[0-9]+ \([^)]*\)/## UNRELEASED/" "${changelog}" - return $? + sed_i ${SED_EXT} -e "s/(Version[[:space:]]*=[[:space:]]*)\"[^\"]*\"/\1\"${version}\"/g" -e "s/(VersionPrerelease[[:space:]]*=[[:space:]]*)\"[^\"]*\"/\1\"${prerelease}\"/g" "${vfile}" + return $? } -function add_unreleased_to_changelog { - # Arguments: - # $1 - Path to top level Consul source - # - # Returns: - # 0 - success - # * - error - - local changelog="${1}/CHANGELOG.md" - - if ! test -f "${changelog}" - then - err "ERROR: File not found: ${changelog}" - return 1 - fi - - # Check if we are already in unreleased mode - if head -n 1 "${changelog}" | grep -q -c UNRELEASED - then - return 0 - fi - - local tfile="$(mktemp) -t "CHANGELOG.md_")" - ( - echo -e "## UNRELEASED\n" > "${tfile}" && - cat "${changelog}" >> "${tfile}" && - cp "${tfile}" "${changelog}" - ) - local ret=$? - rm "${tfile}" - return $ret +function update_version_helm { + # Arguments: + # $1 - Path to the directory where the root of the Helm chart is + # $2 - Version string + # $3 - Release version (if unset will become an empty string) + # $4 - Image base path + # $5 - Consul version string + # $6 - Consul image base path + # $7 - Consul-Dataplane version string + # $8 - Consul-Dataplane base path + # + # Returns: + # 0 - success + # * - error + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. update_version_helm must be called with the path to the Helm chart" + return 1 + fi + + if test -z "$2"; then + err "ERROR: The version specified was empty" + return 1 + fi + + local vfile="$1/values.yaml" + local cfile="$1/Chart.yaml" + local version="$2" + local consul_version="$5" + local prerelease="$3" + local full_version="$2" + local full_consul_version="$5" + local full_consul_dataplane_version="$7" + local consul_dataplane_base_path="$8" + if ! test -z "$3" && test "$3" != "dev"; then + full_version="$2-$3" + full_consul_version="$5-$3" + full_consul_dataplane_version="$7-$3" + elif test "$3" == "dev"; then + full_version="$2-$3" + # strip off the last minor patch version so that the consul image can be set to something like 1.16-dev. The image + # is produced by Consul every night + full_consul_version="${5%.*}-$3" + full_consul_dataplane_version="${7%.*}-$3" + fi + + sed_i ${SED_EXT} -e "s/(imageK8S:.*\/consul-k8s-control-plane:)[^\"]*/imageK8S: $4${full_version}/g" "${vfile}" + sed_i ${SED_EXT} -e "s/(version:[[:space:]]*)[^\"]*/\1${full_version}/g" "${cfile}" + sed_i ${SED_EXT} -e "s/(appVersion:[[:space:]]*)[^\"]*/\1${full_consul_version}/g" "${cfile}" + sed_i ${SED_EXT} -e "s/(image:.*\/consul-k8s-control-plane:)[^\"]*/image: $4${full_version}/g" "${cfile}" + + sed_i ${SED_EXT} -e "s,^( *image:)(.*/consul:)[^\"]*\$,\1 $6:${full_consul_version},g" ${cfile} + sed_i ${SED_EXT} -e "s,^( *image:)(.*/consul:)[^\"]*\$,\1 $6:${full_consul_version},g" ${vfile} + sed_i ${SED_EXT} -e "s,^( *image:)(.*/consul-enterprise:)[^\"]*\$,\1 $6:${full_consul_version},g" ${cfile} + sed_i ${SED_EXT} -e "s,^( *image:)(.*/consul-enterprise:)[^\"]*\$,\1 $6:${full_consul_version},g" ${vfile} + + sed_i ${SED_EXT} -e "s/(imageConsulDataplane:.*\/consul-dataplane:)[^\"]*/imageConsulDataplane: ${consul_dataplane_base_path}:${full_consul_dataplane_version}/g" "${vfile}" + sed_i ${SED_EXT} -e "s,^( *image:)(.*/consul-dataplane:)[^\"]*\$,\1 ${consul_dataplane_base_path}:${full_consul_dataplane_version},g" ${cfile} + + if test -z "$3"; then + sed_i ${SED_EXT} -e "s/(artifacthub.io\/prerelease:[[:space:]]*)[^\"]*/\1false/g" "${cfile}" + else + sed_i ${SED_EXT} -e "s/(artifacthub.io\/prerelease:[[:space:]]*)[^\"]*/\1true/g" "${cfile}" + fi + return $? } function set_version { - # Arguments: - # $1 - Path to top level Consul source - # $2 - The version of the release - # $3 - The release date - # $4 - The pre-release version - # $5 - The helm docker image base path - # - # - # Returns: - # 0 - success - # * - error - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. prepare_release must be called with the path to a git repo as the first argument" - return 1 - fi - - if test -z "$2" - then - err "ERROR: The version specified was empty" - return 1 - fi - - local sdir="$1" - local vers="$2" - - status_stage "==> Updating control-plane version/version.go with version info: ${vers} "$4"" - if ! update_version "${sdir}/control-plane/version/version.go" "${vers}" "$4" - then - unset_changelog_version "${sdir}" - return 1 - fi - - status_stage "==> Updating cli version/version.go with version info: ${vers} "$4"" - if ! update_version "${sdir}/cli/version/version.go" "${vers}" "$4" - then - unset_changelog_version "${sdir}" - return 1 - fi - - status_stage "==> Updating Helm chart versions with version info: ${vers} "$4"" - if ! update_version_helm "${sdir}/charts/consul" "${vers}" "$4" "$5" - then - unset_changelog_version "${sdir}" - return 1 - fi - - return 0 + # Arguments: + # $1 - Path to top level Consul source + # $2 - The version of the release + # $3 - The release date + # $4 - The pre-release version + # $5 - The consul-k8s helm docker image base path + # $6 - The consul version + # $7 - The consul helm docker image base path + # $8 - The consul dataplane version + # $9 - The consul-dataplane helm docker image base path + # + # + # Returns: + # 0 - success + # * - error + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. prepare_release must be called with the path to a git repo as the first argument" + return 1 + fi + + if test -z "$2"; then + err "ERROR: The version specified was empty" + return 1 + fi + + local sdir="$1" + local vers="$2" + local consul_vers="$6" + local consul_dataplane_vers="$8" + + status_stage "==> Updating control-plane version/version.go with version info: ${vers} "$4"" + if ! update_version "${sdir}/control-plane/version/version.go" "${vers}" "$4"; then + return 1 + fi + + status_stage "==> Updating cli version/version.go with version info: ${vers} "$4"" + if ! update_version "${sdir}/cli/version/version.go" "${vers}" "$4"; then + return 1 + fi + + status_stage "==> Updating Helm chart version, consul-k8s: ${vers} "$4" consul: ${consul_vers} "$4" consul-dataplane: ${consul_dataplane_vers} "$4"" + if ! update_version_helm "${sdir}/charts/consul" "${vers}" "$4" "$5" "${consul_vers}" "$7" "${consul_dataplane_vers}" "$9"; then + return 1 + fi + + return 0 } function set_changelog { - # Arguments: - # $1 - Path to top level Consul source - # $2 - The version of the release - # $3 - The release date - # $4 - The pre-release version - # - # - # Returns: - # 0 - success - # * - error - local sdir="$1" - local vers="$2" - local rel_date="$(date +"%B %d, %Y")" - if test -n "$3" - then - rel_date="$3" - fi - - local changelog_vers="${vers}" - if test -n "$4" - then - changelog_vers="${vers}-$4" - fi - status_stage "==> Updating CHANGELOG.md with release info: ${changelog_vers} (${rel_date})" - set_changelog_version "${sdir}" "${changelog_vers}" "${rel_date}" || return 1 + # Arguments: + # $1 - Path to top level Consul source + # $2 - Version + # $3 - Release Date + # $4 - The last git release tag + # $5 - Pre-release version + # + # + # Returns: + # 0 - success + # * - error + + # Check if changelog-build is installed + if ! command -v changelog-build &>/dev/null; then + echo "Error: changelog-build is not installed. Please install it and try again." + exit 1 + fi + + local curdir="$1" + local version="$2" + local rel_date="$(date +"%B %d, %Y")" + if test -n "$3"; then + rel_date="$3" + fi + local last_release_date_git_tag=$4 + local preReleaseVersion="-$5" + + if test -z "${version}"; then + err "ERROR: Must specify a version to put into the changelog" + return 1 + fi + + if [ -z "$CONSUL_K8S_LAST_RELEASE_GIT_TAG" ]; then + echo "Error: CONSUL_K8S_LAST_RELEASE_GIT_TAG not specified." + exit 1 + fi + + cat <tmp && mv tmp "${curdir}"/CHANGELOG.MD +## ${version}${preReleaseVersion} (${rel_date}) +$(changelog-build -last-release ${CONSUL_K8S_LAST_RELEASE_GIT_TAG} \ + -entries-dir .changelog/ \ + -changelog-template .changelog/changelog.tmpl \ + -note-template .changelog/note.tmpl \ + -this-release $(git rev-parse HEAD)) + +EOT } function prepare_release { - # Arguments: - # $1 - Path to top level Consul source - # $2 - The version of the release - # $3 - The release date - # $4 - The pre-release version - # - # - # Returns: - # 0 - success - # * - error - echo "release version: " $1 $2 $3 $4 - set_version "$1" "$2" "$3" "$4" "hashicorp\/consul-k8s-control-plane:" - set_changelog "$1" "$2" "$3" "$4" + # Arguments: + # $1 - Path to top level Consul source + # $2 - The version of the release + # $3 - The release date + # $4 - The last release git tag for this branch (eg. v1.1.0) + # $5 - The consul version + # $6 - The consul-dataplane version + # $7 - The pre-release version + # + # + # Returns: + # 0 - success + # * - error + + local curDir=$1 + local version=$2 + local releaseDate=$3 + local lastGitTag=$4 + local consulVersion=$5 + local consulDataplaneVersion=$6 + local prereleaseVersion=$7 + + echo "prepare_release: dir:${curDir} consul-k8s:${version} consul:${consulVersion} consul-dataplane:${consulDataplaneVersion} date:"${releaseDate}" git tag:${lastGitTag}" + set_version "${curDir}" "${version}" "${releaseDate}" "${prereleaseVersion}" "hashicorp\/consul-k8s-control-plane:" "${consulVersion}" "hashicorp\/consul" "${consulDataplaneVersion}" "hashicorp\/consul-dataplane" + set_changelog "${curDir}" "${version}" "${releaseDate}" "${lastGitTag}" "${prereleaseVersion}" } function prepare_dev { - # Arguments: - # $1 - Path to top level Consul source - # $2 - The version of the release - # $3 - The release date - # $4 - The version of the next release - # $5 - The pre-release version (for setting beta in changelog) - # - # Returns: - # 0 - success - # * - error + # Arguments: + # $1 - Path to top level Consul source + # $2 - The version of the release + # $3 - The release date + # $4 - The last release git tag for this branch (eg. v1.1.0) (Unused) + # $5 - The version of the next release + # $6 - The version of the next consul release + # $7 - The next consul-dataplane version + # + # Returns: + # 0 - success + # * - error + + local curDir=$1 + local version=$2 + local releaseDate=$3 + local nextReleaseVersion=$5 + local nextConsulVersion=$6 + local nextConsulDataplaneVersion=$7 + + echo "prepare_dev: dir:${curDir} consul-k8s:${nextReleaseVersion} consul:${nextConsulVersion} date:"${releaseDate}" mode:dev" + set_version "${curDir}" "${nextReleaseVersion}" "${releaseDate}" "dev" "docker.mirror.hashicorp.services\/hashicorppreview\/consul-k8s-control-plane:" "${nextConsulVersion}" "docker.mirror.hashicorp.services\/hashicorppreview\/consul" "${nextConsulDataplaneVersion}" "docker.mirror.hashicorp.services\/hashicorppreview\/consul-dataplane" + + return 0 +} - echo "dev version: " $1 $4 $3 "dev" +function git_staging_empty { + # Arguments: + # $1 - Path to git repo + # + # Returns: + # 0 - success (nothing staged) + # * - error (staged files) - local sdir="$1" + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. commit_dev_mode must be called with the path to a git repo as the first argument'" + return 1 + fi - set_changelog "$1" "$2" "$3" "$5" - set_version "$1" "$4" "$3" "dev" "docker.mirror.hashicorp.services\/hashicorppreview\/consul-k8s-control-plane:" + pushd "$1" >/dev/null - status_stage "==> Adding new UNRELEASED label in CHANGELOG.md" - add_unreleased_to_changelog "${sdir}" || return 1 + declare -i ret=0 - return 0 -} + for status in $(git status --porcelain=v2 | awk '{print $2}' | cut -b 1); do + if test "${status}" != "."; then + ret=1 + break + fi + done -function git_staging_empty { - # Arguments: - # $1 - Path to git repo - # - # Returns: - # 0 - success (nothing staged) - # * - error (staged files) - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. commit_dev_mode must be called with the path to a git repo as the first argument'" - return 1 - fi - - pushd "$1" > /dev/null - - declare -i ret=0 - - for status in $(git status --porcelain=v2 | awk '{print $2}' | cut -b 1) - do - if test "${status}" != "." - then - ret=1 - break - fi - done - - popd > /dev/null - return ${ret} + popd >/dev/null + return ${ret} } function commit_dev_mode { - # Arguments: - # $1 - Path to top level Consul source - # - # Returns: - # 0 - success - # * - error - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory. commit_dev_mode must be called with the path to a git repo as the first argument'" - return 1 - fi - - status "Checking for previously staged files" - git_staging_empty "$1" || return 1 - - declare -i ret=0 - - pushd "$1" > /dev/null - - status "Staging CHANGELOG.md and version_*.go files" - git add CHANGELOG.md && git add */version/version*.go - ret=$? - - if test ${ret} -eq 0 - then - status "Adding Commit" - git commit -m "Putting source back into Dev Mode" - ret=$? - fi - - popd >/dev/null - return ${ret} + # Arguments: + # $1 - Path to top level Consul source + # + # Returns: + # 0 - success + # * - error + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory. commit_dev_mode must be called with the path to a git repo as the first argument'" + return 1 + fi + + status "Checking for previously staged files" + git_staging_empty "$1" || return 1 + + declare -i ret=0 + + pushd "$1" >/dev/null + + status "Staging CHANGELOG.md and version_*.go files" + git add CHANGELOG.md && git add */version/version*.go + ret=$? + + if test ${ret} -eq 0; then + status "Adding Commit" + git commit -m "Putting source back into Dev Mode" + ret=$? + fi + + popd >/dev/null + return ${ret} } function gpg_detach_sign { - # Arguments: - # $1 - File to sign - # $2 - Alternative GPG key to use for signing - # - # Returns: - # 0 - success - # * - failure - - # determine whether the gpg key to use is being overridden - local gpg_key=${HASHICORP_GPG_KEY} - if test -n "$2" - then - gpg_key=$2 - fi - - gpg --default-key "${gpg_key}" --detach-sig --yes -v "$1" - return $? + # Arguments: + # $1 - File to sign + # $2 - Alternative GPG key to use for signing + # + # Returns: + # 0 - success + # * - failure + + # determine whether the gpg key to use is being overridden + local gpg_key=${HASHICORP_GPG_KEY} + if test -n "$2"; then + gpg_key=$2 + fi + + gpg --default-key "${gpg_key}" --detach-sig --yes -v "$1" + return $? } function shasum_directory { - # Arguments: - # $1 - Path to directory containing the files to shasum - # $2 - File to output sha sums to - # - # Returns: - # 0 - success - # * - failure - - if ! test -d "$1" - then - err "ERROR: '$1' is not a directory and shasum_release requires passing a directory as the first argument" - return 1 - fi - - if test -z "$2" - then - err "ERROR: shasum_release requires a second argument to be the filename to output the shasums to but none was given" - return 1 - fi - - pushd $1 > /dev/null - shasum -a256 * > "$2" - ret=$? - popd >/dev/null - - return $ret + # Arguments: + # $1 - Path to directory containing the files to shasum + # $2 - File to output sha sums to + # + # Returns: + # 0 - success + # * - failure + + if ! test -d "$1"; then + err "ERROR: '$1' is not a directory and shasum_release requires passing a directory as the first argument" + return 1 + fi + + if test -z "$2"; then + err "ERROR: shasum_release requires a second argument to be the filename to output the shasums to but none was given" + return 1 + fi + + pushd $1 >/dev/null + shasum -a256 * >"$2" + ret=$? + popd >/dev/null + + return $ret +} + +function ui_version { + # Arguments: + # $1 - path to index.html + # + # Returns: + # 0 - success + # * -failure + # + # Notes: echoes the version to stdout upon success + if ! test -f "$1"; then + err "ERROR: No such file: '$1'" + return 1 + fi + + local ui_version=$(sed -n ${SED_EXT} -e 's/.*CONSUL_K8S_CONSUL_VERSION%22%3A%22([^%]*)%22%2C%22.*/\1/p' <"$1") || return 1 + echo "$ui_version" + return 0 +} +function ui_logo_type { + # Arguments: + # $1 - path to index.html + # + # Returns: + # 0 - success + # * -failure + # + # Notes: echoes the 'logo type' to stdout upon success + # the 'logo' can be one of 'enterprise' or 'oss' + # and doesn't necessarily correspond to the binary type of consul + # the logo is 'enterprise' if the binary type is anything but 'oss' + if ! test -f "$1"; then + err "ERROR: No such file: '$1'" + return 1 + fi + grep -q "data-enterprise-logo" <"$1" + + if test $? -eq 0; then + echo "enterprise" + else + echo "oss" + fi + return 0 } - - function ui_version { - # Arguments: - # $1 - path to index.html - # - # Returns: - # 0 - success - # * -failure - # - # Notes: echoes the version to stdout upon success - if ! test -f "$1" - then - err "ERROR: No such file: '$1'" - return 1 - fi - - local ui_version=$(sed -n ${SED_EXT} -e 's/.*CONSUL_VERSION%22%3A%22([^%]*)%22%2C%22.*/\1/p' < "$1") || return 1 - echo "$ui_version" - return 0 - } - function ui_logo_type { - # Arguments: - # $1 - path to index.html - # - # Returns: - # 0 - success - # * -failure - # - # Notes: echoes the 'logo type' to stdout upon success - # the 'logo' can be one of 'enterprise' or 'oss' - # and doesn't necessarily correspond to the binary type of consul - # the logo is 'enterprise' if the binary type is anything but 'oss' - if ! test -f "$1" - then - err "ERROR: No such file: '$1'" - return 1 - fi - grep -q "data-enterprise-logo" < "$1" - - if test $? -eq 0 - then - echo "enterprise" - else - echo "oss" - fi - return 0 - } diff --git a/control-plane/build-support/scripts/check-hashicorppreview.sh b/control-plane/build-support/scripts/check-hashicorppreview.sh new file mode 100755 index 0000000000..cd694dad93 --- /dev/null +++ b/control-plane/build-support/scripts/check-hashicorppreview.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 +echo "Checking charts for hashicorpreview images. . ." +if grep -rnw -e 'hashicorppreview' './charts'; then + echo Charts contain hashicorppreview images. If this is intended for release, please remove the preview images. +else + echo Charts do not contain hashicorpreview images, ready for release! +fi \ No newline at end of file diff --git a/control-plane/build-support/scripts/consul-dataplane-version.sh b/control-plane/build-support/scripts/consul-dataplane-version.sh new file mode 100755 index 0000000000..906ee54a1f --- /dev/null +++ b/control-plane/build-support/scripts/consul-dataplane-version.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 +FILE=$1 +VERSION=$(yq .global.imageConsulDataplane $FILE) + +echo "${VERSION}" diff --git a/control-plane/build-support/scripts/consul-enterprise-version.sh b/control-plane/build-support/scripts/consul-enterprise-version.sh new file mode 100755 index 0000000000..24adb6a793 --- /dev/null +++ b/control-plane/build-support/scripts/consul-enterprise-version.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 +FILE=$1 +VERSION=$(yq .global.image $FILE) + +if [[ !"${VERSION}" == *"hashicorppreview/consul:"* ]]; then + VERSION=$(echo ${VERSION} | sed "s/consul:/consul-enterprise:/g") +elif [[ !"${VERSION}" == *"hashicorp/consul:"* ]]; then + VERSION=$(echo ${VERSION} | sed "s/consul:/consul-enterprise:/g" | sed "s/$/-ent/g") +fi + + +echo "${VERSION}" diff --git a/control-plane/build-support/scripts/consul-version.sh b/control-plane/build-support/scripts/consul-version.sh new file mode 100755 index 0000000000..faaed33b20 --- /dev/null +++ b/control-plane/build-support/scripts/consul-version.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 +FILE=$1 +VERSION=$(yq .global.image $FILE) + +if [[ "${VERSION}" == *"consul-enterprise:"* ]]; then + VERSION=$(echo ${VERSION} | sed "s/consul-enterprise:/consul:/g") +fi + +echo "${VERSION}" diff --git a/control-plane/build-support/scripts/read-yaml-config.sh b/control-plane/build-support/scripts/read-yaml-config.sh new file mode 100755 index 0000000000..37cfd0cc17 --- /dev/null +++ b/control-plane/build-support/scripts/read-yaml-config.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 +INPUT_FILE=$1 +FIELD=$2 + +VALUE=$(yq $FIELD $INPUT_FILE) + +echo "${VALUE}" diff --git a/control-plane/build-support/scripts/set_test_package_matrix.sh b/control-plane/build-support/scripts/set_test_package_matrix.sh new file mode 100755 index 0000000000..b248cbad07 --- /dev/null +++ b/control-plane/build-support/scripts/set_test_package_matrix.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +INPUT_FILE=$1 + +# convert readable yaml to json for github actions consumption +# do not include any pretty print, print to single line with -I 0 +VALUE=$(yq eval 'select(fileIndex == 0)' "$INPUT_FILE" -o json -I 0) + +echo "$VALUE" \ No newline at end of file diff --git a/control-plane/catalog/to-consul/annotation.go b/control-plane/catalog/to-consul/annotation.go index 5cd6023181..5df5ab71f4 100644 --- a/control-plane/catalog/to-consul/annotation.go +++ b/control-plane/catalog/to-consul/annotation.go @@ -23,4 +23,10 @@ const ( // annotationServiceMetaPrefix is the prefix for setting meta key/value // for a service. The remainder of the key is the meta key. annotationServiceMetaPrefix = "consul.hashicorp.com/service-meta-" + + // annotationServiceWeight is the key of the annotation that determines + // the traffic weight of the service which is spanned over multiple k8s cluster. + // e.g. Service `backend` in k8s cluster `A` receives 25% of the traffic + // compared to same `backend` service in k8s cluster `B`. + annotationServiceWeight = "consul.hashicorp.com/service-weight" ) diff --git a/control-plane/catalog/to-consul/resource.go b/control-plane/catalog/to-consul/resource.go index 09d8aa6c5d..8d6fa9b82b 100644 --- a/control-plane/catalog/to-consul/resource.go +++ b/control-plane/catalog/to-consul/resource.go @@ -14,6 +14,7 @@ import ( consulapi "github.com/hashicorp/consul/api" "github.com/hashicorp/go-hclog" corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" @@ -143,12 +144,33 @@ type ServiceResource struct { // of each service. endpointsMap map[string]*corev1.Endpoints + // EnableIngress enables syncing of the hostname from an Ingress resource + // to the service registration if an Ingress rule matches the service. + EnableIngress bool + + // SyncLoadBalancerIPs enables syncing the IP of the Ingress LoadBalancer + // if we do not want to sync the hostname from the Ingress resource. + SyncLoadBalancerIPs bool + + // ingressServiceMap uses the same keys as serviceMap but maps to the ingress + // of each service if it exists. + ingressServiceMap map[string]map[string]string + + // serviceHostnameMap maps the name of a service to the hostName and port that + // is provided by the Ingress resource for the service. + serviceHostnameMap map[string]serviceAddress + // consulMap holds the services in Consul that we've registered from kube. // It's populated via Consul's API and lets us diff what is actually in // Consul vs. what we expect to be there. consulMap map[string][]*consulapi.CatalogRegistration } +type serviceAddress struct { + hostName string + port int32 +} + // Informer implements the controller.Resource interface. func (t *ServiceResource) Informer() cache.SharedIndexInformer { // Watch all k8s namespaces. Events will be filtered out as appropriate @@ -253,9 +275,21 @@ func (t *ServiceResource) doDelete(key string) { // Run implements the controller.Backgrounder interface. func (t *ServiceResource) Run(ch <-chan struct{}) { t.Log.Info("starting runner for endpoints") + // Register a controller for Endpoints which subsequently registers a + // controller for the Ingress resource. (&controller.Controller{ - Log: t.Log.Named("controller/endpoints"), - Resource: &serviceEndpointsResource{Service: t, Ctx: t.Ctx}, + Resource: &serviceEndpointsResource{ + Service: t, + Ctx: t.Ctx, + Log: t.Log.Named("controller/endpoints"), + Resource: &serviceIngressResource{ + Service: t, + Ctx: t.Ctx, + SyncLoadBalancerIPs: t.SyncLoadBalancerIPs, + EnableIngress: t.EnableIngress, + }, + }, + Log: t.Log.Named("controller/service"), }).Run(ch) } @@ -474,6 +508,19 @@ func (t *ServiceResource) generateRegistrations(key string) { r.Service = &rs r.Service.ID = serviceID(r.Service.Service, ip) r.Service.Address = ip + // Adding information about service weight. + // Overrides the existing weight if present + if weight, ok := svc.Annotations[annotationServiceWeight]; ok && weight != "" { + weightI, err := getServiceWeight(weight) + if err == nil { + r.Service.Weights = consulapi.AgentWeights{ + Passing: weightI, + } + } else { + t.Log.Debug("[generateRegistrations] service weight err: ", err) + } + } + t.consulMap[key] = append(t.consulMap[key], &r) } @@ -510,6 +557,19 @@ func (t *ServiceResource) generateRegistrations(key string) { r.Service.ID = serviceID(r.Service.Service, addr) r.Service.Address = addr + // Adding information about service weight. + // Overrides the existing weight if present + if weight, ok := svc.Annotations[annotationServiceWeight]; ok && weight != "" { + weightI, err := getServiceWeight(weight) + if err == nil { + r.Service.Weights = consulapi.AgentWeights{ + Passing: weightI, + } + } else { + t.Log.Debug("[generateRegistrations] service weight err: ", err) + } + } + t.consulMap[key] = append(t.consulMap[key], &r) } } @@ -644,12 +704,21 @@ func (t *ServiceResource) registerServiceInstance( } } for _, subsetAddr := range subset.Addresses { - addr := subsetAddr.IP - if addr == "" && useHostname { - addr = subsetAddr.Hostname - } - if addr == "" { - continue + var addr string + // Use the address and port from the Ingress resource if + // ingress-sync is enabled and the service has an ingress + // resource that references it. + if t.EnableIngress && t.isIngressService(key) { + addr = t.serviceHostnameMap[key].hostName + epPort = int(t.serviceHostnameMap[key].port) + } else { + addr = subsetAddr.IP + if addr == "" && useHostname { + addr = subsetAddr.Hostname + } + if addr == "" { + continue + } } // Its not clear whether K8S guarantees ready addresses to @@ -716,8 +785,19 @@ func (t *ServiceResource) sync() { // a background watcher on endpoints that is used by the ServiceResource // to keep track of changing endpoints for registered services. type serviceEndpointsResource struct { - Service *ServiceResource - Ctx context.Context + Service *ServiceResource + Ctx context.Context + Log hclog.Logger + Resource controller.Resource +} + +// Run implements the controller.Backgrounder interface. +func (t *serviceEndpointsResource) Run(ch <-chan struct{}) { + t.Log.Info("starting runner for ingress") + (&controller.Controller{ + Log: t.Log.Named("controller/ingress"), + Resource: t.Resource, + }).Run(ch) } func (t *serviceEndpointsResource) Informer() cache.SharedIndexInformer { @@ -793,6 +873,134 @@ func (t *serviceEndpointsResource) Delete(key string, _ interface{}) error { return nil } +// serviceIngressResource implements controller.Resource and starts +// a background watcher on ingress resources that is used by the ServiceResource +// to keep track of changing ingress for registered services. +type serviceIngressResource struct { + Service *ServiceResource + Resource controller.Resource + Ctx context.Context + EnableIngress bool + SyncLoadBalancerIPs bool +} + +func (t *serviceIngressResource) Informer() cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + return t.Service.Client.NetworkingV1(). + Ingresses(metav1.NamespaceAll). + List(t.Ctx, options) + }, + + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + return t.Service.Client.NetworkingV1(). + Ingresses(metav1.NamespaceAll). + Watch(t.Ctx, options) + }, + }, + &networkingv1.Ingress{}, + 0, + cache.Indexers{}, + ) +} + +func (t *serviceIngressResource) Upsert(key string, raw interface{}) error { + if !t.EnableIngress { + return nil + } + svc := t.Service + ingress, ok := raw.(*networkingv1.Ingress) + if !ok { + svc.Log.Warn("upsert got invalid type", "raw", raw) + return nil + } + + svc.serviceLock.Lock() + defer svc.serviceLock.Unlock() + + for _, rule := range ingress.Spec.Rules { + var svcName string + var hostName string + var svcPort int32 + for _, path := range rule.HTTP.Paths { + if path.Path == "/" { + svcName = path.Backend.Service.Name + svcPort = 80 + } else { + continue + } + } + if svcName == "" { + continue + } + if t.SyncLoadBalancerIPs { + if len(ingress.Status.LoadBalancer.Ingress) > 0 && ingress.Status.LoadBalancer.Ingress[0].IP == "" { + continue + } + hostName = ingress.Status.LoadBalancer.Ingress[0].IP + } else { + hostName = rule.Host + } + for _, ingressTLS := range ingress.Spec.TLS { + for _, host := range ingressTLS.Hosts { + if rule.Host == host { + svcPort = 443 + } + } + } + + if svc.serviceHostnameMap == nil { + svc.serviceHostnameMap = make(map[string]serviceAddress) + } + // Maintain a list of the service name to the hostname from the Ingress resource. + svc.serviceHostnameMap[fmt.Sprintf("%s/%s", ingress.Namespace, svcName)] = serviceAddress{ + hostName: hostName, + port: svcPort, + } + if svc.ingressServiceMap == nil { + svc.ingressServiceMap = make(map[string]map[string]string) + } + if svc.ingressServiceMap[key] == nil { + svc.ingressServiceMap[key] = make(map[string]string) + } + // Maintain a list of all the service names that map to an Ingress resource. + svc.ingressServiceMap[key][fmt.Sprintf("%s/%s", ingress.Namespace, svcName)] = "" + } + + // Update the registration for each matched service and trigger a sync + for svcName := range svc.ingressServiceMap[key] { + svc.Log.Info(fmt.Sprintf("generating registrations for %s", svcName)) + svc.generateRegistrations(svcName) + } + svc.sync() + svc.Log.Info("upsert ingress", "key", key) + + return nil +} + +func (t *serviceIngressResource) Delete(key string, _ interface{}) error { + if !t.EnableIngress { + return nil + } + t.Service.serviceLock.Lock() + defer t.Service.serviceLock.Unlock() + + // This is a bit of an optimization. We only want to force a resync + // if we were tracking this ingress to begin with and that ingress + // had associated registrations. + if _, ok := t.Service.ingressServiceMap[key]; ok { + for svcName := range t.Service.ingressServiceMap[key] { + delete(t.Service.serviceHostnameMap, svcName) + } + delete(t.Service.ingressServiceMap, key) + t.Service.sync() + } + + t.Service.Log.Info("delete ingress", "key", key) + return nil +} + func (t *ServiceResource) addPrefixAndK8SNamespace(name, namespace string) string { if t.ConsulServicePrefix != "" { name = fmt.Sprintf("%s%s", t.ConsulServicePrefix, name) @@ -805,7 +1013,27 @@ func (t *ServiceResource) addPrefixAndK8SNamespace(name, namespace string) strin return name } +// isIngressService return if a service has an Ingress resource that references it. +func (t *ServiceResource) isIngressService(key string) bool { + return t.serviceHostnameMap != nil && t.serviceHostnameMap[key].hostName != "" +} + // consulHealthCheckID deterministically generates a health check ID based on service ID and Kubernetes namespace. func consulHealthCheckID(k8sNS string, serviceID string) string { return fmt.Sprintf("%s/%s", k8sNS, serviceID) } + +// Calculates the passing service weight. +func getServiceWeight(weight string) (int, error) { + // error validation if the input param is a number + weightI, err := strconv.Atoi(weight) + if err != nil { + return -1, err + } + + if weightI <= 1 { + return -1, fmt.Errorf("expecting the service annotation %s value to be greater than 1", annotationServiceWeight) + } + + return weightI, nil +} diff --git a/control-plane/catalog/to-consul/resource_test.go b/control-plane/catalog/to-consul/resource_test.go index 9ba94123ef..680c052823 100644 --- a/control-plane/catalog/to-consul/resource_test.go +++ b/control-plane/catalog/to-consul/resource_test.go @@ -10,7 +10,8 @@ import ( "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/hashicorp/go-hclog" "github.com/stretchr/testify/require" - apiv1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/kubernetes" @@ -52,6 +53,139 @@ func TestServiceResource_createDelete(t *testing.T) { }) } +// Test that Loadbalancer service weight is set from service annotation. +func TestServiceWeight_ingress(t *testing.T) { + t.Parallel() + client := fake.NewSimpleClientset() + syncer := newTestSyncer() + serviceResource := defaultServiceResource(client, syncer) + + // Start the controller + closer := controller.TestControllerRun(&serviceResource) + defer closer() + + // Insert an LB service + svc := lbService("foo", metav1.NamespaceDefault, "1.2.3.4") + svc.Annotations[annotationServiceWeight] = "22" + svc.Status.LoadBalancer.Ingress = append( + svc.Status.LoadBalancer.Ingress, + corev1.LoadBalancerIngress{IP: "3.3.3.3"}, + ) + + svc.Status.LoadBalancer.Ingress = append( + svc.Status.LoadBalancer.Ingress, + corev1.LoadBalancerIngress{IP: "4.4.4.4"}, + ) + + _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.Background(), svc, metav1.CreateOptions{}) + require.NoError(t, err) + + // Verify what we got + retry.Run(t, func(r *retry.R) { + syncer.Lock() + defer syncer.Unlock() + actual := syncer.Registrations + require.Len(r, actual, 3) + require.Equal(r, "foo", actual[1].Service.Service) + require.Equal(r, "3.3.3.3", actual[1].Service.Address) + require.Equal(r, 22, actual[1].Service.Weights.Passing) + require.Equal(r, "foo", actual[2].Service.Service) + require.Equal(r, "4.4.4.4", actual[2].Service.Address) + require.Equal(r, 22, actual[2].Service.Weights.Passing) + require.NotEqual(r, actual[1].Service.ID, actual[2].Service.ID) + }) +} + +// Test that Loadbalancer service weight is set from service annotation. +func TestServiceWeight_externalIP(t *testing.T) { + t.Parallel() + client := fake.NewSimpleClientset() + syncer := newTestSyncer() + serviceResource := defaultServiceResource(client, syncer) + + // Start the controller + closer := controller.TestControllerRun(&serviceResource) + defer closer() + + // Insert an LB service + svc := lbService("foo", metav1.NamespaceDefault, "1.2.3.4") + svc.Annotations[annotationServiceWeight] = "22" + svc.Spec.ExternalIPs = []string{"3.3.3.3", "4.4.4.4"} + + _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.Background(), svc, metav1.CreateOptions{}) + require.NoError(t, err) + + // Verify what we got + retry.Run(t, func(r *retry.R) { + syncer.Lock() + defer syncer.Unlock() + actual := syncer.Registrations + require.Len(r, actual, 2) + require.Equal(r, "foo", actual[0].Service.Service) + require.Equal(r, "3.3.3.3", actual[0].Service.Address) + require.Equal(r, 22, actual[0].Service.Weights.Passing) + require.Equal(r, "foo", actual[1].Service.Service) + require.Equal(r, "4.4.4.4", actual[1].Service.Address) + require.Equal(r, 22, actual[1].Service.Weights.Passing) + require.NotEqual(r, actual[0].Service.ID, actual[1].Service.ID) + }) +} + +// Test service weight. +func TestServiceWeight(t *testing.T) { + t.Parallel() + cases := map[string]struct { + Weight string + ExpectError bool + ExtectedWeight int + }{ + "external-IP": { + Weight: "22", + ExpectError: false, + ExtectedWeight: 22, + }, + "non-int-weight": { + Weight: "non-int", + ExpectError: true, + ExtectedWeight: 0, + }, + "one-weight": { + Weight: "1", + ExpectError: true, + ExtectedWeight: 0, + }, + "zero-weight": { + Weight: "0", + ExpectError: true, + ExtectedWeight: 0, + }, + "negative-weight": { + Weight: "-2", + ExpectError: true, + ExtectedWeight: 0, + }, + "greater-than-100-is-allowed": { + Weight: "1000", + ExpectError: false, + ExtectedWeight: 1000, + }, + } + + for name, c := range cases { + t.Run(name, func(tt *testing.T) { + weightI, err := getServiceWeight(c.Weight) + // Verify what we got + retry.Run(tt, func(r *retry.R) { + if c.ExpectError { + require.Error(r, err) + } else { + require.Equal(r, c.ExtectedWeight, weightI) + } + }) + }) + } +} + // Test that we're default enabled. func TestServiceResource_defaultEnable(t *testing.T) { t.Parallel() @@ -442,7 +576,7 @@ func TestServiceResource_lbMultiEndpoint(t *testing.T) { svc := lbService("foo", metav1.NamespaceDefault, "1.2.3.4") svc.Status.LoadBalancer.Ingress = append( svc.Status.LoadBalancer.Ingress, - apiv1.LoadBalancerIngress{IP: "2.3.4.5"}, + corev1.LoadBalancerIngress{IP: "2.3.4.5"}, ) _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.Background(), svc, metav1.CreateOptions{}) require.NoError(t, err) @@ -501,7 +635,7 @@ func TestServiceResource_lbPort(t *testing.T) { // Insert an LB service svc := lbService("foo", metav1.NamespaceDefault, "1.2.3.4") - svc.Spec.Ports = []apiv1.ServicePort{ + svc.Spec.Ports = []corev1.ServicePort{ {Name: "http", Port: 80, TargetPort: intstr.FromInt(8080)}, {Name: "rpc", Port: 8500, TargetPort: intstr.FromInt(2000)}, } @@ -534,7 +668,7 @@ func TestServiceResource_lbAnnotatedPort(t *testing.T) { // Insert an LB service svc := lbService("foo", metav1.NamespaceDefault, "1.2.3.4") svc.Annotations[annotationServicePort] = "rpc" - svc.Spec.Ports = []apiv1.ServicePort{ + svc.Spec.Ports = []corev1.ServicePort{ {Name: "http", Port: 80, TargetPort: intstr.FromInt(8080)}, {Name: "rpc", Port: 8500, TargetPort: intstr.FromInt(2000)}, } @@ -625,17 +759,17 @@ func TestServiceResource_lbRegisterEndpoints(t *testing.T) { // Insert the endpoints _, err := client.CoreV1().Endpoints(metav1.NamespaceDefault).Create( context.Background(), - &apiv1.Endpoints{ + &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, - Subsets: []apiv1.EndpointSubset{ + Subsets: []corev1.EndpointSubset{ { - Addresses: []apiv1.EndpointAddress{ + Addresses: []corev1.EndpointAddress{ {NodeName: &node1.Name, IP: "8.8.8.8"}, }, - Ports: []apiv1.EndpointPort{ + Ports: []corev1.EndpointPort{ {Name: "http", Port: 8080}, {Name: "rpc", Port: 2000}, }, @@ -760,17 +894,17 @@ func TestServiceResource_nodePort_singleEndpoint(t *testing.T) { // Insert the endpoints _, err := client.CoreV1().Endpoints(metav1.NamespaceDefault).Create( context.Background(), - &apiv1.Endpoints{ + &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, - Subsets: []apiv1.EndpointSubset{ + Subsets: []corev1.EndpointSubset{ { - Addresses: []apiv1.EndpointAddress{ + Addresses: []corev1.EndpointAddress{ {NodeName: &node1.Name, IP: "1.2.3.4"}, }, - Ports: []apiv1.EndpointPort{ + Ports: []corev1.EndpointPort{ {Name: "http", Port: 8080}, {Name: "rpc", Port: 2000}, }, @@ -857,7 +991,7 @@ func TestServiceResource_nodePortUnnamedPort(t *testing.T) { // Insert the service svc := nodePortService("foo", metav1.NamespaceDefault) // Override service ports - svc.Spec.Ports = []apiv1.ServicePort{ + svc.Spec.Ports = []corev1.ServicePort{ {Port: 80, TargetPort: intstr.FromInt(8080), NodePort: 30000}, {Port: 8500, TargetPort: intstr.FromInt(2000), NodePort: 30001}, } @@ -937,9 +1071,9 @@ func TestServiceResource_nodePort_externalFirstSync(t *testing.T) { node1, _ := createNodes(t, client) - node1.Status = apiv1.NodeStatus{ - Addresses: []apiv1.NodeAddress{ - {Type: apiv1.NodeInternalIP, Address: "4.5.6.7"}, + node1.Status = corev1.NodeStatus{ + Addresses: []corev1.NodeAddress{ + {Type: corev1.NodeInternalIP, Address: "4.5.6.7"}, }, } _, err := client.CoreV1().Nodes().UpdateStatus(context.Background(), node1, metav1.UpdateOptions{}) @@ -1170,7 +1304,7 @@ func TestServiceResource_clusterIPUnnamedPorts(t *testing.T) { // Insert the service svc := clusterIPService("foo", metav1.NamespaceDefault) - svc.Spec.Ports = []apiv1.ServicePort{ + svc.Spec.Ports = []corev1.ServicePort{ {Port: 80, TargetPort: intstr.FromInt(8080)}, {Port: 8500, TargetPort: intstr.FromInt(2000)}, } @@ -1278,7 +1412,7 @@ func TestServiceResource_clusterIPTargetPortNamed(t *testing.T) { // Insert the service svc := clusterIPService("foo", metav1.NamespaceDefault) svc.Annotations[annotationServicePort] = "rpc" - svc.Spec.Ports = []apiv1.ServicePort{ + svc.Spec.Ports = []corev1.ServicePort{ {Port: 80, TargetPort: intstr.FromString("httpPort"), Name: "http"}, {Port: 8500, TargetPort: intstr.FromString("rpcPort"), Name: "rpc"}, } @@ -1528,22 +1662,323 @@ func TestServiceResource_MirroredPrefixNamespace(t *testing.T) { }) } +// Test k8s namespace suffix is not appended +// when the service name annotation is provided. +func TestServiceResource_addIngress(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + enableIngress bool + syncIngressIP bool + ingress *networkingv1.Ingress + expectIngressSync bool + expectedAddress string + expectedPort int + }{ + "enable ingress on port 80": { + enableIngress: true, + ingress: &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + }, + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ + { + Hosts: []string{"test.other.consul"}, + SecretName: "test-other-tls-secret", + }, + }, + Rules: []networkingv1.IngressRule{ + { + Host: "test.host.consul", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-service", + Port: networkingv1.ServiceBackendPort{ + Number: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectIngressSync: true, + expectedAddress: "test.host.consul", + expectedPort: 80, + }, + "enable ingress on port 443": { + enableIngress: true, + ingress: &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + }, + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ + { + Hosts: []string{"test.host.consul"}, + SecretName: "test-tls-secret", + }, + }, + Rules: []networkingv1.IngressRule{ + { + Host: "test.host.consul", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-service", + Port: networkingv1.ServiceBackendPort{ + Number: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectIngressSync: true, + expectedAddress: "test.host.consul", + expectedPort: 443, + }, + "enable ingress on port 80 with loadbalancer IP": { + enableIngress: true, + syncIngressIP: true, + ingress: &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + }, + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ + { + Hosts: []string{"test.other.consul"}, + SecretName: "test-other-tls-secret", + }, + }, + Rules: []networkingv1.IngressRule{ + { + Host: "test.host.consul", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-service", + Port: networkingv1.ServiceBackendPort{ + Number: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Status: networkingv1.IngressStatus{ + LoadBalancer: networkingv1.IngressLoadBalancerStatus{ + Ingress: []networkingv1.IngressLoadBalancerIngress{{IP: "1.2.3.4"}}, + }, + }, + }, + expectIngressSync: true, + expectedAddress: "1.2.3.4", + expectedPort: 80, + }, + "enable ingress on port 443 with loadbalancer IP": { + enableIngress: true, + syncIngressIP: true, + ingress: &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + }, + Spec: networkingv1.IngressSpec{ + TLS: []networkingv1.IngressTLS{ + { + Hosts: []string{"test.host.consul"}, + SecretName: "test-tls-secret", + }, + }, + Rules: []networkingv1.IngressRule{ + { + Host: "test.host.consul", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-service", + Port: networkingv1.ServiceBackendPort{ + Number: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Status: networkingv1.IngressStatus{ + LoadBalancer: networkingv1.IngressLoadBalancerStatus{ + Ingress: []networkingv1.IngressLoadBalancerIngress{{IP: "1.2.3.4"}}, + }, + }, + }, + expectIngressSync: true, + expectedAddress: "1.2.3.4", + expectedPort: 443, + }, + "ingress disabled": { + enableIngress: false, + ingress: &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "test.host.consul", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-service", + Port: networkingv1.ServiceBackendPort{ + Number: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectIngressSync: false, + expectedAddress: "1.1.1.1", + expectedPort: 8080, + }, + "ignores ingress if host != /": { + enableIngress: true, + ingress: &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "test.host.consul", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/foo", + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-service", + Port: networkingv1.ServiceBackendPort{ + Number: 8080, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectIngressSync: false, + expectedAddress: "1.1.1.1", + expectedPort: 8080, + }, + } + + for name, test := range cases { + t.Run(name, func(t *testing.T) { + client := fake.NewSimpleClientset() + syncer := newTestSyncer() + serviceResource := defaultServiceResource(client, syncer) + serviceResource.ClusterIPSync = true + serviceResource.EnableIngress = test.enableIngress + serviceResource.SyncLoadBalancerIPs = test.syncIngressIP + + // Start the controller + closer := controller.TestControllerRun(&serviceResource) + defer closer() + + // Create the service + _, err := client.CoreV1().Services(metav1.NamespaceDefault).Create(context.Background(), clusterIPService("test-service", metav1.NamespaceDefault), metav1.CreateOptions{}) + require.NoError(t, err) + // Create the ingress + _, err = client.NetworkingV1().Ingresses(metav1.NamespaceDefault).Create(context.Background(), test.ingress, metav1.CreateOptions{}) + require.NoError(t, err) + createEndpoints(t, client, "test-service", metav1.NamespaceDefault) + // Verify that the service name annotation is preferred + retry.Run(t, func(r *retry.R) { + syncer.Lock() + defer syncer.Unlock() + actual := syncer.Registrations + if test.expectIngressSync { + require.Len(r, actual, 1) + require.Equal(r, test.expectedAddress, actual[0].Service.Address) + require.Equal(r, test.expectedPort, actual[0].Service.Port) + } else { + require.Len(r, actual, 2) + require.Equal(r, test.expectedAddress, actual[0].Service.Address) + require.Equal(r, test.expectedPort, actual[0].Service.Port) + } + + }) + }) + } +} + // lbService returns a Kubernetes service of type LoadBalancer. -func lbService(name, namespace, lbIP string) *apiv1.Service { - return &apiv1.Service{ +func lbService(name, namespace, lbIP string) *corev1.Service { + return &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, Annotations: map[string]string{}, }, - Spec: apiv1.ServiceSpec{ - Type: apiv1.ServiceTypeLoadBalancer, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeLoadBalancer, }, - Status: apiv1.ServiceStatus{ - LoadBalancer: apiv1.LoadBalancerStatus{ - Ingress: []apiv1.LoadBalancerIngress{ + Status: corev1.ServiceStatus{ + LoadBalancer: corev1.LoadBalancerStatus{ + Ingress: []corev1.LoadBalancerIngress{ { IP: lbIP, }, @@ -1554,16 +1989,16 @@ func lbService(name, namespace, lbIP string) *apiv1.Service { } // nodePortService returns a Kubernetes service of type NodePort. -func nodePortService(name, namespace string) *apiv1.Service { - return &apiv1.Service{ +func nodePortService(name, namespace string) *corev1.Service { + return &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Spec: apiv1.ServiceSpec{ - Type: apiv1.ServiceTypeNodePort, - Ports: []apiv1.ServicePort{ + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeNodePort, + Ports: []corev1.ServicePort{ {Name: "http", Port: 80, TargetPort: intstr.FromInt(8080), NodePort: 30000}, {Name: "rpc", Port: 8500, TargetPort: intstr.FromInt(2000), NodePort: 30001}, }, @@ -1572,17 +2007,17 @@ func nodePortService(name, namespace string) *apiv1.Service { } // clusterIPService returns a Kubernetes service of type ClusterIP. -func clusterIPService(name, namespace string) *apiv1.Service { - return &apiv1.Service{ +func clusterIPService(name, namespace string) *corev1.Service { + return &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, Annotations: map[string]string{}, }, - Spec: apiv1.ServiceSpec{ - Type: apiv1.ServiceTypeClusterIP, - Ports: []apiv1.ServicePort{ + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeClusterIP, + Ports: []corev1.ServicePort{ {Name: "http", Port: 80, TargetPort: intstr.FromInt(8080)}, {Name: "rpc", Port: 8500, TargetPort: intstr.FromInt(2000)}, }, @@ -1591,34 +2026,34 @@ func clusterIPService(name, namespace string) *apiv1.Service { } // createNodes calls the fake k8s client to create two Kubernetes nodes and returns them. -func createNodes(t *testing.T, client *fake.Clientset) (*apiv1.Node, *apiv1.Node) { +func createNodes(t *testing.T, client *fake.Clientset) (*corev1.Node, *corev1.Node) { // Insert the nodes - node1 := &apiv1.Node{ + node1 := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: nodeName1, }, - Status: apiv1.NodeStatus{ - Addresses: []apiv1.NodeAddress{ - {Type: apiv1.NodeExternalIP, Address: "1.2.3.4"}, - {Type: apiv1.NodeInternalIP, Address: "4.5.6.7"}, - {Type: apiv1.NodeInternalIP, Address: "7.8.9.10"}, + Status: corev1.NodeStatus{ + Addresses: []corev1.NodeAddress{ + {Type: corev1.NodeExternalIP, Address: "1.2.3.4"}, + {Type: corev1.NodeInternalIP, Address: "4.5.6.7"}, + {Type: corev1.NodeInternalIP, Address: "7.8.9.10"}, }, }, } _, err := client.CoreV1().Nodes().Create(context.Background(), node1, metav1.CreateOptions{}) require.NoError(t, err) - node2 := &apiv1.Node{ + node2 := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: nodeName2, }, - Status: apiv1.NodeStatus{ - Addresses: []apiv1.NodeAddress{ - {Type: apiv1.NodeExternalIP, Address: "2.3.4.5"}, - {Type: apiv1.NodeInternalIP, Address: "3.4.5.6"}, - {Type: apiv1.NodeInternalIP, Address: "6.7.8.9"}, + Status: corev1.NodeStatus{ + Addresses: []corev1.NodeAddress{ + {Type: corev1.NodeExternalIP, Address: "2.3.4.5"}, + {Type: corev1.NodeInternalIP, Address: "3.4.5.6"}, + {Type: corev1.NodeInternalIP, Address: "6.7.8.9"}, }, }, } @@ -1632,31 +2067,31 @@ func createNodes(t *testing.T, client *fake.Clientset) (*apiv1.Node, *apiv1.Node func createEndpoints(t *testing.T, client *fake.Clientset, serviceName string, namespace string) { node1 := nodeName1 node2 := nodeName2 - targetRef := apiv1.ObjectReference{Kind: "pod", Name: "foobar"} + targetRef := corev1.ObjectReference{Kind: "pod", Name: "foobar"} _, err := client.CoreV1().Endpoints(namespace).Create( context.Background(), - &apiv1.Endpoints{ + &corev1.Endpoints{ ObjectMeta: metav1.ObjectMeta{ Name: serviceName, Namespace: namespace, }, - Subsets: []apiv1.EndpointSubset{ + Subsets: []corev1.EndpointSubset{ { - Addresses: []apiv1.EndpointAddress{ + Addresses: []corev1.EndpointAddress{ {NodeName: &node1, IP: "1.1.1.1", TargetRef: &targetRef}, }, - Ports: []apiv1.EndpointPort{ + Ports: []corev1.EndpointPort{ {Name: "http", Port: 8080}, {Name: "rpc", Port: 2000}, }, }, { - Addresses: []apiv1.EndpointAddress{ + Addresses: []corev1.EndpointAddress{ {NodeName: &node2, IP: "2.2.2.2"}, }, - Ports: []apiv1.EndpointPort{ + Ports: []corev1.EndpointPort{ {Name: "http", Port: 8080}, {Name: "rpc", Port: 2000}, }, diff --git a/control-plane/cni/go.mod b/control-plane/cni/go.mod index 2b43b784fd..e8fcc980ef 100644 --- a/control-plane/cni/go.mod +++ b/control-plane/cni/go.mod @@ -3,9 +3,9 @@ module github.com/hashicorp/consul-k8s/control-plane/cni require ( github.com/containernetworking/cni v1.1.1 github.com/containernetworking/plugins v1.1.1 - github.com/hashicorp/consul/sdk v0.13.0 - github.com/hashicorp/go-hclog v0.16.1 - github.com/stretchr/testify v1.7.1 + github.com/hashicorp/consul/sdk v0.13.1 + github.com/hashicorp/go-hclog v1.2.1 + github.com/stretchr/testify v1.7.2 k8s.io/api v0.22.2 k8s.io/apimachinery v0.22.2 k8s.io/client-go v0.22.2 @@ -14,7 +14,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/fatih/color v1.12.0 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/go-logr/logr v0.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.2 // indirect @@ -23,24 +23,24 @@ require ( github.com/googleapis/gnostic v0.5.5 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/json-iterator/go v1.1.11 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.13 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect + golang.org/x/net v0.13.0 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect - golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect - golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/term v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.26.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog/v2 v2.9.0 // indirect k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect @@ -50,4 +50,4 @@ require ( replace github.com/hashicorp/consul/sdk => github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 -go 1.19 +go 1.20 diff --git a/control-plane/cni/go.sum b/control-plane/cni/go.sum index 1188cc5dd4..8f4c0668ea 100644 --- a/control-plane/cni/go.sum +++ b/control-plane/cni/go.sum @@ -57,8 +57,8 @@ 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/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.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= -github.com/fatih/color v1.12.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/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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -135,8 +135,8 @@ github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 h1:jw0NwPmN github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.1 h1:IVQwpTGNRRIHafnTs2dQLIk4ENtneRIEEJWOVDqz99o= -github.com/hashicorp/go-hclog v0.16.1/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-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 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= @@ -162,14 +162,15 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +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.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.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA= -github.com/mattn/go-isatty v0.0.13/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/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -217,8 +218,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.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/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -286,8 +287,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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= @@ -334,20 +335,25 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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-20210630005230-0f9fa26af87c/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-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.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.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.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.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= @@ -445,8 +451,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 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= @@ -467,8 +474,9 @@ 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= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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= diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml index 4f335a923d..e30f41ecb9 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicedefaults.yaml @@ -267,7 +267,14 @@ spec: upstream proxy instances will be monitored for removal from the load balancing pool. properties: - enforcing_consecutive_5xx: + baseEjectionTime: + description: The base time that a host is ejected for. + The real time is equal to the base time multiplied by + the number of times the host has been ejected and is + capped by max_ejection_time (Default 300s). Defaults + to 30000ms or 30s. + type: string + enforcingConsecutive5xx: description: EnforcingConsecutive5xx is the % chance that a host will be actually ejected when an outlier status is detected through consecutive 5xx. This setting can @@ -278,6 +285,13 @@ spec: description: Interval between health check analysis sweeps. Each sweep may remove hosts or return hosts to the pool. type: string + maxEjectionPercent: + description: The maximum % of an upstream cluster that + can be ejected due to outlier detection. Defaults to + 10% but will eject at least one host regardless of the + value. + format: int32 + type: integer maxFailures: description: MaxFailures is the count of consecutive failures that results in a host being removed from the pool. @@ -370,7 +384,14 @@ spec: how upstream proxy instances will be monitored for removal from the load balancing pool. properties: - enforcing_consecutive_5xx: + baseEjectionTime: + description: The base time that a host is ejected for. + The real time is equal to the base time multiplied + by the number of times the host has been ejected and + is capped by max_ejection_time (Default 300s). Defaults + to 30000ms or 30s. + type: string + enforcingConsecutive5xx: description: EnforcingConsecutive5xx is the % chance that a host will be actually ejected when an outlier status is detected through consecutive 5xx. This setting @@ -382,6 +403,13 @@ spec: sweeps. Each sweep may remove hosts or return hosts to the pool. type: string + maxEjectionPercent: + description: The maximum % of an upstream cluster that + can be ejected due to outlier detection. Defaults + to 10% but will eject at least one host regardless + of the value. + format: int32 + type: integer maxFailures: description: MaxFailures is the count of consecutive failures that results in a host being removed from diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml index a84fc0bd88..a9af572ba9 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_serviceresolvers.yaml @@ -235,6 +235,10 @@ spec: If empty the default subset is used. type: string type: object + requestTimeout: + description: RequestTimeout is the timeout for receiving an HTTP response + from this service before the connection is terminated. + type: string subsets: additionalProperties: properties: diff --git a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml index de071bd0ef..8b55692bd2 100644 --- a/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml +++ b/control-plane/config/crd/bases/consul.hashicorp.com_servicerouters.yaml @@ -61,6 +61,10 @@ spec: description: Destination controls how to proxy the matching request(s) to a service. properties: + idleTimeout: + description: IdleTimeout is total amount of time permitted + for the request stream to be idle. + type: string namespace: description: Namespace is the Consul namespace to resolve the service from instead of the current namespace. If diff --git a/control-plane/connect-inject/common/common.go b/control-plane/connect-inject/common/common.go index 9611797c9f..0ebf829666 100644 --- a/control-plane/connect-inject/common/common.go +++ b/control-plane/connect-inject/common/common.go @@ -9,6 +9,40 @@ import ( corev1 "k8s.io/api/core/v1" ) +// DetermineAndValidatePort behaves as follows: +// If the annotation exists, validate the port and return it. +// If the annotation does not exist, return the default port. +// If the privileged flag is true, it will allow the port to be in the +// privileged port range of 1-1023. Otherwise, it will only allow ports in the +// unprivileged range of 1024-65535. +func DetermineAndValidatePort(pod corev1.Pod, annotation string, defaultPort string, privileged bool) (string, error) { + if raw, ok := pod.Annotations[annotation]; ok && raw != "" { + port, err := PortValue(pod, raw) + if err != nil { + return "", fmt.Errorf("%s annotation value of %s is not a valid integer", annotation, raw) + } + + if privileged && (port < 1 || port > 65535) { + return "", fmt.Errorf("%s annotation value of %d is not in the valid port range 1-65535", annotation, port) + } else if !privileged && (port < 1024 || port > 65535) { + return "", fmt.Errorf("%s annotation value of %d is not in the unprivileged port range 1024-65535", annotation, port) + } + + // If the annotation exists, return the validated port. + return fmt.Sprint(port), nil + } + + // If the annotation does not exist, return the default. + if defaultPort != "" { + port, err := PortValue(pod, defaultPort) + if err != nil { + return "", fmt.Errorf("%s is not a valid port on the pod %s", defaultPort, pod.Name) + } + return fmt.Sprint(port), nil + } + return "", nil +} + // PortValue returns the port of the container for the string value passed // in as an argument on the provided pod. func PortValue(pod corev1.Pod, value string) (int32, error) { diff --git a/control-plane/connect-inject/common/common_test.go b/control-plane/connect-inject/common/common_test.go index e43ccf8255..155f6b892d 100644 --- a/control-plane/connect-inject/common/common_test.go +++ b/control-plane/connect-inject/common/common_test.go @@ -3,10 +3,153 @@ package common import ( "testing" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/namespaces" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +func TestCommonDetermineAndValidatePort(t *testing.T) { + cases := []struct { + Name string + Pod func(*corev1.Pod) *corev1.Pod + Annotation string + Privileged bool + DefaultPort string + Expected string + Err string + }{ + { + Name: "Valid annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "1234" + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + Expected: "1234", + Err: "", + }, + { + Name: "Uses default when there's no annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + DefaultPort: "4321", + Expected: "4321", + Err: "", + }, + { + Name: "Gets the value of the named default port when there's no annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Spec.Containers[0].Ports = []corev1.ContainerPort{ + { + Name: "web-port", + ContainerPort: 2222, + }, + } + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + DefaultPort: "web-port", + Expected: "2222", + Err: "", + }, + { + Name: "Errors if the named default port doesn't exist on the pod", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + DefaultPort: "web-port", + Expected: "", + Err: "web-port is not a valid port on the pod minimal", + }, + { + Name: "Gets the value of the named port", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "web-port" + pod.Spec.Containers[0].Ports = []corev1.ContainerPort{ + { + Name: "web-port", + ContainerPort: 2222, + }, + } + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + DefaultPort: "4321", + Expected: "2222", + Err: "", + }, + { + Name: "Invalid annotation (not an integer)", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "not-an-int" + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + Expected: "", + Err: "consul.hashicorp.com/test-annotation-port annotation value of not-an-int is not a valid integer", + }, + { + Name: "Invalid annotation (integer not in port range)", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "100000" + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: true, + Expected: "", + Err: "consul.hashicorp.com/test-annotation-port annotation value of 100000 is not in the valid port range 1-65535", + }, + { + Name: "Invalid annotation (integer not in unprivileged port range)", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "22" + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: false, + Expected: "", + Err: "consul.hashicorp.com/test-annotation-port annotation value of 22 is not in the unprivileged port range 1024-65535", + }, + { + Name: "Privileged ports allowed", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "22" + return pod + }, + Annotation: "consul.hashicorp.com/test-annotation-port", + Privileged: true, + Expected: "22", + Err: "", + }, + } + + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + require := require.New(t) + + actual, err := DetermineAndValidatePort(*tt.Pod(minimal()), tt.Annotation, tt.DefaultPort, tt.Privileged) + + if tt.Err == "" { + require.NoError(err) + require.Equal(tt.Expected, actual) + } else { + require.EqualError(err, tt.Err) + } + }) + } +} + func TestPortValue(t *testing.T) { cases := []struct { Name string @@ -90,3 +233,26 @@ func TestPortValue(t *testing.T) { }) } } + +func minimal() *corev1.Pod { + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespaces.DefaultNamespace, + Name: "minimal", + Annotations: map[string]string{ + constants.AnnotationService: "foo", + }, + }, + + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + { + Name: "web-side", + }, + }, + }, + } +} diff --git a/control-plane/connect-inject/constants/annotations_and_labels.go b/control-plane/connect-inject/constants/annotations_and_labels.go index 637e028202..8767de33b3 100644 --- a/control-plane/connect-inject/constants/annotations_and_labels.go +++ b/control-plane/connect-inject/constants/annotations_and_labels.go @@ -97,6 +97,13 @@ const ( AnnotationSidecarProxyMemoryLimit = "consul.hashicorp.com/sidecar-proxy-memory-limit" AnnotationSidecarProxyMemoryRequest = "consul.hashicorp.com/sidecar-proxy-memory-request" + // annotations for sidecar proxy lifecycle configuration. + AnnotationEnableSidecarProxyLifecycle = "consul.hashicorp.com/enable-sidecar-proxy-lifecycle" + AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners = "consul.hashicorp.com/enable-sidecar-proxy-lifecycle-shutdown-drain-listeners" + AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds = "consul.hashicorp.com/sidecar-proxy-lifecycle-shutdown-grace-period-seconds" + AnnotationSidecarProxyLifecycleGracefulPort = "consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-port" + AnnotationSidecarProxyLifecycleGracefulShutdownPath = "consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-shutdown-path" + // annotations for sidecar volumes. AnnotationConsulSidecarUserVolume = "consul.hashicorp.com/consul-sidecar-user-volume" AnnotationConsulSidecarUserVolumeMount = "consul.hashicorp.com/consul-sidecar-user-volume-mount" diff --git a/control-plane/connect-inject/constants/constants.go b/control-plane/connect-inject/constants/constants.go index e371677629..673a64eabc 100644 --- a/control-plane/connect-inject/constants/constants.go +++ b/control-plane/connect-inject/constants/constants.go @@ -15,4 +15,10 @@ const ( // MetaKeyPodName is the meta key name for Kubernetes pod name used for the Consul services. MetaKeyPodName = "pod-name" + + // DefaultGracefulPort is the default port that consul-dataplane uses for graceful shutdown. + DefaultGracefulPort = 20600 + + // DefaultGracefulShutdownPath is the default path that consul-dataplane uses for graceful shutdown. + DefaultGracefulShutdownPath = "/graceful_shutdown" ) diff --git a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go index 189587106d..7f4978c133 100644 --- a/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go +++ b/control-plane/connect-inject/controllers/endpoints/consul_client_health_checks_test.go @@ -3,7 +3,7 @@ package endpoints import ( "testing" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/helper/test" "github.com/hashicorp/consul-server-connection-manager/discovery" @@ -238,7 +238,7 @@ func TestUpdateHealthCheckOnConsulClient(t *testing.T) { ctrl := Controller{ ConsulClientConfig: testClient.Cfg, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), } err := ctrl.updateHealthCheckOnConsulClient(testClient.Cfg.APIClientConfig, pod, endpoints, c.updateToStatus) diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go index 800df9cc24..decf9f3c77 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller.go @@ -42,9 +42,10 @@ const ( terminatingGateway = "terminating-gateway" ingressGateway = "ingress-gateway" - kubernetesSuccessReasonMsg = "Kubernetes health checks passing" - envoyPrometheusBindAddr = "envoy_prometheus_bind_addr" - defaultNS = "default" + kubernetesSuccessReasonMsg = "Kubernetes health checks passing" + envoyPrometheusBindAddr = "envoy_prometheus_bind_addr" + envoyTelemetryCollectorBindSocketDir = "envoy_telemetry_collector_bind_socket_dir" + defaultNS = "default" // clusterIPTaggedAddressName is the key for the tagged address to store the service's cluster IP and service port // in Consul. Note: This value should not be changed without a corresponding change in Consul. @@ -117,6 +118,10 @@ type Controller struct { // to Consul client agents. EnableAutoEncrypt bool + // EnableTelemetryCollector controls whether the proxy service should be registered + // with config to enable telemetry forwarding. + EnableTelemetryCollector bool + MetricsConfig metrics.Config Log logr.Logger @@ -152,7 +157,6 @@ func (r *Controller) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu } err = r.Client.Get(ctx, req.NamespacedName, &serviceEndpoints) - // endpointPods holds a set of all pods this endpoints object is currently pointing to. // We use this later when we reconcile ACL tokens to decide whether an ACL token in Consul // is for a pod that no longer exists. @@ -176,7 +180,7 @@ func (r *Controller) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu // It is possible that the endpoints object has never been registered, in which case deregistration is a no-op. if isLabeledIgnore(serviceEndpoints.Labels) { // We always deregister the service to handle the case where a user has registered the service, then added the label later. - r.Log.Info("Ignoring endpoint labeled with `consul.hashicorp.com/service-ignore: \"true\"`", "name", req.Name, "namespace", req.Namespace) + r.Log.Info("ignoring endpoint labeled with `consul.hashicorp.com/service-ignore: \"true\"`", "name", req.Name, "namespace", req.Namespace) err = r.deregisterService(apiClient, req.Name, req.Namespace, nil) return ctrl.Result{}, err } @@ -472,6 +476,10 @@ func (r *Controller) createServiceRegistrations(pod corev1.Pod, serviceEndpoints proxyConfig.Config[envoyPrometheusBindAddr] = prometheusScrapeListener } + if r.EnableTelemetryCollector && proxyConfig.Config != nil { + proxyConfig.Config[envoyTelemetryCollectorBindSocketDir] = "/consul/connect-inject" + } + if consulServicePort > 0 { proxyConfig.LocalServiceAddress = "127.0.0.1" proxyConfig.LocalServicePort = consulServicePort @@ -652,6 +660,9 @@ func (r *Controller) createGatewayRegistrations(pod corev1.Pod, serviceEndpoints ID: pod.Name, Address: pod.Status.PodIP, Meta: meta, + Proxy: &api.AgentServiceConnectProxyConfig{ + Config: map[string]interface{}{}, + }, } gatewayServiceName, ok := pod.Annotations[constants.AnnotationGatewayConsulServiceName] @@ -751,6 +762,10 @@ func (r *Controller) createGatewayRegistrations(pod corev1.Pod, serviceEndpoints } } + if r.EnableTelemetryCollector && service.Proxy != nil && service.Proxy.Config != nil { + service.Proxy.Config[envoyTelemetryCollectorBindSocketDir] = "/consul/service" + } + serviceRegistration := &api.CatalogRegistration{ Node: common.ConsulNodeNameFromK8sNode(pod.Spec.NodeName), Address: pod.Status.HostIP, @@ -869,14 +884,14 @@ func getHealthCheckStatusReason(healthCheckStatus, podName, podNamespace string) // them only if they are not in endpointsAddressesMap. If the map is nil, it will deregister all instances. If the map // has addresses, it will only deregister instances not in the map. func (r *Controller) deregisterService(apiClient *api.Client, k8sSvcName, k8sSvcNamespace string, endpointsAddressesMap map[string]bool) error { - // Get services matching metadata. - nodesWithSvcs, err := r.serviceInstancesForK8sNodes(apiClient, k8sSvcName, k8sSvcNamespace) + // Get services matching metadata from Consul + nodesWithSvcs, err := r.serviceInstancesForNodes(apiClient, k8sSvcName, k8sSvcNamespace) if err != nil { r.Log.Error(err, "failed to get service instances", "name", k8sSvcName) return err } - // Deregister each service instance that matches the metadata. + var errs error for _, nodeSvcs := range nodesWithSvcs { for _, svc := range nodeSvcs.Services { // We need to get services matching "k8s-service-name" and "k8s-namespace" metadata. @@ -887,42 +902,48 @@ func (r *Controller) deregisterService(apiClient *api.Client, k8sSvcName, k8sSvc if _, ok := endpointsAddressesMap[svc.Address]; !ok { // If the service address is not in the Endpoints addresses, deregister it. r.Log.Info("deregistering service from consul", "svc", svc.ID) - _, err = apiClient.Catalog().Deregister(&api.CatalogDeregistration{ + _, err := apiClient.Catalog().Deregister(&api.CatalogDeregistration{ Node: nodeSvcs.Node.Node, ServiceID: svc.ID, Namespace: svc.Namespace, }, nil) if err != nil { + // Do not exit right away as there might be other services that need to be deregistered. r.Log.Error(err, "failed to deregister service instance", "id", svc.ID) - return err + errs = multierror.Append(errs, err) + } else { + serviceDeregistered = true } - serviceDeregistered = true } } else { r.Log.Info("deregistering service from consul", "svc", svc.ID) - if _, err = apiClient.Catalog().Deregister(&api.CatalogDeregistration{ + _, err := apiClient.Catalog().Deregister(&api.CatalogDeregistration{ Node: nodeSvcs.Node.Node, ServiceID: svc.ID, Namespace: svc.Namespace, - }, nil); err != nil { + }, nil) + if err != nil { + // Do not exit right away as there might be other services that need to be deregistered. r.Log.Error(err, "failed to deregister service instance", "id", svc.ID) - return err + errs = multierror.Append(errs, err) + } else { + serviceDeregistered = true } - serviceDeregistered = true } if r.AuthMethod != "" && serviceDeregistered { r.Log.Info("reconciling ACL tokens for service", "svc", svc.Service) - err = r.deleteACLTokensForServiceInstance(apiClient, svc, k8sSvcNamespace, svc.Meta[constants.MetaKeyPodName]) + err := r.deleteACLTokensForServiceInstance(apiClient, svc, k8sSvcNamespace, svc.Meta[constants.MetaKeyPodName]) if err != nil { r.Log.Error(err, "failed to reconcile ACL tokens for service", "svc", svc.Service) - return err + errs = multierror.Append(errs, err) } } } } - return nil + return errs + } // deleteACLTokensForServiceInstance finds the ACL tokens that belongs to the service instance and deletes it from Consul. @@ -1042,21 +1063,32 @@ func getTokenMetaFromDescription(description string) (map[string]string, error) return tokenMeta, nil } -func (r *Controller) serviceInstancesForK8sNodes(apiClient *api.Client, k8sServiceName, k8sServiceNamespace string) ([]*api.CatalogNodeServiceList, error) { +func (r *Controller) serviceInstancesForNodes(apiClient *api.Client, k8sServiceName, k8sServiceNamespace string) ([]*api.CatalogNodeServiceList, error) { var serviceList []*api.CatalogNodeServiceList - // Get a list of k8s nodes. - var nodeList corev1.NodeList - err := r.Client.List(r.Context, &nodeList) + + // The nodelist may have changed between this point and when the event was raised + // For example, if a pod is evicted because a node has been deleted, there is no guarantee that that node will show up here + // query consul catalog for a list of nodes supporting this service + // quite a lot of results as synthetic nodes are never deregistered. + var nodes []*api.Node + filter := fmt.Sprintf(`Meta[%q] == %q `, "synthetic-node", "true") + nodes, _, err := apiClient.Catalog().Nodes(&api.QueryOptions{Filter: filter, Namespace: namespaces.WildcardNamespace}) if err != nil { return nil, err } - for _, node := range nodeList.Items { + + var errs error + for _, node := range nodes { var nodeServices *api.CatalogNodeServiceList - nodeServices, err = r.serviceInstancesForK8SServiceNameAndNamespace(apiClient, k8sServiceName, k8sServiceNamespace, common.ConsulNodeNameFromK8sNode(node.Name)) - serviceList = append(serviceList, nodeServices) + nodeServices, err := r.serviceInstancesForK8SServiceNameAndNamespace(apiClient, k8sServiceName, k8sServiceNamespace, node.Node) + if err != nil { + errs = multierror.Append(errs, err) + } else { + serviceList = append(serviceList, nodeServices) + } } - return serviceList, err + return serviceList, errs } // serviceInstancesForK8SServiceNameAndNamespace calls Consul's ServicesWithFilter to get the list diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go index 4303bafbc8..f0309cdc8f 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_ent_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + //go:build enterprise package endpoints @@ -8,7 +11,7 @@ import ( "testing" mapset "github.com/deckarep/golang-set" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" @@ -223,7 +226,7 @@ func TestReconcileCreateEndpointWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -484,7 +487,7 @@ func TestReconcileCreateGatewayWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -1491,7 +1494,7 @@ func TestReconcileUpdateEndpointWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -1561,7 +1564,7 @@ func TestReconcileUpdateEndpointWithNamespaces(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(serviceID) { - require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") + require.Contains(t, err.Error(), "ACL not found") } else { require.NoError(t, err, "token should exist for service instance: "+serviceID) require.NotNil(t, token) @@ -1777,7 +1780,7 @@ func TestReconcileDeleteEndpointWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -1819,7 +1822,7 @@ func TestReconcileDeleteEndpointWithNamespaces(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") + require.Contains(t, err.Error(), "ACL not found") } }) } @@ -2071,7 +2074,7 @@ func TestReconcileDeleteGatewayWithNamespaces(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -2104,7 +2107,7 @@ func TestReconcileDeleteGatewayWithNamespaces(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") + require.Contains(t, err.Error(), "ACL not found") } }) } diff --git a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go index 63cde6404b..f5a910a5c3 100644 --- a/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go +++ b/control-plane/connect-inject/controllers/endpoints/endpoints_controller_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package endpoints import ( @@ -7,7 +10,7 @@ import ( "testing" mapset "github.com/deckarep/golang-set" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" @@ -600,7 +603,7 @@ func TestProcessUpstreams(t *testing.T) { for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { ep := &Controller{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSetWith(), EnableConsulNamespaces: tt.consulNamespacesEnabled, @@ -890,6 +893,9 @@ func TestReconcileCreateEndpoint_MultiportService(t *testing.T) { catalogRegistration := &api.CatalogRegistration{ Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: svc, } _, err := consulClient.Catalog().Register(catalogRegistration, nil) @@ -899,7 +905,7 @@ func TestReconcileCreateEndpoint_MultiportService(t *testing.T) { // Create the endpoints controller ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -993,6 +999,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { expectedProxySvcInstances []*api.CatalogService expectedHealthChecks []*api.HealthCheck metricsEnabled bool + telemetryCollectorDisabled bool nodeMeta map[string]string expErr string }{ @@ -1075,6 +1082,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod1-service-created", LocalServiceAddress: "", LocalServicePort: 0, + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod1", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1160,7 +1168,9 @@ func TestReconcileCreateEndpoint(t *testing.T) { Port: 443, }, }, - ServiceProxy: &api.AgentServiceConnectProxyConfig{}, + ServiceProxy: &api.AgentServiceConnectProxyConfig{ + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/service")}, + }, NodeMeta: map[string]string{ "synthetic-node": "true", "test-node": "true", @@ -1213,6 +1223,80 @@ func TestReconcileCreateEndpoint(t *testing.T) { } return []runtime.Object{gateway, endpoint} }, + expectedConsulSvcInstances: []*api.CatalogService{ + { + ServiceID: "mesh-gateway", + ServiceName: "mesh-gateway", + ServiceAddress: "1.2.3.4", + ServicePort: 8443, + ServiceMeta: map[string]string{constants.MetaKeyPodName: "mesh-gateway", metaKeyKubeServiceName: "mesh-gateway", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, + ServiceTags: []string{}, + ServiceTaggedAddresses: map[string]api.ServiceAddress{ + "lan": { + Address: "1.2.3.4", + Port: 8443, + }, + "wan": { + Address: "2.3.4.5", + Port: 443, + }, + }, + ServiceProxy: &api.AgentServiceConnectProxyConfig{ + Config: map[string]interface{}{ + "envoy_prometheus_bind_addr": "1.2.3.4:20200", + "envoy_telemetry_collector_bind_socket_dir": "/consul/service", + }, + }, + }, + }, + expectedHealthChecks: []*api.HealthCheck{ + { + CheckID: "default/mesh-gateway", + ServiceName: "mesh-gateway", + ServiceID: "mesh-gateway", + Name: consulKubernetesCheckName, + Status: api.HealthPassing, + Output: kubernetesSuccessReasonMsg, + Type: consulKubernetesCheckType, + }, + }, + metricsEnabled: true, + }, + { + name: "Mesh_Gateway_with_Metrics_enabled_and_telemetry_collector_disabled", + svcName: "mesh-gateway", + consulSvcName: "mesh-gateway", + telemetryCollectorDisabled: true, + k8sObjects: func() []runtime.Object { + gateway := createGatewayPod("mesh-gateway", "1.2.3.4", map[string]string{ + constants.AnnotationGatewayConsulServiceName: "mesh-gateway", + constants.AnnotationGatewayWANSource: "Static", + constants.AnnotationGatewayWANAddress: "2.3.4.5", + constants.AnnotationGatewayWANPort: "443", + constants.AnnotationMeshGatewayContainerPort: "8443", + constants.AnnotationGatewayKind: meshGateway}) + endpoint := &corev1.Endpoints{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mesh-gateway", + Namespace: "default", + }, + Subsets: []corev1.EndpointSubset{ + { + Addresses: []corev1.EndpointAddress{ + { + IP: "1.2.3.4", + TargetRef: &corev1.ObjectReference{ + Kind: "Pod", + Name: "mesh-gateway", + Namespace: "default", + }, + }, + }, + }, + }, + } + return []runtime.Object{gateway, endpoint} + }, expectedConsulSvcInstances: []*api.CatalogService{ { ServiceID: "mesh-gateway", @@ -1295,8 +1379,10 @@ func TestReconcileCreateEndpoint(t *testing.T) { metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true", }, - ServiceTags: []string{}, - ServiceProxy: &api.AgentServiceConnectProxyConfig{}, + ServiceTags: []string{}, + ServiceProxy: &api.AgentServiceConnectProxyConfig{ + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/service")}, + }, }, }, expectedHealthChecks: []*api.HealthCheck{ @@ -1359,7 +1445,8 @@ func TestReconcileCreateEndpoint(t *testing.T) { ServiceTags: []string{}, ServiceProxy: &api.AgentServiceConnectProxyConfig{ Config: map[string]interface{}{ - "envoy_prometheus_bind_addr": "1.2.3.4:20200", + "envoy_prometheus_bind_addr": "1.2.3.4:20200", + "envoy_telemetry_collector_bind_socket_dir": "/consul/service", }, }, }, @@ -1459,6 +1546,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { "address": "0.0.0.0", }, }, + "envoy_telemetry_collector_bind_socket_dir": "/consul/service", }, }, }, @@ -1559,7 +1647,8 @@ func TestReconcileCreateEndpoint(t *testing.T) { "address": "0.0.0.0", }, }, - "envoy_prometheus_bind_addr": "1.2.3.4:20200", + "envoy_prometheus_bind_addr": "1.2.3.4:20200", + "envoy_telemetry_collector_bind_socket_dir": "/consul/service", }, }, }, @@ -1644,6 +1733,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod1-service-created", LocalServiceAddress: "", LocalServicePort: 0, + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod1", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1658,6 +1748,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod2-service-created", LocalServiceAddress: "", LocalServicePort: 0, + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod2", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1783,6 +1874,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod1-service-created", LocalServiceAddress: "", LocalServicePort: 0, + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod1", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1797,6 +1889,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod2-service-created", LocalServiceAddress: "", LocalServicePort: 0, + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod2", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -1909,7 +2002,8 @@ func TestReconcileCreateEndpoint(t *testing.T) { }, }, Config: map[string]interface{}{ - "envoy_prometheus_bind_addr": "0.0.0.0:12345", + "envoy_prometheus_bind_addr": "0.0.0.0:12345", + "envoy_telemetry_collector_bind_socket_dir": "/consul/connect-inject", }, }, ServiceMeta: map[string]string{ @@ -2010,6 +2104,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { DestinationServiceID: "pod1-service-created", LocalServiceAddress: "", LocalServicePort: 0, + Config: map[string]any{"envoy_telemetry_collector_bind_socket_dir": string("/consul/connect-inject")}, }, ServiceMeta: map[string]string{constants.MetaKeyPodName: "pod1", metaKeyKubeServiceName: "service-created", constants.MetaKeyKubeNS: "default", metaKeyManagedBy: constants.ManagedByValue, metaKeySyntheticNode: "true"}, ServiceTags: []string{}, @@ -2054,7 +2149,7 @@ func TestReconcileCreateEndpoint(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -2069,6 +2164,9 @@ func TestReconcileCreateEndpoint(t *testing.T) { EnableGatewayMetrics: true, } } + + ep.EnableTelemetryCollector = !tt.telemetryCollectorDisabled + namespacedName := types.NamespacedName{ Namespace: "default", Name: tt.svcName, @@ -2198,6 +2296,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -2217,6 +2318,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -2303,6 +2407,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -2322,6 +2429,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: "127.0.0.1", + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -2408,6 +2518,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -2425,6 +2538,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -2490,6 +2606,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-different-consul-svc-name", Service: "different-consul-svc-name", @@ -2507,6 +2626,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-different-consul-svc-name-sidecar-proxy", @@ -2580,6 +2702,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -2591,6 +2716,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -2694,6 +2822,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -2705,6 +2836,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -2721,6 +2855,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod2-service-updated", Service: "service-updated", @@ -2732,6 +2869,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod2-service-updated-sidecar-proxy", @@ -2791,6 +2931,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-different-consul-svc-name", Service: "different-consul-svc-name", @@ -2802,6 +2945,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-different-consul-svc-name-sidecar-proxy", @@ -2818,6 +2964,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod2-different-consul-svc-name", Service: "different-consul-svc-name", @@ -2829,6 +2978,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod2-different-consul-svc-name-sidecar-proxy", @@ -2874,6 +3026,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -2885,6 +3040,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -2901,6 +3059,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod2-service-updated", Service: "service-updated", @@ -2912,6 +3073,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod2-service-updated-sidecar-proxy", @@ -2947,6 +3111,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-different-consul-svc-name", Service: "different-consul-svc-name", @@ -2958,6 +3125,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-different-consul-svc-name-sidecar-proxy", @@ -2974,6 +3144,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod2-different-consul-svc-name", Service: "different-consul-svc-name", @@ -2985,6 +3158,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod2-different-consul-svc-name-sidecar-proxy", @@ -3033,6 +3209,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -3050,6 +3229,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -3129,6 +3311,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -3146,6 +3331,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -3168,6 +3356,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod2-service-updated", Service: "service-updated", @@ -3185,6 +3376,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod2-service-updated-sidecar-proxy", @@ -3268,6 +3462,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-service-updated", Service: "service-updated", @@ -3285,6 +3482,9 @@ func TestReconcileUpdateEndpoint(t *testing.T) { { Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ Kind: api.ServiceKindConnectProxy, ID: "pod1-service-updated-sidecar-proxy", @@ -3374,7 +3574,7 @@ func TestReconcileUpdateEndpoint(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -3439,7 +3639,7 @@ func TestReconcileUpdateEndpoint(t *testing.T) { // Read the token from Consul. token, _, err := consulClient.ACL().TokenRead(tokenID, nil) if deregisteredServices.Contains(sID) { - require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") + require.Contains(t, err.Error(), "ACL not found") } else { require.NoError(t, err, "token should exist for service instance: "+sID) require.NotNil(t, token) @@ -3624,7 +3824,7 @@ func TestReconcileUpdateEndpoint_LegacyService(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -3975,6 +4175,9 @@ func TestReconcileDeleteEndpoint(t *testing.T) { serviceRegistration := &api.CatalogRegistration{ Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: svc, } _, err := consulClient.Catalog().Register(serviceRegistration, nil) @@ -3998,7 +4201,7 @@ func TestReconcileDeleteEndpoint(t *testing.T) { // Create the endpoints controller ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -4037,7 +4240,7 @@ func TestReconcileDeleteEndpoint(t *testing.T) { if tt.enableACLs { _, _, err = consulClient.ACL().TokenRead(token.AccessorID, nil) - require.EqualError(t, err, "Unexpected response code: 403 (ACL not found)") + require.Contains(t, err.Error(), "ACL not found") } }) } @@ -4121,6 +4324,9 @@ func TestReconcileIgnoresServiceIgnoreLabel(t *testing.T) { serviceRegistration := &api.CatalogRegistration{ Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-" + svcName, Service: svcName, @@ -4143,7 +4349,7 @@ func TestReconcileIgnoresServiceIgnoreLabel(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -4229,7 +4435,7 @@ func TestReconcile_podSpecifiesExplicitService(t *testing.T) { // Create the endpoints controller. ep := &Controller{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, AllowK8sNamespacesSet: mapset.NewSetWith("*"), @@ -4244,6 +4450,9 @@ func TestReconcile_podSpecifiesExplicitService(t *testing.T) { _, err := consulClient.Catalog().Register(&api.CatalogRegistration{ Node: consulNodeName, Address: consulNodeAddress, + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: &api.AgentService{ ID: "pod1-" + svcName, Service: svcName, @@ -4401,6 +4610,9 @@ func TestServiceInstancesForK8SServiceNameAndNamespace(t *testing.T) { catalogRegistration := &api.CatalogRegistration{ Node: consulNodeName, Address: "127.0.0.1", + NodeMeta: map[string]string{ + metaKeySyntheticNode: "true", + }, Service: svc, } _, err = consulClient.Catalog().Register(catalogRegistration, nil) @@ -5058,7 +5270,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300), }, @@ -5116,7 +5328,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300), }, @@ -5173,7 +5385,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300), }, @@ -5219,7 +5431,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20400), }, @@ -5276,7 +5488,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20500), }, @@ -5333,21 +5545,21 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20400), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20500), }, @@ -5412,21 +5624,21 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20400), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20500), }, @@ -5446,21 +5658,21 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300 + 1), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20400 + 1), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20500 + 1), }, @@ -5546,7 +5758,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ TCPSocket: &corev1.TCPSocketAction{ Port: intstr.FromInt(8080), }, @@ -5598,21 +5810,21 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20300), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20400), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(20500), }, @@ -5708,7 +5920,7 @@ func TestCreateServiceRegistrations_withTransparentProxy(t *testing.T) { Client: fakeClient, EnableTransparentProxy: c.tproxyGlobalEnabled, TProxyOverwriteProbes: c.overwriteProbes, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), } serviceRegistration, proxyServiceRegistration, err := epCtrl.createServiceRegistrations(*pod, *endpoints, api.HealthPassing) diff --git a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go index 15a3740816..30db612602 100644 --- a/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go +++ b/control-plane/connect-inject/controllers/peering/peering_acceptor_controller_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/helper/test" @@ -19,7 +19,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/pointer" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -498,7 +497,8 @@ func TestReconcile_CreateUpdatePeeringAcceptor(t *testing.T) { // Create fake k8s client k8sObjects := append(tt.k8sObjects(), &ns) - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringAcceptor{}, &v1alpha1.PeeringAcceptorList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() @@ -517,7 +517,7 @@ func TestReconcile_CreateUpdatePeeringAcceptor(t *testing.T) { Client: fakeClient, ExposeServersServiceName: "test-expose-servers", ReleaseNamespace: "default", - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -619,7 +619,8 @@ func TestReconcile_DeletePeeringAcceptor(t *testing.T) { k8sObjects := []runtime.Object{&ns, acceptor} // Add peering types to the scheme. - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringAcceptor{}, &v1alpha1.PeeringAcceptorList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() @@ -635,7 +636,7 @@ func TestReconcile_DeletePeeringAcceptor(t *testing.T) { // Create the peering acceptor controller. controller := &AcceptorController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -765,7 +766,8 @@ func TestReconcile_VersionAnnotation(t *testing.T) { // Create fake k8s client k8sObjects := []runtime.Object{acceptor, secret, ns} - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringAcceptor{}, &v1alpha1.PeeringAcceptorList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() @@ -779,7 +781,7 @@ func TestReconcile_VersionAnnotation(t *testing.T) { // Create the peering acceptor controller controller := &AcceptorController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -1077,13 +1079,14 @@ func TestAcceptorUpdateStatus(t *testing.T) { k8sObjects = append(k8sObjects, tt.peeringAcceptor) // Add peering types to the scheme. - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringAcceptor{}, &v1alpha1.PeeringAcceptorList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() // Create the peering acceptor controller. pac := &AcceptorController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), Scheme: s, } @@ -1189,13 +1192,14 @@ func TestAcceptorUpdateStatusError(t *testing.T) { k8sObjects = append(k8sObjects, tt.acceptor) // Add peering types to the scheme. - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringAcceptor{}, &v1alpha1.PeeringAcceptorList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() // Create the peering acceptor controller. controller := &AcceptorController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), Scheme: s, } @@ -1473,12 +1477,13 @@ func TestAcceptor_RequestsForPeeringTokens(t *testing.T) { for name, tt := range cases { t.Run(name, func(t *testing.T) { - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringAcceptor{}, &v1alpha1.PeeringAcceptorList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tt.secret, &tt.acceptors).Build() controller := AcceptorController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), } result := controller.requestsForPeeringTokens(tt.secret) diff --git a/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go b/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go index ba33afd765..3bc959ab0a 100644 --- a/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go +++ b/control-plane/connect-inject/controllers/peering/peering_dialer_controller_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/consul" @@ -21,7 +21,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes/scheme" "k8s.io/utils/pointer" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -277,9 +276,11 @@ func TestReconcile_CreateUpdatePeeringDialer(t *testing.T) { var encodedPeeringToken string if tt.peeringName != "" { // Create the initial token. - baseToken, _, err := acceptorClient.Peerings().GenerateToken(context.Background(), api.PeeringGenerateTokenRequest{PeerName: tt.peeringName}, nil) - require.NoError(t, err) - encodedPeeringToken = baseToken.PeeringToken + retry.Run(t, func(r *retry.R) { + baseToken, _, err := acceptorClient.Peerings().GenerateToken(context.Background(), api.PeeringGenerateTokenRequest{PeerName: tt.peeringName}, nil) + require.NoError(r, err) + encodedPeeringToken = baseToken.PeeringToken + }) } // If the peering is not supposed to already exist in Consul, then create a secret with the generated token. @@ -311,14 +312,15 @@ func TestReconcile_CreateUpdatePeeringDialer(t *testing.T) { secret.SetResourceVersion("latest-version") k8sObjects = append(k8sObjects, secret) } - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringDialer{}, &v1alpha1.PeeringDialerList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() // Create the peering dialer controller controller := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -479,8 +481,11 @@ func TestReconcile_VersionAnnotationPeeringDialer(t *testing.T) { // Create a peering connection in Consul by generating and establishing a peering connection before calling // Reconcile(). // Generate a token. - generatedToken, _, err := acceptorClient.Peerings().GenerateToken(context.Background(), api.PeeringGenerateTokenRequest{PeerName: "peering"}, nil) - require.NoError(t, err) + var generatedToken *api.PeeringGenerateTokenResponse + retry.Run(t, func(r *retry.R) { + generatedToken, _, err = acceptorClient.Peerings().GenerateToken(context.Background(), api.PeeringGenerateTokenRequest{PeerName: "peering"}, nil) + require.NoError(r, err) + }) // Create test consul server. var testServerCfg *testutil.TestServerConfig @@ -521,14 +526,15 @@ func TestReconcile_VersionAnnotationPeeringDialer(t *testing.T) { secret.SetResourceVersion("latest-version") k8sObjects = append(k8sObjects, secret) - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringDialer{}, &v1alpha1.PeeringDialerList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() // Create the peering dialer controller controller := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: consulConfig, ConsulServerConnMgr: watcher, Scheme: s, @@ -737,7 +743,8 @@ func TestReconcileDeletePeeringDialer(t *testing.T) { k8sObjects := []runtime.Object{ns, dialer} // Add peering types to the scheme. - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringDialer{}, &v1alpha1.PeeringDialerList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() @@ -752,7 +759,7 @@ func TestReconcileDeletePeeringDialer(t *testing.T) { // Create the peering dialer controller. pdc := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, Scheme: s, @@ -878,13 +885,14 @@ func TestDialerUpdateStatus(t *testing.T) { k8sObjects = append(k8sObjects, tt.peeringDialer) // Add peering types to the scheme. - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringDialer{}, &v1alpha1.PeeringDialerList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() // Create the peering dialer controller. controller := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), Scheme: s, } @@ -990,13 +998,14 @@ func TestDialerUpdateStatusError(t *testing.T) { k8sObjects = append(k8sObjects, tt.dialer) // Add peering types to the scheme. - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringDialer{}, &v1alpha1.PeeringDialerList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(k8sObjects...).Build() // Create the peering dialer controller. controller := &PeeringDialerController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), Scheme: s, } @@ -1274,12 +1283,13 @@ func TestDialer_RequestsForPeeringTokens(t *testing.T) { for name, tt := range cases { t.Run(name, func(t *testing.T) { - s := scheme.Scheme + s := runtime.NewScheme() + corev1.AddToScheme(s) s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.PeeringDialer{}, &v1alpha1.PeeringDialerList{}) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(tt.secret, &tt.dialers).Build() controller := PeeringDialerController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), } result := controller.requestsForPeeringTokens(tt.secret) diff --git a/control-plane/connect-inject/lifecycle/lifecycle_configuration.go b/control-plane/connect-inject/lifecycle/lifecycle_configuration.go new file mode 100644 index 0000000000..651d4eecae --- /dev/null +++ b/control-plane/connect-inject/lifecycle/lifecycle_configuration.go @@ -0,0 +1,95 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package lifecycle + +import ( + "fmt" + "strconv" + + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + corev1 "k8s.io/api/core/v1" +) + +// Config represents configuration common to connect-inject components related to proxy lifecycle management. +type Config struct { + DefaultEnableProxyLifecycle bool + DefaultEnableShutdownDrainListeners bool + DefaultShutdownGracePeriodSeconds int + DefaultGracefulPort string + DefaultGracefulShutdownPath string +} + +// EnableProxyLifecycle returns whether proxy lifecycle management is enabled either via the default value in the meshWebhook, or if it's been +// overridden via the annotation. +func (lc Config) EnableProxyLifecycle(pod corev1.Pod) (bool, error) { + enabled := lc.DefaultEnableProxyLifecycle + if raw, ok := pod.Annotations[constants.AnnotationEnableSidecarProxyLifecycle]; ok && raw != "" { + enableProxyLifecycle, err := strconv.ParseBool(raw) + if err != nil { + return false, fmt.Errorf("%s annotation value of %s was invalid: %s", constants.AnnotationEnableSidecarProxyLifecycle, raw, err) + } + enabled = enableProxyLifecycle + } + return enabled, nil +} + +// EnableShutdownDrainListeners returns whether proxy listener draining during shutdown is enabled either via the default value in the meshWebhook, or if it's been +// overridden via the annotation. +func (lc Config) EnableShutdownDrainListeners(pod corev1.Pod) (bool, error) { + enabled := lc.DefaultEnableShutdownDrainListeners + if raw, ok := pod.Annotations[constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners]; ok && raw != "" { + enableShutdownDrainListeners, err := strconv.ParseBool(raw) + if err != nil { + return false, fmt.Errorf("%s annotation value of %s was invalid: %s", constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners, raw, err) + } + enabled = enableShutdownDrainListeners + } + return enabled, nil +} + +// ShutdownGracePeriodSeconds returns how long the sidecar proxy should wait before shutdown, either via the default value in the meshWebhook, or if it's been +// overridden via the annotation. +func (lc Config) ShutdownGracePeriodSeconds(pod corev1.Pod) (int, error) { + shutdownGracePeriodSeconds := lc.DefaultShutdownGracePeriodSeconds + if shutdownGracePeriodSecondsAnnotation, ok := pod.Annotations[constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds]; ok { + val, err := strconv.ParseUint(shutdownGracePeriodSecondsAnnotation, 10, 64) + if err != nil { + return 0, fmt.Errorf("unable to parse annotation %q: %w", constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds, err) + } + shutdownGracePeriodSeconds = int(val) + } + return shutdownGracePeriodSeconds, nil +} + +// GracefulPort returns the port on which consul-dataplane should serve the proxy lifecycle management HTTP endpoints, either via the default value in the meshWebhook, or +// if it's been overridden via the annotation. It also validates the port is in the unprivileged port range. +func (lc Config) GracefulPort(pod corev1.Pod) (int, error) { + anno, err := common.DetermineAndValidatePort(pod, constants.AnnotationSidecarProxyLifecycleGracefulPort, lc.DefaultGracefulPort, false) + if err != nil { + return 0, err + } + + if anno == "" { + return constants.DefaultGracefulPort, nil + } + + port, _ := strconv.Atoi(anno) + + return port, nil +} + +// GracefulShutdownPath returns the path on which consul-dataplane should serve the graceful shutdown HTTP endpoint, either via the default value in the meshWebhook, or +// if it's been overridden via the annotation. +func (lc Config) GracefulShutdownPath(pod corev1.Pod) string { + if raw, ok := pod.Annotations[constants.AnnotationSidecarProxyLifecycleGracefulShutdownPath]; ok && raw != "" { + return raw + } + + if lc.DefaultGracefulShutdownPath == "" { + return constants.DefaultGracefulShutdownPath + } + + return lc.DefaultGracefulShutdownPath +} diff --git a/control-plane/connect-inject/lifecycle/lifecycle_configuration_test.go b/control-plane/connect-inject/lifecycle/lifecycle_configuration_test.go new file mode 100644 index 0000000000..64157a3d55 --- /dev/null +++ b/control-plane/connect-inject/lifecycle/lifecycle_configuration_test.go @@ -0,0 +1,351 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package lifecycle + +import ( + "testing" + + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/namespaces" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestLifecycleConfig_EnableSidecarProxyLifecycle(t *testing.T) { + cases := []struct { + Name string + Pod func(*corev1.Pod) *corev1.Pod + LifecycleConfig Config + Expected bool + Err string + }{ + { + Name: "Sidecar proxy lifecycle management enabled via meshWebhook", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + LifecycleConfig: Config{ + DefaultEnableProxyLifecycle: true, + }, + Expected: true, + Err: "", + }, + { + Name: "Sidecar proxy lifecycle management enabled via annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationEnableSidecarProxyLifecycle] = "true" + return pod + }, + LifecycleConfig: Config{ + DefaultEnableProxyLifecycle: false, + }, + Expected: true, + Err: "", + }, + { + Name: "Sidecar proxy lifecycle management configured via invalid annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationEnableSidecarProxyLifecycle] = "not-a-bool" + return pod + }, + LifecycleConfig: Config{ + DefaultEnableProxyLifecycle: false, + }, + Expected: false, + Err: "consul.hashicorp.com/enable-sidecar-proxy-lifecycle annotation value of not-a-bool was invalid: strconv.ParseBool: parsing \"not-a-bool\": invalid syntax", + }, + } + + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + require := require.New(t) + lc := tt.LifecycleConfig + + actual, err := lc.EnableProxyLifecycle(*tt.Pod(minimal())) + + if tt.Err == "" { + require.Equal(tt.Expected, actual) + require.NoError(err) + } else { + require.EqualError(err, tt.Err) + } + }) + } +} + +func TestLifecycleConfig_ShutdownDrainListeners(t *testing.T) { + cases := []struct { + Name string + Pod func(*corev1.Pod) *corev1.Pod + LifecycleConfig Config + Expected bool + Err string + }{ + { + Name: "Sidecar proxy shutdown listener draining enabled via meshWebhook", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + LifecycleConfig: Config{ + DefaultEnableShutdownDrainListeners: true, + }, + Expected: true, + Err: "", + }, + { + Name: "Sidecar proxy shutdown listener draining enabled via annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners] = "true" + return pod + }, + LifecycleConfig: Config{ + DefaultEnableShutdownDrainListeners: false, + }, + Expected: true, + Err: "", + }, + { + Name: "Sidecar proxy shutdown listener draining configured via invalid annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners] = "not-a-bool" + return pod + }, + Expected: false, + Err: "consul.hashicorp.com/enable-sidecar-proxy-lifecycle-shutdown-drain-listeners annotation value of not-a-bool was invalid: strconv.ParseBool: parsing \"not-a-bool\": invalid syntax", + }, + } + + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + require := require.New(t) + lc := tt.LifecycleConfig + + actual, err := lc.EnableShutdownDrainListeners(*tt.Pod(minimal())) + + if tt.Err == "" { + require.Equal(tt.Expected, actual) + require.NoError(err) + } else { + require.EqualError(err, tt.Err) + } + }) + } +} + +func TestLifecycleConfig_ShutdownGracePeriodSeconds(t *testing.T) { + cases := []struct { + Name string + Pod func(*corev1.Pod) *corev1.Pod + LifecycleConfig Config + Expected int + Err string + }{ + { + Name: "Sidecar proxy shutdown grace period set via meshWebhook", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + LifecycleConfig: Config{ + DefaultShutdownGracePeriodSeconds: 10, + }, + Expected: 10, + Err: "", + }, + { + Name: "Sidecar proxy shutdown grace period set via annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds] = "20" + return pod + }, + LifecycleConfig: Config{ + DefaultShutdownGracePeriodSeconds: 10, + }, + Expected: 20, + Err: "", + }, + { + Name: "Sidecar proxy shutdown grace period configured via invalid annotation, negative number", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds] = "-1" + return pod + }, + Err: "unable to parse annotation \"consul.hashicorp.com/sidecar-proxy-lifecycle-shutdown-grace-period-seconds\": strconv.ParseUint: parsing \"-1\": invalid syntax", + }, + { + Name: "Sidecar proxy shutdown grace period configured via invalid annotation, not-parseable string", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds] = "not-int" + return pod + }, + Err: "unable to parse annotation \"consul.hashicorp.com/sidecar-proxy-lifecycle-shutdown-grace-period-seconds\": strconv.ParseUint: parsing \"not-int\": invalid syntax", + }, + } + + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + require := require.New(t) + lc := tt.LifecycleConfig + + actual, err := lc.ShutdownGracePeriodSeconds(*tt.Pod(minimal())) + + if tt.Err == "" { + require.Equal(tt.Expected, actual) + require.NoError(err) + } else { + require.EqualError(err, tt.Err) + } + }) + } +} + +func TestLifecycleConfig_GracefulPort(t *testing.T) { + cases := []struct { + Name string + Pod func(*corev1.Pod) *corev1.Pod + LifecycleConfig Config + Expected int + Err string + }{ + { + Name: "Sidecar proxy lifecycle graceful port set to default", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + Expected: constants.DefaultGracefulPort, + Err: "", + }, + { + Name: "Sidecar proxy lifecycle graceful port set via meshWebhook", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + LifecycleConfig: Config{ + DefaultGracefulPort: "3000", + }, + Expected: 3000, + Err: "", + }, + { + Name: "Sidecar proxy lifecycle graceful port set via annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleGracefulPort] = "9000" + return pod + }, + LifecycleConfig: Config{ + DefaultGracefulPort: "3000", + }, + Expected: 9000, + Err: "", + }, + { + Name: "Sidecar proxy lifecycle graceful port configured via invalid annotation, negative number", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleGracefulPort] = "-1" + return pod + }, + Err: "consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-port annotation value of -1 is not in the unprivileged port range 1024-65535", + }, + { + Name: "Sidecar proxy lifecycle graceful port configured via invalid annotation, not-parseable string", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleGracefulPort] = "not-int" + return pod + }, + Err: "consul.hashicorp.com/sidecar-proxy-lifecycle-graceful-port annotation value of not-int is not a valid integer", + }, + } + + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + require := require.New(t) + lc := tt.LifecycleConfig + + actual, err := lc.GracefulPort(*tt.Pod(minimal())) + + if tt.Err == "" { + require.Equal(tt.Expected, actual) + require.NoError(err) + } else { + require.EqualError(err, tt.Err) + } + }) + } +} + +func TestLifecycleConfig_GracefulShutdownPath(t *testing.T) { + cases := []struct { + Name string + Pod func(*corev1.Pod) *corev1.Pod + LifecycleConfig Config + Expected string + Err string + }{ + { + Name: "Sidecar proxy lifecycle graceful shutdown path defaults to /graceful_shutdown", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + Expected: "/graceful_shutdown", + Err: "", + }, + { + Name: "Sidecar proxy lifecycle graceful shutdown path set via meshWebhook", + Pod: func(pod *corev1.Pod) *corev1.Pod { + return pod + }, + LifecycleConfig: Config{ + DefaultGracefulShutdownPath: "/quit", + }, + Expected: "/quit", + Err: "", + }, + { + Name: "Sidecar proxy lifecycle graceful port set via annotation", + Pod: func(pod *corev1.Pod) *corev1.Pod { + pod.Annotations[constants.AnnotationSidecarProxyLifecycleGracefulShutdownPath] = "/custom-shutdown-path" + return pod + }, + LifecycleConfig: Config{ + DefaultGracefulShutdownPath: "/quit", + }, + Expected: "/custom-shutdown-path", + Err: "", + }, + } + + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + require := require.New(t) + lc := tt.LifecycleConfig + + actual := lc.GracefulShutdownPath(*tt.Pod(minimal())) + + require.Equal(tt.Expected, actual) + }) + } +} + +func minimal() *corev1.Pod { + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespaces.DefaultNamespace, + Name: "minimal", + Annotations: map[string]string{ + constants.AnnotationService: "foo", + }, + }, + + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + { + Name: "web-side", + }, + }, + }, + } +} diff --git a/control-plane/connect-inject/metrics/metrics_configuration.go b/control-plane/connect-inject/metrics/metrics_configuration.go index 651fb87184..09f01a4c87 100644 --- a/control-plane/connect-inject/metrics/metrics_configuration.go +++ b/control-plane/connect-inject/metrics/metrics_configuration.go @@ -95,13 +95,13 @@ func (mc Config) EnableMetricsMerging(pod corev1.Pod) (bool, error) { // MergedMetricsPort returns the port to run the merged metrics server on, either via the default value in the meshWebhook, // or if it's been overridden via the annotation. It also validates the port is in the unprivileged port range. func (mc Config) MergedMetricsPort(pod corev1.Pod) (string, error) { - return determineAndValidatePort(pod, constants.AnnotationMergedMetricsPort, mc.DefaultMergedMetricsPort, false) + return common.DetermineAndValidatePort(pod, constants.AnnotationMergedMetricsPort, mc.DefaultMergedMetricsPort, false) } // PrometheusScrapePort returns the port for Prometheus to scrape from, either via the default value in the meshWebhook, or // if it's been overridden via the annotation. It also validates the port is in the unprivileged port range. func (mc Config) PrometheusScrapePort(pod corev1.Pod) (string, error) { - return determineAndValidatePort(pod, constants.AnnotationPrometheusScrapePort, mc.DefaultPrometheusScrapePort, false) + return common.DetermineAndValidatePort(pod, constants.AnnotationPrometheusScrapePort, mc.DefaultPrometheusScrapePort, false) } // PrometheusScrapePath returns the path for Prometheus to scrape from, either via the default value in the meshWebhook, or @@ -130,14 +130,14 @@ func (mc Config) ServiceMetricsPort(pod corev1.Pod) (string, error) { // written their service in such a way that it expects to be able to use // privileged ports. So, the port metrics are exposed on the service can // be privileged. - return determineAndValidatePort(pod, constants.AnnotationServiceMetricsPort, raw, true) + return common.DetermineAndValidatePort(pod, constants.AnnotationServiceMetricsPort, raw, true) } // If the annotationPort is not set, the serviceMetrics port will be 0 // unless overridden by the service-metrics-port annotation. If the service // metrics port is 0, the consul sidecar will not run a merged metrics // server. - return determineAndValidatePort(pod, constants.AnnotationServiceMetricsPort, "0", true) + return common.DetermineAndValidatePort(pod, constants.AnnotationServiceMetricsPort, "0", true) } // ServiceMetricsPath returns a default of /metrics, or overrides @@ -177,37 +177,3 @@ func (mc Config) ShouldRunMergedMetricsServer(pod corev1.Pod) (bool, error) { } return false, nil } - -// determineAndValidatePort behaves as follows: -// If the annotation exists, validate the port and return it. -// If the annotation does not exist, return the default port. -// If the privileged flag is true, it will allow the port to be in the -// privileged port range of 1-1023. Otherwise, it will only allow ports in the -// unprivileged range of 1024-65535. -func determineAndValidatePort(pod corev1.Pod, annotation string, defaultPort string, privileged bool) (string, error) { - if raw, ok := pod.Annotations[annotation]; ok && raw != "" { - port, err := common.PortValue(pod, raw) - if err != nil { - return "", fmt.Errorf("%s annotation value of %s is not a valid integer", annotation, raw) - } - - if privileged && (port < 1 || port > 65535) { - return "", fmt.Errorf("%s annotation value of %d is not in the valid port range 1-65535", annotation, port) - } else if !privileged && (port < 1024 || port > 65535) { - return "", fmt.Errorf("%s annotation value of %d is not in the unprivileged port range 1024-65535", annotation, port) - } - - // If the annotation exists, return the validated port. - return fmt.Sprint(port), nil - } - - // If the annotation does not exist, return the default. - if defaultPort != "" { - port, err := common.PortValue(pod, defaultPort) - if err != nil { - return "", fmt.Errorf("%s is not a valid port on the pod %s", defaultPort, pod.Name) - } - return fmt.Sprint(port), nil - } - return "", nil -} diff --git a/control-plane/connect-inject/metrics/metrics_configuration_test.go b/control-plane/connect-inject/metrics/metrics_configuration_test.go index 2f41b05744..ea232576b5 100644 --- a/control-plane/connect-inject/metrics/metrics_configuration_test.go +++ b/control-plane/connect-inject/metrics/metrics_configuration_test.go @@ -304,149 +304,6 @@ func TestMetricsConfigShouldRunMergedMetricsServer(t *testing.T) { } } -// Tests determineAndValidatePort, which in turn tests the -// PrometheusScrapePort() and MergedMetricsPort() functions because their logic -// is just to call out to determineAndValidatePort(). -func TestMetricsConfigDetermineAndValidatePort(t *testing.T) { - cases := []struct { - Name string - Pod func(*corev1.Pod) *corev1.Pod - Annotation string - Privileged bool - DefaultPort string - Expected string - Err string - }{ - { - Name: "Valid annotation", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "1234" - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - Expected: "1234", - Err: "", - }, - { - Name: "Uses default when there's no annotation", - Pod: func(pod *corev1.Pod) *corev1.Pod { - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - DefaultPort: "4321", - Expected: "4321", - Err: "", - }, - { - Name: "Gets the value of the named default port when there's no annotation", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Spec.Containers[0].Ports = []corev1.ContainerPort{ - { - Name: "web-port", - ContainerPort: 2222, - }, - } - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - DefaultPort: "web-port", - Expected: "2222", - Err: "", - }, - { - Name: "Errors if the named default port doesn't exist on the pod", - Pod: func(pod *corev1.Pod) *corev1.Pod { - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - DefaultPort: "web-port", - Expected: "", - Err: "web-port is not a valid port on the pod minimal", - }, - { - Name: "Gets the value of the named port", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "web-port" - pod.Spec.Containers[0].Ports = []corev1.ContainerPort{ - { - Name: "web-port", - ContainerPort: 2222, - }, - } - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - DefaultPort: "4321", - Expected: "2222", - Err: "", - }, - { - Name: "Invalid annotation (not an integer)", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "not-an-int" - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - Expected: "", - Err: "consul.hashicorp.com/test-annotation-port annotation value of not-an-int is not a valid integer", - }, - { - Name: "Invalid annotation (integer not in port range)", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "100000" - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: true, - Expected: "", - Err: "consul.hashicorp.com/test-annotation-port annotation value of 100000 is not in the valid port range 1-65535", - }, - { - Name: "Invalid annotation (integer not in unprivileged port range)", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "22" - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: false, - Expected: "", - Err: "consul.hashicorp.com/test-annotation-port annotation value of 22 is not in the unprivileged port range 1024-65535", - }, - { - Name: "Privileged ports allowed", - Pod: func(pod *corev1.Pod) *corev1.Pod { - pod.Annotations["consul.hashicorp.com/test-annotation-port"] = "22" - return pod - }, - Annotation: "consul.hashicorp.com/test-annotation-port", - Privileged: true, - Expected: "22", - Err: "", - }, - } - - for _, tt := range cases { - t.Run(tt.Name, func(t *testing.T) { - require := require.New(t) - - actual, err := determineAndValidatePort(*tt.Pod(minimal()), tt.Annotation, tt.DefaultPort, tt.Privileged) - - if tt.Err == "" { - require.NoError(err) - require.Equal(tt.Expected, actual) - } else { - require.EqualError(err, tt.Err) - } - }) - } -} - // Tests MergedMetricsServerConfiguration happy path and error case not covered by other Config tests. func TestMetricsConfigMergedMetricsServerConfiguration(t *testing.T) { cases := []struct { diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go index ad3333ba1b..c1fae3eff9 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar.go @@ -52,7 +52,7 @@ func (w *MeshWebhook) consulDataplaneSidecar(namespace corev1.Namespace, pod cor // If using the proxy health check for a service, configure an HTTP handler // that queries the '/ready' endpoint of the proxy. probe = &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(constants.ProxyDefaultHealthPort + mpi.serviceIndex), Path: "/ready", @@ -62,7 +62,7 @@ func (w *MeshWebhook) consulDataplaneSidecar(namespace corev1.Namespace, pod cor } } else { probe = &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ TCPSocket: &corev1.TCPSocketAction{ Port: intstr.FromInt(constants.ProxyDefaultInboundPort + mpi.serviceIndex), }, @@ -95,6 +95,29 @@ func (w *MeshWebhook) consulDataplaneSidecar(namespace corev1.Namespace, pod cor Name: "DP_SERVICE_NODE_NAME", Value: "$(NODE_NAME)-virtual", }, + // The pod name isn't known currently, so we must rely on the environment variable to fill it in rather than using args. + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{FieldPath: "metadata.name"}, + }, + }, + { + Name: "POD_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{FieldPath: "metadata.namespace"}, + }, + }, + { + Name: "DP_CREDENTIAL_LOGIN_META", + Value: "pod=$(POD_NAMESPACE)/$(POD_NAME)", + }, + // This entry exists to support certain versions of consul dataplane, where environment variable entries + // utilize this numbered notation to indicate individual KV pairs in a map. + { + Name: "DP_CREDENTIAL_LOGIN_META1", + Value: "pod=$(POD_NAMESPACE)/$(POD_NAME)", + }, }, VolumeMounts: []corev1.VolumeMount{ { @@ -205,7 +228,7 @@ func (w *MeshWebhook) getContainerSidecarArgs(namespace corev1.Namespace, mpi mu "-credential-type=login", "-login-auth-method="+w.AuthMethod, "-login-bearer-token-path="+bearerTokenFile, - "-login-meta="+fmt.Sprintf("pod=%s/%s", namespace.Name, pod.Name), + // We don't know the pod name at this time, so we must use environment variables to populate the login-meta instead. ) if w.EnableNamespaces { if w.EnableK8SNSMirroring { @@ -244,6 +267,45 @@ func (w *MeshWebhook) getContainerSidecarArgs(namespace corev1.Namespace, mpi mu args = append(args, fmt.Sprintf("-envoy-admin-bind-port=%d", 19000+mpi.serviceIndex)) } + // The consul-dataplane HTTP listener always starts for graceful shutdown. To avoid port conflicts, the + // graceful port always needs to be set + gracefulPort, err := w.LifecycleConfig.GracefulPort(pod) + if err != nil { + return nil, fmt.Errorf("unable to determine proxy lifecycle graceful port: %w", err) + } + + // To avoid conflicts + if mpi.serviceName != "" { + gracefulPort = gracefulPort + mpi.serviceIndex + } + args = append(args, fmt.Sprintf("-graceful-port=%d", gracefulPort)) + + enableProxyLifecycle, err := w.LifecycleConfig.EnableProxyLifecycle(pod) + if err != nil { + return nil, fmt.Errorf("unable to determine if proxy lifecycle management is enabled: %w", err) + } + if enableProxyLifecycle { + shutdownDrainListeners, err := w.LifecycleConfig.EnableShutdownDrainListeners(pod) + if err != nil { + return nil, fmt.Errorf("unable to determine if proxy lifecycle shutdown listener draining is enabled: %w", err) + } + if shutdownDrainListeners { + args = append(args, "-shutdown-drain-listeners") + } + + shutdownGracePeriodSeconds, err := w.LifecycleConfig.ShutdownGracePeriodSeconds(pod) + if err != nil { + return nil, fmt.Errorf("unable to determine proxy lifecycle shutdown grace period: %w", err) + } + args = append(args, fmt.Sprintf("-shutdown-grace-period-seconds=%d", shutdownGracePeriodSeconds)) + + gracefulShutdownPath := w.LifecycleConfig.GracefulShutdownPath(pod) + if err != nil { + return nil, fmt.Errorf("unable to determine proxy lifecycle graceful shutdown path: %w", err) + } + args = append(args, fmt.Sprintf("-graceful-shutdown-path=%s", gracefulShutdownPath)) + } + // Set a default scrape path that can be overwritten by the annotation. prometheusScrapePath := w.MetricsConfig.PrometheusScrapePath(pod) args = append(args, "-telemetry-prom-scrape-path="+prometheusScrapePath) @@ -311,7 +373,11 @@ func (w *MeshWebhook) getContainerSidecarArgs(namespace corev1.Namespace, mpi mu // If Consul DNS is enabled, we want to configure consul-dataplane to be the DNS proxy // for Consul DNS in the pod. - if w.EnableConsulDNS { + dnsEnabled, err := consulDNSEnabled(namespace, pod, w.EnableConsulDNS, w.EnableTransparentProxy) + if err != nil { + return nil, err + } + if dnsEnabled { args = append(args, "-consul-dns-bind-port="+strconv.Itoa(consulDataplaneDNSBindPort)) } diff --git a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go index 37aa1619bf..e6ce27bab1 100644 --- a/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go +++ b/control-plane/connect-inject/webhook/consul_dataplane_sidecar_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/lifecycle" "github.com/hashicorp/consul-k8s/control-plane/consul" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" @@ -25,20 +26,20 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { }{ "default": { webhookSetupFunc: nil, - additionalExpCmdArgs: " -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with custom gRPC port": { webhookSetupFunc: func(w *MeshWebhook) { w.ConsulConfig.GRPCPort = 8602 }, - additionalExpCmdArgs: " -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with ACLs": { webhookSetupFunc: func(w *MeshWebhook) { w.AuthMethod = "test-auth-method" }, additionalExpCmdArgs: " -credential-type=login -login-auth-method=test-auth-method -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token " + - "-login-meta=pod=k8snamespace/test-pod -tls-disabled -telemetry-prom-scrape-path=/metrics", + "-tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with ACLs and namespace mirroring": { webhookSetupFunc: func(w *MeshWebhook) { @@ -47,7 +48,7 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { w.EnableK8SNSMirroring = true }, additionalExpCmdArgs: " -credential-type=login -login-auth-method=test-auth-method -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token " + - "-login-meta=pod=k8snamespace/test-pod -login-namespace=default -service-namespace=k8snamespace -tls-disabled -telemetry-prom-scrape-path=/metrics", + "-login-namespace=default -service-namespace=k8snamespace -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with ACLs and single destination namespace": { webhookSetupFunc: func(w *MeshWebhook) { @@ -56,7 +57,7 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { w.ConsulDestinationNamespace = "test-ns" }, additionalExpCmdArgs: " -credential-type=login -login-auth-method=test-auth-method -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token " + - "-login-meta=pod=k8snamespace/test-pod -login-namespace=test-ns -service-namespace=test-ns -tls-disabled -telemetry-prom-scrape-path=/metrics", + "-login-namespace=test-ns -service-namespace=test-ns -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with ACLs and partitions": { webhookSetupFunc: func(w *MeshWebhook) { @@ -64,7 +65,7 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { w.ConsulPartition = "test-part" }, additionalExpCmdArgs: " -credential-type=login -login-auth-method=test-auth-method -login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token " + - "-login-meta=pod=k8snamespace/test-pod -login-partition=test-part -service-partition=test-part -tls-disabled -telemetry-prom-scrape-path=/metrics", + "-login-partition=test-part -service-partition=test-part -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with TLS and CA cert provided": { webhookSetupFunc: func(w *MeshWebhook) { @@ -72,28 +73,28 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { w.ConsulTLSServerName = "server.dc1.consul" w.ConsulCACert = "consul-ca-cert" }, - additionalExpCmdArgs: " -tls-server-name=server.dc1.consul -ca-certs=/consul/connect-inject/consul-ca.pem -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -tls-server-name=server.dc1.consul -ca-certs=/consul/connect-inject/consul-ca.pem -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with TLS and no CA cert provided": { webhookSetupFunc: func(w *MeshWebhook) { w.TLSEnabled = true w.ConsulTLSServerName = "server.dc1.consul" }, - additionalExpCmdArgs: " -tls-server-name=server.dc1.consul -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -tls-server-name=server.dc1.consul -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with single destination namespace": { webhookSetupFunc: func(w *MeshWebhook) { w.EnableNamespaces = true w.ConsulDestinationNamespace = "consul-namespace" }, - additionalExpCmdArgs: " -service-namespace=consul-namespace -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -service-namespace=consul-namespace -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with namespace mirroring": { webhookSetupFunc: func(w *MeshWebhook) { w.EnableNamespaces = true w.EnableK8SNSMirroring = true }, - additionalExpCmdArgs: " -service-namespace=k8snamespace -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -service-namespace=k8snamespace -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with namespace mirroring prefix": { webhookSetupFunc: func(w *MeshWebhook) { @@ -101,38 +102,38 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { w.EnableK8SNSMirroring = true w.K8SNSMirroringPrefix = "foo-" }, - additionalExpCmdArgs: " -service-namespace=foo-k8snamespace -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -service-namespace=foo-k8snamespace -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with partitions": { webhookSetupFunc: func(w *MeshWebhook) { w.ConsulPartition = "partition-1" }, - additionalExpCmdArgs: " -service-partition=partition-1 -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -service-partition=partition-1 -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with different log level": { webhookSetupFunc: func(w *MeshWebhook) { w.LogLevel = "debug" }, - additionalExpCmdArgs: " -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "with different log level and log json": { webhookSetupFunc: func(w *MeshWebhook) { w.LogLevel = "debug" w.LogJSON = true }, - additionalExpCmdArgs: " -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "skip server watch enabled": { webhookSetupFunc: func(w *MeshWebhook) { w.SkipServerWatch = true }, - additionalExpCmdArgs: " -server-watch-disabled=true -tls-disabled -telemetry-prom-scrape-path=/metrics", + additionalExpCmdArgs: " -server-watch-disabled=true -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/metrics", }, "custom prometheus scrape path": { webhookSetupFunc: func(w *MeshWebhook) { w.MetricsConfig.DefaultPrometheusScrapePath = "/scrape-path" // Simulate what would be passed as a flag }, - additionalExpCmdArgs: " -tls-disabled -telemetry-prom-scrape-path=/scrape-path", + additionalExpCmdArgs: " -tls-disabled -graceful-port=20600 -telemetry-prom-scrape-path=/scrape-path", }, } @@ -206,7 +207,7 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { } expectedProbe := &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ TCPSocket: &corev1.TCPSocketAction{ Port: intstr.FromInt(constants.ProxyDefaultInboundPort), }, @@ -215,11 +216,17 @@ func TestHandlerConsulDataplaneSidecar(t *testing.T) { } require.Equal(t, expectedProbe, container.ReadinessProbe) require.Nil(t, container.StartupProbe) - require.Len(t, container.Env, 3) + require.Len(t, container.Env, 7) require.Equal(t, container.Env[0].Name, "TMPDIR") require.Equal(t, container.Env[0].Value, "/consul/connect-inject") require.Equal(t, container.Env[2].Name, "DP_SERVICE_NODE_NAME") require.Equal(t, container.Env[2].Value, "$(NODE_NAME)-virtual") + require.Equal(t, container.Env[3].Name, "POD_NAME") + require.Equal(t, container.Env[4].Name, "POD_NAMESPACE") + require.Equal(t, container.Env[5].Name, "DP_CREDENTIAL_LOGIN_META") + require.Equal(t, container.Env[5].Value, "pod=$(POD_NAMESPACE)/$(POD_NAME)") + require.Equal(t, container.Env[6].Name, "DP_CREDENTIAL_LOGIN_META1") + require.Equal(t, container.Env[6].Value, "pod=$(POD_NAMESPACE)/$(POD_NAME)") }) } } @@ -287,24 +294,120 @@ func TestHandlerConsulDataplaneSidecar_Concurrency(t *testing.T) { } } +// Test that we pass the dns proxy flag to dataplane correctly. func TestHandlerConsulDataplaneSidecar_DNSProxy(t *testing.T) { - h := MeshWebhook{ - ConsulConfig: &consul.Config{HTTPPort: 8500, GRPCPort: 8502}, - EnableConsulDNS: true, + + // We only want the flag passed when DNS and tproxy are both enabled. DNS/tproxy can + // both be enabled/disabled with annotations/labels on the pod and namespace and then globally + // through the helm chart. To test this we use an outer loop with the possible DNS settings and then + // and inner loop with possible tproxy settings. + dnsCases := []struct { + GlobalConsulDNS bool + NamespaceDNS *bool + PodDNS *bool + ExpEnabled bool + }{ + { + GlobalConsulDNS: false, + ExpEnabled: false, + }, + { + GlobalConsulDNS: true, + ExpEnabled: true, + }, + { + GlobalConsulDNS: false, + NamespaceDNS: boolPtr(true), + ExpEnabled: true, + }, + { + GlobalConsulDNS: false, + PodDNS: boolPtr(true), + ExpEnabled: true, + }, } - pod := corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{}, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "web", - }, - }, + tproxyCases := []struct { + GlobalTProxy bool + NamespaceTProxy *bool + PodTProxy *bool + ExpEnabled bool + }{ + { + GlobalTProxy: false, + ExpEnabled: false, + }, + { + GlobalTProxy: true, + ExpEnabled: true, + }, + { + GlobalTProxy: false, + NamespaceTProxy: boolPtr(true), + ExpEnabled: true, + }, + { + GlobalTProxy: false, + PodTProxy: boolPtr(true), + ExpEnabled: true, }, } - container, err := h.consulDataplaneSidecar(testNS, pod, multiPortInfo{}) - require.NoError(t, err) - require.Contains(t, container.Args, "-consul-dns-bind-port=8600") + + // Outer loop is permutations of dns being enabled. Inner loop is permutations of tproxy being enabled. + // Both must be enabled for dns to be enabled. + for i, dnsCase := range dnsCases { + for j, tproxyCase := range tproxyCases { + t.Run(fmt.Sprintf("dns=%d,tproxy=%d", i, j), func(t *testing.T) { + + // Test setup. + h := MeshWebhook{ + ConsulConfig: &consul.Config{HTTPPort: 8500, GRPCPort: 8502}, + EnableTransparentProxy: tproxyCase.GlobalTProxy, + EnableConsulDNS: dnsCase.GlobalConsulDNS, + } + pod := corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + }, + }, + } + if dnsCase.PodDNS != nil { + pod.Annotations[constants.KeyConsulDNS] = strconv.FormatBool(*dnsCase.PodDNS) + } + if tproxyCase.PodTProxy != nil { + pod.Annotations[constants.KeyTransparentProxy] = strconv.FormatBool(*tproxyCase.PodTProxy) + } + + ns := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: k8sNamespace, + Labels: map[string]string{}, + }, + } + if dnsCase.NamespaceDNS != nil { + ns.Labels[constants.KeyConsulDNS] = strconv.FormatBool(*dnsCase.NamespaceDNS) + } + if tproxyCase.NamespaceTProxy != nil { + ns.Labels[constants.KeyTransparentProxy] = strconv.FormatBool(*tproxyCase.NamespaceTProxy) + } + + // Actual test here. + container, err := h.consulDataplaneSidecar(ns, pod, multiPortInfo{}) + require.NoError(t, err) + // Flag should only be passed if both tproxy and dns are enabled. + if tproxyCase.ExpEnabled && dnsCase.ExpEnabled { + require.Contains(t, container.Args, "-consul-dns-bind-port=8600") + } else { + require.NotContains(t, container.Args, "-consul-dns-bind-port=8600") + } + }) + } + } } func TestHandlerConsulDataplaneSidecar_ProxyHealthCheck(t *testing.T) { @@ -329,7 +432,7 @@ func TestHandlerConsulDataplaneSidecar_ProxyHealthCheck(t *testing.T) { } container, err := h.consulDataplaneSidecar(testNS, pod, multiPortInfo{}) expectedProbe := &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(21000), Path: "/ready", @@ -415,7 +518,7 @@ func TestHandlerConsulDataplaneSidecar_ProxyHealthCheck_Multiport(t *testing.T) } expectedProbe := []*corev1.Probe{ { - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(21000), Path: "/ready", @@ -424,7 +527,7 @@ func TestHandlerConsulDataplaneSidecar_ProxyHealthCheck_Multiport(t *testing.T) InitialDelaySeconds: 1, }, { - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(21001), Path: "/ready", @@ -523,18 +626,18 @@ func TestHandlerConsulDataplaneSidecar_Multiport(t *testing.T) { } expArgs := []string{ "-addresses 1.1.1.1 -grpc-port=8502 -proxy-service-id-path=/consul/connect-inject/proxyid-web " + - "-log-level=info -log-json=false -envoy-concurrency=0 -tls-disabled -envoy-admin-bind-port=19000 -telemetry-prom-scrape-path=/metrics -- --base-id 0", + "-log-level=info -log-json=false -envoy-concurrency=0 -tls-disabled -envoy-admin-bind-port=19000 -graceful-port=20600 -telemetry-prom-scrape-path=/metrics -- --base-id 0", "-addresses 1.1.1.1 -grpc-port=8502 -proxy-service-id-path=/consul/connect-inject/proxyid-web-admin " + - "-log-level=info -log-json=false -envoy-concurrency=0 -tls-disabled -envoy-admin-bind-port=19001 -telemetry-prom-scrape-path=/metrics -- --base-id 1", + "-log-level=info -log-json=false -envoy-concurrency=0 -tls-disabled -envoy-admin-bind-port=19001 -graceful-port=20601 -telemetry-prom-scrape-path=/metrics -- --base-id 1", } if aclsEnabled { expArgs = []string{ "-addresses 1.1.1.1 -grpc-port=8502 -proxy-service-id-path=/consul/connect-inject/proxyid-web " + "-log-level=info -log-json=false -envoy-concurrency=0 -credential-type=login -login-auth-method=test-auth-method " + - "-login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token -login-meta=pod=k8snamespace/test-pod -tls-disabled -envoy-admin-bind-port=19000 -telemetry-prom-scrape-path=/metrics -- --base-id 0", + "-login-bearer-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token -tls-disabled -envoy-admin-bind-port=19000 -graceful-port=20600 -telemetry-prom-scrape-path=/metrics -- --base-id 0", "-addresses 1.1.1.1 -grpc-port=8502 -proxy-service-id-path=/consul/connect-inject/proxyid-web-admin " + "-log-level=info -log-json=false -envoy-concurrency=0 -credential-type=login -login-auth-method=test-auth-method " + - "-login-bearer-token-path=/consul/serviceaccount-web-admin/token -login-meta=pod=k8snamespace/test-pod -tls-disabled -envoy-admin-bind-port=19001 -telemetry-prom-scrape-path=/metrics -- --base-id 1", + "-login-bearer-token-path=/consul/serviceaccount-web-admin/token -tls-disabled -envoy-admin-bind-port=19001 -graceful-port=20601 -telemetry-prom-scrape-path=/metrics -- --base-id 1", } } expSAVolumeMounts := []corev1.VolumeMount{ @@ -573,7 +676,7 @@ func TestHandlerConsulDataplaneSidecar_Multiport(t *testing.T) { port := constants.ProxyDefaultInboundPort + i expectedProbe := &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ TCPSocket: &corev1.TCPSocketAction{ Port: intstr.FromInt(port), }, @@ -1199,3 +1302,158 @@ func TestHandlerConsulDataplaneSidecar_Metrics(t *testing.T) { }) } } + +func TestHandlerConsulDataplaneSidecar_Lifecycle(t *testing.T) { + gracefulShutdownSeconds := 10 + gracefulPort := "20307" + gracefulShutdownPath := "/exit" + + cases := []struct { + name string + webhook MeshWebhook + annotations map[string]string + expCmdArgs string + expErr string + }{ + { + name: "no defaults, no annotations", + webhook: MeshWebhook{}, + annotations: nil, + expCmdArgs: "", + }, + { + name: "all defaults, no annotations", + webhook: MeshWebhook{ + LifecycleConfig: lifecycle.Config{ + DefaultEnableProxyLifecycle: true, + DefaultEnableShutdownDrainListeners: true, + DefaultShutdownGracePeriodSeconds: gracefulShutdownSeconds, + DefaultGracefulPort: gracefulPort, + DefaultGracefulShutdownPath: gracefulShutdownPath, + }, + }, + annotations: nil, + expCmdArgs: "graceful-port=20307 -shutdown-drain-listeners -shutdown-grace-period-seconds=10 -graceful-shutdown-path=/exit", + }, + { + name: "no defaults, all annotations", + webhook: MeshWebhook{}, + annotations: map[string]string{ + constants.AnnotationEnableSidecarProxyLifecycle: "true", + constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners: "true", + constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds: fmt.Sprint(gracefulShutdownSeconds), + constants.AnnotationSidecarProxyLifecycleGracefulPort: gracefulPort, + constants.AnnotationSidecarProxyLifecycleGracefulShutdownPath: gracefulShutdownPath, + }, + expCmdArgs: "-graceful-port=20307 -shutdown-drain-listeners -shutdown-grace-period-seconds=10 -graceful-shutdown-path=/exit", + }, + { + name: "annotations override defaults", + webhook: MeshWebhook{ + LifecycleConfig: lifecycle.Config{ + DefaultEnableProxyLifecycle: false, + DefaultEnableShutdownDrainListeners: true, + DefaultShutdownGracePeriodSeconds: gracefulShutdownSeconds, + DefaultGracefulPort: gracefulPort, + DefaultGracefulShutdownPath: gracefulShutdownPath, + }, + }, + annotations: map[string]string{ + constants.AnnotationEnableSidecarProxyLifecycle: "true", + constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners: "false", + constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds: fmt.Sprint(gracefulShutdownSeconds + 5), + constants.AnnotationSidecarProxyLifecycleGracefulPort: "20317", + constants.AnnotationSidecarProxyLifecycleGracefulShutdownPath: "/foo", + }, + expCmdArgs: "-graceful-port=20317 -shutdown-grace-period-seconds=15 -graceful-shutdown-path=/foo", + }, + { + name: "lifecycle disabled, no annotations", + webhook: MeshWebhook{ + LifecycleConfig: lifecycle.Config{ + DefaultEnableProxyLifecycle: false, + DefaultEnableShutdownDrainListeners: true, + DefaultShutdownGracePeriodSeconds: gracefulShutdownSeconds, + DefaultGracefulPort: gracefulPort, + DefaultGracefulShutdownPath: gracefulShutdownPath, + }, + }, + annotations: nil, + expCmdArgs: "-graceful-port=20307", + }, + { + name: "lifecycle enabled, defaults omited, no annotations", + webhook: MeshWebhook{ + LifecycleConfig: lifecycle.Config{ + DefaultEnableProxyLifecycle: true, + }, + }, + annotations: nil, + expCmdArgs: "", + }, + { + name: "annotations disable lifecycle default", + webhook: MeshWebhook{ + LifecycleConfig: lifecycle.Config{ + DefaultEnableProxyLifecycle: true, + DefaultEnableShutdownDrainListeners: true, + DefaultShutdownGracePeriodSeconds: gracefulShutdownSeconds, + DefaultGracefulPort: gracefulPort, + DefaultGracefulShutdownPath: gracefulShutdownPath, + }, + }, + annotations: map[string]string{ + constants.AnnotationEnableSidecarProxyLifecycle: "false", + }, + expCmdArgs: "-graceful-port=20307", + }, + { + name: "annotations skip graceful shutdown", + webhook: MeshWebhook{ + LifecycleConfig: lifecycle.Config{ + DefaultEnableProxyLifecycle: false, + DefaultEnableShutdownDrainListeners: true, + DefaultShutdownGracePeriodSeconds: gracefulShutdownSeconds, + }, + }, + annotations: map[string]string{ + constants.AnnotationEnableSidecarProxyLifecycle: "false", + constants.AnnotationEnableSidecarProxyLifecycleShutdownDrainListeners: "false", + constants.AnnotationSidecarProxyLifecycleShutdownGracePeriodSeconds: "0", + }, + expCmdArgs: "", + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + c.webhook.ConsulConfig = &consul.Config{HTTPPort: 8500, GRPCPort: 8502} + require := require.New(t) + pod := corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: c.annotations, + }, + + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + }, + }, + } + container, err := c.webhook.consulDataplaneSidecar(testNS, pod, multiPortInfo{}) + if c.expErr != "" { + require.NotNil(err) + require.Contains(err.Error(), c.expErr) + } else { + require.NoError(err) + require.Contains(strings.Join(container.Args, " "), c.expCmdArgs) + } + }) + } +} + +// boolPtr returns pointer to b. +func boolPtr(b bool) *bool { + return &b +} diff --git a/control-plane/connect-inject/webhook/container_init.go b/control-plane/connect-inject/webhook/container_init.go index 328882bc04..db13485489 100644 --- a/control-plane/connect-inject/webhook/container_init.go +++ b/control-plane/connect-inject/webhook/container_init.go @@ -220,6 +220,12 @@ func (w *MeshWebhook) containerInit(namespace corev1.Namespace, pod corev1.Pod, }) } + // OpenShift without CNI is the only environment where privileged must be true. + privileged := false + if w.EnableOpenShift && !w.EnableCNI { + privileged = true + } + if tproxyEnabled { if !w.EnableCNI { // Set redirect traffic config for the container so that we can apply iptables rules. @@ -240,7 +246,7 @@ func (w *MeshWebhook) containerInit(namespace corev1.Namespace, pod corev1.Pod, RunAsGroup: pointer.Int64(rootUserAndGroupID), // RunAsNonRoot overrides any setting in the Pod so that we can still run as root here as required. RunAsNonRoot: pointer.Bool(false), - Privileged: pointer.Bool(true), + Privileged: pointer.Bool(privileged), Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{netAdminCapability}, }, @@ -250,7 +256,7 @@ func (w *MeshWebhook) containerInit(namespace corev1.Namespace, pod corev1.Pod, RunAsUser: pointer.Int64(initContainersUserAndGroupID), RunAsGroup: pointer.Int64(initContainersUserAndGroupID), RunAsNonRoot: pointer.Bool(true), - Privileged: pointer.Bool(false), + Privileged: pointer.Bool(privileged), Capabilities: &corev1.Capabilities{ Drop: []corev1.Capability{"ALL"}, }, @@ -264,7 +270,17 @@ func (w *MeshWebhook) containerInit(namespace corev1.Namespace, pod corev1.Pod, // consulDNSEnabled returns true if Consul DNS should be enabled for this pod. // It returns an error when the annotation value cannot be parsed by strconv.ParseBool or if we are unable // to read the pod's namespace label when it exists. -func consulDNSEnabled(namespace corev1.Namespace, pod corev1.Pod, globalEnabled bool) (bool, error) { +func consulDNSEnabled(namespace corev1.Namespace, pod corev1.Pod, globalDNSEnabled bool, globalTProxyEnabled bool) (bool, error) { + // DNS is only possible when tproxy is also enabled because it relies + // on traffic being redirected. + tproxy, err := common.TransparentProxyEnabled(namespace, pod, globalTProxyEnabled) + if err != nil { + return false, err + } + if !tproxy { + return false, nil + } + // First check to see if the pod annotation exists to override the namespace or global settings. if raw, ok := pod.Annotations[constants.KeyConsulDNS]; ok { return strconv.ParseBool(raw) @@ -274,7 +290,7 @@ func consulDNSEnabled(namespace corev1.Namespace, pod corev1.Pod, globalEnabled return strconv.ParseBool(raw) } // Else fall back to the global default. - return globalEnabled, nil + return globalDNSEnabled, nil } // splitCommaSeparatedItemsFromAnnotation takes an annotation and a pod diff --git a/control-plane/connect-inject/webhook/container_init_test.go b/control-plane/connect-inject/webhook/container_init_test.go index 8e0b551b24..5f14f44bc2 100644 --- a/control-plane/connect-inject/webhook/container_init_test.go +++ b/control-plane/connect-inject/webhook/container_init_test.go @@ -173,77 +173,104 @@ func TestHandlerContainerInit_transparentProxy(t *testing.T) { annotations map[string]string expTproxyEnabled bool namespaceLabel map[string]string + openShiftEnabled bool }{ - "enabled globally, ns not set, annotation not provided, cni disabled": { + "enabled globally, ns not set, annotation not provided, cni disabled, openshift disabled": { true, false, nil, true, nil, + false, }, - "enabled globally, ns not set, annotation is false, cni disabled": { + "enabled globally, ns not set, annotation is false, cni disabled, openshift disabled": { true, false, map[string]string{constants.KeyTransparentProxy: "false"}, false, nil, + false, }, - "enabled globally, ns not set, annotation is true, cni disabled": { + "enabled globally, ns not set, annotation is true, cni disabled, openshift disabled": { true, false, map[string]string{constants.KeyTransparentProxy: "true"}, true, nil, + false, }, - "disabled globally, ns not set, annotation not provided, cni disabled": { + "disabled globally, ns not set, annotation not provided, cni disabled, openshift disabled": { false, false, nil, false, nil, + false, }, - "disabled globally, ns not set, annotation is false, cni disabled": { + "disabled globally, ns not set, annotation is false, cni disabled, openshift disabled": { false, false, map[string]string{constants.KeyTransparentProxy: "false"}, false, nil, + false, }, - "disabled globally, ns not set, annotation is true, cni disabled": { + "disabled globally, ns not set, annotation is true, cni disabled, openshift disabled": { false, false, map[string]string{constants.KeyTransparentProxy: "true"}, true, nil, + false, }, - "disabled globally, ns enabled, annotation not set, cni disabled": { + "disabled globally, ns enabled, annotation not set, cni disabled, openshift disabled": { false, false, nil, true, map[string]string{constants.KeyTransparentProxy: "true"}, + false, }, - "enabled globally, ns disabled, annotation not set, cni disabled": { + "enabled globally, ns disabled, annotation not set, cni disabled, openshift disabled": { true, false, nil, false, map[string]string{constants.KeyTransparentProxy: "false"}, + false, }, - "disabled globally, ns enabled, annotation not set, cni enabled": { + "disabled globally, ns enabled, annotation not set, cni enabled, openshift disabled": { false, true, nil, false, map[string]string{constants.KeyTransparentProxy: "true"}, + false, }, - "enabled globally, ns not set, annotation not set, cni enabled": { + "enabled globally, ns not set, annotation not set, cni enabled, openshift disabled": { + true, + true, + nil, + false, + nil, + false, + }, + "enabled globally, ns not set, annotation not set, cni enabled, openshift enabled": { true, true, nil, false, nil, + true, + }, + "enabled globally, ns not set, annotation not set, cni disabled, openshift enabled": { + true, + false, + nil, + true, + nil, + true, }, } for name, c := range cases { @@ -252,17 +279,23 @@ func TestHandlerContainerInit_transparentProxy(t *testing.T) { EnableTransparentProxy: c.globalEnabled, EnableCNI: c.cniEnabled, ConsulConfig: &consul.Config{HTTPPort: 8500}, + EnableOpenShift: c.openShiftEnabled, } pod := minimal() pod.Annotations = c.annotations + privileged := false + if c.openShiftEnabled && !c.cniEnabled { + privileged = true + } + var expectedSecurityContext *corev1.SecurityContext if c.cniEnabled { expectedSecurityContext = &corev1.SecurityContext{ RunAsUser: pointer.Int64(initContainersUserAndGroupID), RunAsGroup: pointer.Int64(initContainersUserAndGroupID), RunAsNonRoot: pointer.Bool(true), - Privileged: pointer.Bool(false), + Privileged: pointer.Bool(privileged), Capabilities: &corev1.Capabilities{ Drop: []corev1.Capability{"ALL"}, }, @@ -272,7 +305,7 @@ func TestHandlerContainerInit_transparentProxy(t *testing.T) { RunAsUser: pointer.Int64(0), RunAsGroup: pointer.Int64(0), RunAsNonRoot: pointer.Bool(false), - Privileged: pointer.Bool(true), + Privileged: pointer.Bool(privileged), Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{netAdminCapability}, }, @@ -934,7 +967,8 @@ func TestHandlerContainerInit_Resources(t *testing.T) { var testNS = corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: k8sNamespace, + Name: k8sNamespace, + Labels: map[string]string{}, }, } diff --git a/control-plane/connect-inject/webhook/mesh_webhook.go b/control-plane/connect-inject/webhook/mesh_webhook.go index 503d3182b4..c8d412184b 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook.go +++ b/control-plane/connect-inject/webhook/mesh_webhook.go @@ -14,6 +14,7 @@ import ( "github.com/go-logr/logr" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/common" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/lifecycle" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/metrics" "github.com/hashicorp/consul-k8s/control-plane/consul" "github.com/hashicorp/consul-k8s/control-plane/namespaces" @@ -148,6 +149,10 @@ type MeshWebhook struct { DefaultProxyMemoryRequest resource.Quantity DefaultProxyMemoryLimit resource.Quantity + // LifecycleConfig contains proxy lifecycle management configuration from the inject-connect command and has methods to determine whether + // configuration should come from the default flags or annotations. The meshWebhook uses this to configure container sidecar proxy args. + LifecycleConfig lifecycle.Config + // Default Envoy concurrency flag, this is the number of worker threads to be used by the proxy. DefaultEnvoyProxyConcurrency int @@ -303,6 +308,7 @@ func (w *MeshWebhook) Handle(ctx context.Context, req admission.Request) admissi w.Log.Error(err, "error configuring injection sidecar container", "request name", req.Name) return admission.Errored(http.StatusInternalServerError, fmt.Errorf("error configuring injection sidecar container: %s", err)) } + // TODO: invert to start the Envoy sidecar before the application container pod.Spec.Containers = append(pod.Spec.Containers, envoySidecar) } else { // For multi port pods, check for unsupported cases, mount all relevant service account tokens, and mount an init @@ -373,6 +379,8 @@ func (w *MeshWebhook) Handle(ctx context.Context, req admission.Request) admissi w.Log.Error(err, "error configuring injection sidecar container", "request name", req.Name) return admission.Errored(http.StatusInternalServerError, fmt.Errorf("error configuring injection sidecar container: %s", err)) } + // TODO: invert to start the Envoy sidecar container before the + // application container pod.Spec.Containers = append(pod.Spec.Containers, envoySidecar) } } @@ -393,13 +401,17 @@ func (w *MeshWebhook) Handle(ctx context.Context, req admission.Request) admissi pod.Annotations[constants.KeyTransparentProxyStatus] = constants.Enabled } - // If tproxy with DNS redirection is enabled, we want to configure dns on the pod. - if tproxyEnabled && w.EnableConsulDNS { + // If DNS redirection is enabled, we want to configure dns on the pod. + dnsEnabled, err := consulDNSEnabled(*ns, pod, w.EnableConsulDNS, w.EnableTransparentProxy) + if err != nil { + w.Log.Error(err, "error determining if dns redirection is enabled", "request name", req.Name) + return admission.Errored(http.StatusInternalServerError, fmt.Errorf("error determining if dns redirection is enabled: %s", err)) + } + if dnsEnabled { if err = w.configureDNS(&pod, req.Namespace); err != nil { w.Log.Error(err, "error configuring DNS on the pod", "request name", req.Name) return admission.Errored(http.StatusInternalServerError, fmt.Errorf("error configuring DNS on the pod: %s", err)) } - } // Add annotations for metrics. diff --git a/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go b/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go index 34071a686c..3f6b668c6b 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go +++ b/control-plane/connect-inject/webhook/mesh_webhook_ent_test.go @@ -6,8 +6,8 @@ import ( "context" "testing" - "github.com/deckarep/golang-set" - logrtest "github.com/go-logr/logr/testing" + mapset "github.com/deckarep/golang-set" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/helper/test" "github.com/hashicorp/consul/api" @@ -49,7 +49,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "single destination namespace 'default' from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -71,7 +71,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "single destination namespace 'default' from k8s 'non-default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -93,7 +93,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "single destination namespace 'dest' from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -115,7 +115,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "single destination namespace 'dest' from k8s 'non-default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -137,7 +137,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "mirroring from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -160,7 +160,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "mirroring from k8s 'dest'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -183,7 +183,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "mirroring with prefix from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -207,7 +207,7 @@ func TestHandler_MutateWithNamespaces(t *testing.T) { { Name: "mirroring with prefix from k8s 'dest'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -295,7 +295,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + single destination namespace 'default' from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -318,7 +318,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + single destination namespace 'default' from k8s 'non-default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -341,7 +341,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + single destination namespace 'dest' from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -364,7 +364,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + single destination namespace 'dest' from k8s 'non-default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -387,7 +387,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + mirroring from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -411,7 +411,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + mirroring from k8s 'dest'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -435,7 +435,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + mirroring with prefix from k8s 'default'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -460,7 +460,7 @@ func TestHandler_MutateWithNamespaces_ACLs(t *testing.T) { { Name: "acls + mirroring with prefix from k8s 'dest'", Webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, @@ -600,7 +600,7 @@ func TestHandler_MutateWithNamespaces_Annotation(t *testing.T) { require.NoError(t, err) webhook := MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSet("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableNamespaces: true, diff --git a/control-plane/connect-inject/webhook/mesh_webhook_test.go b/control-plane/connect-inject/webhook/mesh_webhook_test.go index 602ba63239..9761551b85 100644 --- a/control-plane/connect-inject/webhook/mesh_webhook_test.go +++ b/control-plane/connect-inject/webhook/mesh_webhook_test.go @@ -7,7 +7,7 @@ import ( "testing" mapset "github.com/deckarep/golang-set" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/metrics" "github.com/hashicorp/consul-k8s/control-plane/consul" @@ -53,7 +53,7 @@ func TestHandlerHandle(t *testing.T) { { "kube-system namespace", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -73,7 +73,7 @@ func TestHandlerHandle(t *testing.T) { { "already injected", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -97,7 +97,7 @@ func TestHandlerHandle(t *testing.T) { { "empty pod basic", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -139,7 +139,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with upstreams specified", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -198,7 +198,7 @@ func TestHandlerHandle(t *testing.T) { { "empty pod with injection disabled", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -224,7 +224,7 @@ func TestHandlerHandle(t *testing.T) { { "empty pod with injection truthy", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -279,7 +279,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with empty volume mount annotation", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -333,7 +333,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with volume mount annotation", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -407,7 +407,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with sidecar volume mount annotation", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -467,7 +467,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with sidecar invalid volume mount annotation", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -498,7 +498,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with service annotation", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -553,7 +553,7 @@ func TestHandlerHandle(t *testing.T) { { "pod with existing label", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -603,7 +603,7 @@ func TestHandlerHandle(t *testing.T) { { "tproxy with overwriteProbes is enabled", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), EnableTransparentProxy: true, @@ -626,14 +626,14 @@ func TestHandlerHandle(t *testing.T) { { Name: "web", LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8081), }, @@ -693,7 +693,7 @@ func TestHandlerHandle(t *testing.T) { { "multiport pod kube < 1.24 with AuthMethod, serviceaccount has secret ref", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -752,7 +752,7 @@ func TestHandlerHandle(t *testing.T) { { "multiport pod kube 1.24 with AuthMethod, serviceaccount does not have secret ref", MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -808,6 +808,145 @@ func TestHandlerHandle(t *testing.T) { }, }, }, + { + "dns redirection enabled", + MeshWebhook{ + Log: logrtest.New(t), + AllowK8sNamespacesSet: mapset.NewSetWith("*"), + DenyK8sNamespacesSet: mapset.NewSet(), + EnableTransparentProxy: true, + TProxyOverwriteProbes: true, + decoder: decoder, + Clientset: defaultTestClientWithNamespace(), + }, + admission.Request{ + AdmissionRequest: admissionv1.AdmissionRequest{ + Namespace: namespaces.DefaultNamespace, + Object: encodeRaw(t, &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{}, + Annotations: map[string]string{constants.KeyConsulDNS: "true"}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + }, + }, + }), + }, + }, + "", + []jsonpatch.Operation{ + { + Operation: "add", + Path: "/spec/volumes", + }, + { + Operation: "add", + Path: "/spec/initContainers", + }, + { + Operation: "add", + Path: "/spec/containers/1", + }, + { + Operation: "add", + Path: "/metadata/labels", + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.KeyInjectStatus), + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.KeyTransparentProxyStatus), + }, + + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.AnnotationOriginalPod), + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.AnnotationConsulK8sVersion), + }, + { + Operation: "add", + Path: "/spec/dnsPolicy", + }, + { + Operation: "add", + Path: "/spec/dnsConfig", + }, + }, + }, + { + "dns redirection only enabled if tproxy enabled", + MeshWebhook{ + Log: logrtest.New(t), + AllowK8sNamespacesSet: mapset.NewSetWith("*"), + DenyK8sNamespacesSet: mapset.NewSet(), + EnableTransparentProxy: true, + TProxyOverwriteProbes: true, + decoder: decoder, + Clientset: defaultTestClientWithNamespace(), + }, + admission.Request{ + AdmissionRequest: admissionv1.AdmissionRequest{ + Namespace: namespaces.DefaultNamespace, + Object: encodeRaw(t, &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{}, + Annotations: map[string]string{ + constants.KeyConsulDNS: "true", + constants.KeyTransparentProxy: "false", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "web", + }, + }, + }, + }), + }, + }, + "", + []jsonpatch.Operation{ + { + Operation: "add", + Path: "/spec/volumes", + }, + { + Operation: "add", + Path: "/spec/initContainers", + }, + { + Operation: "add", + Path: "/spec/containers/1", + }, + { + Operation: "add", + Path: "/metadata/labels", + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.KeyInjectStatus), + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.AnnotationOriginalPod), + }, + { + Operation: "add", + Path: "/metadata/annotations/" + escapeJSONPointer(constants.AnnotationConsulK8sVersion), + }, + // Note: no DNS policy/config additions. + }, + }, } for _, tt := range cases { @@ -1460,7 +1599,7 @@ func TestOverwriteProbes(t *testing.T) { }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, @@ -1486,7 +1625,7 @@ func TestOverwriteProbes(t *testing.T) { }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, @@ -1513,7 +1652,7 @@ func TestOverwriteProbes(t *testing.T) { }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, @@ -1540,7 +1679,7 @@ func TestOverwriteProbes(t *testing.T) { }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, @@ -1567,7 +1706,7 @@ func TestOverwriteProbes(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8081), }, @@ -1603,21 +1742,21 @@ func TestOverwriteProbes(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8081), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8082), }, @@ -1646,21 +1785,21 @@ func TestOverwriteProbes(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8081), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8080), }, @@ -1680,21 +1819,21 @@ func TestOverwriteProbes(t *testing.T) { }, }, LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8083), }, }, }, ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8082), }, }, }, StartupProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(8082), }, diff --git a/control-plane/connect-inject/webhook/redirect_traffic.go b/control-plane/connect-inject/webhook/redirect_traffic.go index eab23a2b91..8ae8fce068 100644 --- a/control-plane/connect-inject/webhook/redirect_traffic.go +++ b/control-plane/connect-inject/webhook/redirect_traffic.go @@ -95,7 +95,7 @@ func (w *MeshWebhook) iptablesConfigJSON(pod corev1.Pod, ns corev1.Namespace) (s // Add init container user ID to exclude from traffic redirection. cfg.ExcludeUIDs = append(cfg.ExcludeUIDs, strconv.Itoa(initContainersUserAndGroupID)) - dnsEnabled, err := consulDNSEnabled(ns, pod, w.EnableConsulDNS) + dnsEnabled, err := consulDNSEnabled(ns, pod, w.EnableConsulDNS, w.EnableTransparentProxy) if err != nil { return "", err } diff --git a/control-plane/connect-inject/webhook/redirect_traffic_test.go b/control-plane/connect-inject/webhook/redirect_traffic_test.go index 2ad9940fbe..38aad20578 100644 --- a/control-plane/connect-inject/webhook/redirect_traffic_test.go +++ b/control-plane/connect-inject/webhook/redirect_traffic_test.go @@ -7,7 +7,7 @@ import ( "testing" mapset "github.com/deckarep/golang-set" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/consul" "github.com/hashicorp/consul/sdk/iptables" @@ -45,7 +45,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "basic bare minimum pod", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -75,7 +75,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "proxy health checks enabled", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -108,7 +108,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "metrics enabled", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -142,7 +142,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "metrics enabled with incorrect annotation", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -177,7 +177,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "overwrite probes, transparent proxy annotation set", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -196,7 +196,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { Name: "test", LivenessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Port: intstr.FromInt(exposedPathsLivenessPortsRangeStart), }, @@ -218,7 +218,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude inbound ports", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -251,7 +251,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude outbound ports", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -284,7 +284,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude outbound CIDRs", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -317,7 +317,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude UIDs", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, @@ -349,7 +349,7 @@ func TestAddRedirectTrafficConfig(t *testing.T) { { name: "exclude inbound ports, outbound ports, outbound CIDRs, and UIDs", webhook: MeshWebhook{ - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), AllowK8sNamespacesSet: mapset.NewSetWith("*"), DenyK8sNamespacesSet: mapset.NewSet(), decoder: decoder, diff --git a/control-plane/controller/configentry_controller.go b/control-plane/controller/configentry_controller.go index 8ae90a56a6..7782d5e9a9 100644 --- a/control-plane/controller/configentry_controller.go +++ b/control-plane/controller/configentry_controller.go @@ -37,11 +37,11 @@ type Controller interface { // Update updates the state of the whole object. Update(context.Context, client.Object, ...client.UpdateOption) error // UpdateStatus updates the state of just the object's status. - UpdateStatus(context.Context, client.Object, ...client.UpdateOption) error + UpdateStatus(context.Context, client.Object, ...client.SubResourceUpdateOption) error // Get retrieves an obj for the given object key from the Kubernetes Cluster. // obj must be a struct pointer so that obj can be updated with the response // returned by the Server. - Get(ctx context.Context, key client.ObjectKey, obj client.Object) error + Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error // Logger returns a logger with values added for the specific controller // and request name. Logger(types.NamespacedName) logr.Logger diff --git a/control-plane/controller/configentry_controller_ent_test.go b/control-plane/controller/configentry_controller_ent_test.go index 61a6aef947..aa59ddaceb 100644 --- a/control-plane/controller/configentry_controller_ent_test.go +++ b/control-plane/controller/configentry_controller_ent_test.go @@ -9,7 +9,7 @@ import ( "time" "github.com/go-logr/logr" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul-k8s/control-plane/controller" @@ -201,7 +201,7 @@ func TestConfigEntryController_createsConfigEntry_consulNamespaces(tt *testing.T r := in.GetController( fakeClient, - logrtest.TestLogger{T: t}, + logrtest.New(t), s, &controller.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, @@ -463,7 +463,7 @@ func TestConfigEntryController_updatesConfigEntry_consulNamespaces(tt *testing.T r := in.GetControllerFunc( fakeClient, - logrtest.TestLogger{T: t}, + logrtest.New(t), s, &controller.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, @@ -712,7 +712,7 @@ func TestConfigEntryController_deletesConfigEntry_consulNamespaces(tt *testing.T r := in.GetControllerFunc( fakeClient, - logrtest.TestLogger{T: t}, + logrtest.New(t), s, &controller.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, diff --git a/control-plane/controller/configentry_controller_test.go b/control-plane/controller/configentry_controller_test.go index 83b9e3eecf..3b8ba9e561 100644 --- a/control-plane/controller/configentry_controller_test.go +++ b/control-plane/controller/configentry_controller_test.go @@ -7,7 +7,7 @@ import ( "time" "github.com/go-logr/logr" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/hashicorp/consul-k8s/control-plane/api/common" @@ -449,7 +449,7 @@ func TestConfigEntryControllers_createsConfigEntry(t *testing.T) { req.True(written) } - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.New(t)) namespacedName := types.NamespacedName{ Namespace: kubeNS, Name: c.configEntryResource.KubernetesName(), @@ -949,7 +949,7 @@ func TestConfigEntryControllers_updatesConfigEntry(t *testing.T) { c.updateF(c.configEntryResource) err = fakeClient.Update(ctx, c.configEntryResource) req.NoError(err) - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.New(t)) resp, err := r.Reconcile(ctx, ctrl.Request{ NamespacedName: namespacedName, }) @@ -1341,7 +1341,7 @@ func TestConfigEntryControllers_deletesConfigEntry(t *testing.T) { Namespace: kubeNS, Name: c.configEntryResourceWithDeletion.KubernetesName(), } - r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.TestLogger{T: t}) + r := c.reconciler(fakeClient, testClient.Cfg, testClient.Watcher, logrtest.New(t)) resp, err := r.Reconcile(context.Background(), ctrl.Request{ NamespacedName: namespacedName, }) @@ -1388,7 +1388,7 @@ func TestConfigEntryControllers_errorUpdatesSyncStatus(t *testing.T) { reconciler := &ServiceDefaultsController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConfigEntryController: &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, @@ -1455,7 +1455,7 @@ func TestConfigEntryControllers_setsSyncedToTrue(t *testing.T) { consulClient := testClient.APIClient reconciler := &ServiceDefaultsController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConfigEntryController: &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, @@ -1547,7 +1547,7 @@ func TestConfigEntryControllers_doesNotCreateUnownedConfigEntry(t *testing.T) { // Attempt to create the entry in Kube and run reconcile. reconciler := ServiceDefaultsController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConfigEntryController: &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, @@ -1611,7 +1611,7 @@ func TestConfigEntryControllers_doesNotDeleteUnownedConfig(t *testing.T) { consulClient := testClient.APIClient reconciler := &ServiceDefaultsController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), ConfigEntryController: &ConfigEntryController{ ConsulClientConfig: testClient.Cfg, ConsulServerConnMgr: testClient.Watcher, @@ -1691,7 +1691,7 @@ func TestConfigEntryControllers_updatesStatusWhenDeleteFails(t *testing.T) { testClient := test.TestServerWithMockConnMgrWatcher(t, nil) testClient.TestServer.WaitForServiceIntentions(t) - logger := logrtest.TestLogger{T: t} + logger := logrtest.New(t) svcDefaultsReconciler := ServiceDefaultsController{ Client: fakeClient, @@ -1829,7 +1829,7 @@ func TestConfigEntryController_Migration(t *testing.T) { require.True(t, success, "config entry was not created") // Set up the reconciler. - logger := logrtest.TestLogger{T: t} + logger := logrtest.New(t) svcDefaultsReconciler := ServiceDefaultsController{ Client: fakeClient, Log: logger, diff --git a/control-plane/controller/exportedservices_controller.go b/control-plane/controller/exportedservices_controller.go index 84e767d6dc..9466360207 100644 --- a/control-plane/controller/exportedservices_controller.go +++ b/control-plane/controller/exportedservices_controller.go @@ -31,7 +31,7 @@ func (r *ExportedServicesController) Logger(name types.NamespacedName) logr.Logg return r.Log.WithValues("request", name) } -func (r *ExportedServicesController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ExportedServicesController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controller/exportedservices_controller_ent_test.go b/control-plane/controller/exportedservices_controller_ent_test.go index dd91c49b57..29b4e006f1 100644 --- a/control-plane/controller/exportedservices_controller_ent_test.go +++ b/control-plane/controller/exportedservices_controller_ent_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - logrtest "github.com/go-logr/logr/testing" + logrtest "github.com/go-logr/logr/testr" "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" "github.com/hashicorp/consul-k8s/control-plane/controller" @@ -102,7 +102,7 @@ func TestExportedServicesController_createsExportedServices(tt *testing.T) { controller := &controller.ExportedServicesController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), Scheme: s, ConfigEntryController: &controller.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, @@ -217,7 +217,7 @@ func TestExportedServicesController_updatesExportedServices(tt *testing.T) { controller := &controller.ExportedServicesController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), Scheme: s, ConfigEntryController: &controller.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, @@ -355,7 +355,7 @@ func TestExportedServicesController_deletesExportedServices(tt *testing.T) { controller := &controller.ExportedServicesController{ Client: fakeClient, - Log: logrtest.TestLogger{T: t}, + Log: logrtest.New(t), Scheme: s, ConfigEntryController: &controller.ConfigEntryController{ ConsulClientConfig: testClient.Cfg, diff --git a/control-plane/controller/ingressgateway_controller.go b/control-plane/controller/ingressgateway_controller.go index 7e656b3d29..5a6a07776b 100644 --- a/control-plane/controller/ingressgateway_controller.go +++ b/control-plane/controller/ingressgateway_controller.go @@ -31,7 +31,7 @@ func (r *IngressGatewayController) Logger(name types.NamespacedName) logr.Logger return r.Log.WithValues("request", name) } -func (r *IngressGatewayController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *IngressGatewayController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controller/mesh_controller.go b/control-plane/controller/mesh_controller.go index e15f49fca0..94b0df6a5e 100644 --- a/control-plane/controller/mesh_controller.go +++ b/control-plane/controller/mesh_controller.go @@ -31,7 +31,7 @@ func (r *MeshController) Logger(name types.NamespacedName) logr.Logger { return r.Log.WithValues("request", name) } -func (r *MeshController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *MeshController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controller/proxydefaults_controller.go b/control-plane/controller/proxydefaults_controller.go index a63e121522..fe929c1bf1 100644 --- a/control-plane/controller/proxydefaults_controller.go +++ b/control-plane/controller/proxydefaults_controller.go @@ -31,7 +31,7 @@ func (r *ProxyDefaultsController) Logger(name types.NamespacedName) logr.Logger return r.Log.WithValues("request", name) } -func (r *ProxyDefaultsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ProxyDefaultsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controller/servicedefaults_controller.go b/control-plane/controller/servicedefaults_controller.go index 7dd73914e4..e58e186234 100644 --- a/control-plane/controller/servicedefaults_controller.go +++ b/control-plane/controller/servicedefaults_controller.go @@ -31,7 +31,7 @@ func (r *ServiceDefaultsController) Logger(name types.NamespacedName) logr.Logge return r.Log.WithValues("request", name) } -func (r *ServiceDefaultsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ServiceDefaultsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controller/serviceintentions_controller.go b/control-plane/controller/serviceintentions_controller.go index 3b70447517..e5bfab959e 100644 --- a/control-plane/controller/serviceintentions_controller.go +++ b/control-plane/controller/serviceintentions_controller.go @@ -31,7 +31,7 @@ func (r *ServiceIntentionsController) Logger(name types.NamespacedName) logr.Log return r.Log.WithValues("request", name) } -func (r *ServiceIntentionsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ServiceIntentionsController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controller/serviceresolver_controller.go b/control-plane/controller/serviceresolver_controller.go index 3e01e680ea..3019369dc6 100644 --- a/control-plane/controller/serviceresolver_controller.go +++ b/control-plane/controller/serviceresolver_controller.go @@ -31,7 +31,7 @@ func (r *ServiceResolverController) Logger(name types.NamespacedName) logr.Logge return r.Log.WithValues("request", name) } -func (r *ServiceResolverController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ServiceResolverController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controller/servicerouter_controller.go b/control-plane/controller/servicerouter_controller.go index 7db983dec2..9c435169f4 100644 --- a/control-plane/controller/servicerouter_controller.go +++ b/control-plane/controller/servicerouter_controller.go @@ -31,7 +31,7 @@ func (r *ServiceRouterController) Logger(name types.NamespacedName) logr.Logger return r.Log.WithValues("request", name) } -func (r *ServiceRouterController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ServiceRouterController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controller/servicesplitter_controller.go b/control-plane/controller/servicesplitter_controller.go index 9d07845dbb..f977301afe 100644 --- a/control-plane/controller/servicesplitter_controller.go +++ b/control-plane/controller/servicesplitter_controller.go @@ -31,7 +31,7 @@ func (r *ServiceSplitterController) Logger(name types.NamespacedName) logr.Logge return r.Log.WithValues("request", name) } -func (r *ServiceSplitterController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *ServiceSplitterController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/controller/terminatinggateway_controller.go b/control-plane/controller/terminatinggateway_controller.go index a8db2d851e..159f8ff8c1 100644 --- a/control-plane/controller/terminatinggateway_controller.go +++ b/control-plane/controller/terminatinggateway_controller.go @@ -31,7 +31,7 @@ func (r *TerminatingGatewayController) Logger(name types.NamespacedName) logr.Lo return r.Log.WithValues("request", name) } -func (r *TerminatingGatewayController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { +func (r *TerminatingGatewayController) UpdateStatus(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { return r.Status().Update(ctx, obj, opts...) } diff --git a/control-plane/go.mod b/control-plane/go.mod index d5805fe558..7a4d8e99f9 100644 --- a/control-plane/go.mod +++ b/control-plane/go.mod @@ -4,41 +4,42 @@ require ( github.com/cenkalti/backoff v2.2.1+incompatible github.com/containernetworking/cni v1.1.1 github.com/deckarep/golang-set v1.7.1 - github.com/fsnotify/fsnotify v1.5.4 - github.com/go-logr/logr v0.4.0 - github.com/google/go-cmp v0.5.7 + github.com/fsnotify/fsnotify v1.6.0 + github.com/go-logr/logr v1.2.3 + github.com/google/go-cmp v0.5.9 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 - github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8 - github.com/hashicorp/consul-server-connection-manager v0.1.0 - github.com/hashicorp/consul/api v1.10.1-0.20230203155153-2f149d60ccbf - github.com/hashicorp/consul/sdk v0.13.0 - github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f + github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d + github.com/hashicorp/consul-server-connection-manager v0.1.3 + github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca + github.com/hashicorp/consul/sdk v0.13.1 + github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530 github.com/hashicorp/go-hclog v1.2.2 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-netaddrs v0.1.0 github.com/hashicorp/go-rootcerts v1.0.2 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/serf v0.10.1 + github.com/hashicorp/vault/api v1.8.3 github.com/kr/text v0.2.0 - github.com/miekg/dns v1.1.41 + github.com/miekg/dns v1.1.50 github.com/mitchellh/cli v1.1.0 github.com/mitchellh/go-homedir v1.1.0 - github.com/mitchellh/mapstructure v1.4.1 - github.com/stretchr/testify v1.7.2 - go.uber.org/zap v1.19.0 - golang.org/x/text v0.3.7 - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 - gomodules.xyz/jsonpatch/v2 v2.2.0 - k8s.io/api v0.22.2 - k8s.io/apimachinery v0.22.2 - k8s.io/client-go v0.22.2 - k8s.io/klog/v2 v2.9.0 - k8s.io/utils v0.0.0-20220812165043-ad590609e2e5 - sigs.k8s.io/controller-runtime v0.10.2 + github.com/mitchellh/mapstructure v1.5.0 + github.com/stretchr/testify v1.8.1 + go.uber.org/zap v1.24.0 + golang.org/x/text v0.11.0 + golang.org/x/time v0.3.0 + gomodules.xyz/jsonpatch/v2 v2.3.0 + k8s.io/api v0.26.1 + k8s.io/apimachinery v0.26.1 + k8s.io/client-go v0.26.1 + k8s.io/klog/v2 v2.90.1 + k8s.io/utils v0.0.0-20230209194617-a36077c30491 + sigs.k8s.io/controller-runtime v0.14.6 ) require ( - cloud.google.com/go v0.81.0 // indirect + cloud.google.com/go v0.65.0 // indirect github.com/Azure/azure-sdk-for-go v44.0.0+incompatible // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.18 // indirect @@ -52,88 +53,116 @@ require ( github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/armon/go-radix v1.0.0 // indirect - github.com/aws/aws-sdk-go v1.25.41 // indirect + github.com/aws/aws-sdk-go v1.44.262 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect + github.com/cenkalti/backoff/v3 v3.0.0 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect - github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661 // indirect - github.com/digitalocean/godo v1.10.0 // indirect + github.com/digitalocean/godo v1.7.5 // indirect github.com/dimchansky/utfbom v1.1.0 // indirect + github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fatih/color v1.13.0 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect - github.com/go-logr/zapr v0.4.0 // indirect + github.com/go-logr/zapr 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.19.14 // 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/google/go-querystring v1.0.0 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/gnostic v0.5.7-v3refs // indirect + github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.1.2 // indirect github.com/googleapis/gax-go/v2 v2.0.5 // indirect - github.com/googleapis/gnostic v0.5.5 // indirect github.com/gophercloud/gophercloud v0.1.0 // indirect github.com/hashicorp/consul/proto-public v0.1.0 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-immutable-radix v1.3.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-retryablehttp v0.6.6 // indirect + github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // 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/golang-lru v0.5.4 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/mdns v1.0.4 // indirect + github.com/hashicorp/vault/sdk v0.7.0 // indirect github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 // indirect + github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/imdario/mergo v0.3.12 // indirect - github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect - github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/linode/linodego v0.7.1 // indirect + github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // 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.0.0 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 // indirect + github.com/oklog/run v1.0.0 // indirect github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c // indirect + github.com/pierrec/lz4 v2.5.2+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/posener/complete v1.2.3 // indirect - github.com/prometheus/client_golang v1.11.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.26.0 // indirect - github.com/prometheus/procfs v0.6.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 github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.2.0 // indirect - github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible // indirect + github.com/stretchr/objx v0.5.0 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.480 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.480 // indirect github.com/vmware/govmomi v0.18.0 // indirect - go.opencensus.io v0.23.0 // indirect - go.uber.org/atomic v1.7.0 // indirect + go.opencensus.io v0.22.4 // indirect + go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect - golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect - golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/api v0.43.0 // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect + golang.org/x/mod v0.8.0 // indirect + golang.org/x/net v0.13.0 // indirect + golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/term v0.10.0 // indirect + golang.org/x/tools v0.6.0 // indirect + google.golang.org/api v0.30.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect - google.golang.org/grpc v1.48.0 // indirect + google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // 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/resty.v1 v1.12.0 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.22.2 // indirect - k8s.io/component-base v0.22.2 // indirect - k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect - sigs.k8s.io/yaml v1.2.0 // indirect + k8s.io/apiextensions-apiserver v0.26.1 // indirect + k8s.io/component-base v0.26.1 // indirect + k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) -replace github.com/hashicorp/consul/sdk => github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 - -go 1.19 +go 1.20 diff --git a/control-plane/go.sum b/control-plane/go.sum index d1473ae24f..dd0f84b02c 100644 --- a/control-plane/go.sum +++ b/control-plane/go.sum @@ -12,13 +12,8 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= 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.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 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= 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= @@ -27,7 +22,6 @@ 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/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/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= @@ -40,15 +34,11 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v44.0.0+incompatible h1:e82Yv2HNpS0kuyeCrV29OPKvEiqfs2/uJHic3/3iKdg= github.com/Azure/azure-sdk-for-go v44.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -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.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.11.0/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= 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.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q= github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= @@ -56,11 +46,8 @@ github.com/Azure/go-autorest/autorest/azure/auth v0.5.0 h1:nSMjYIe24eBYasAIxt859 github.com/Azure/go-autorest/autorest/azure/auth v0.5.0/go.mod h1:QRTvSZQpxqm8mSErhnbI+tANIBAKP7B+UIE2z4ypUO0= github.com/Azure/go-autorest/autorest/azure/cli v0.4.0 h1:Ml+UCrnlKD+cJmSzrZ/RDcDw86NjkRUpnFh7V5JUhzU= github.com/Azure/go-autorest/autorest/azure/cli v0.4.0/go.mod h1:JljT387FplPzBA31vUcvsetLKF3pec5bdAxjVU4kI2s= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= github.com/Azure/go-autorest/autorest/date v0.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.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= @@ -68,25 +55,15 @@ github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+X 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.0 h1:3I9AAI63HfcLtphd9g39ruUwRI+Ca+z/f36KHPFRUss= github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= 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.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= 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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 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/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/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/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14= -github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= 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= @@ -94,17 +71,14 @@ 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/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-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/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go v1.25.41 h1:/hj7nZ0586wFqpwjNpzWiUTwtaMgxAZNZKHay80MdXw= -github.com/aws/aws-sdk-go v1.25.41/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/aws/aws-sdk-go v1.44.262 h1:gyXpcJptWoNkK+DiAiaBltlreoWKQXjAIh6FRh60F+I= +github.com/aws/aws-sdk-go v1.44.262/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= 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= @@ -113,18 +87,16 @@ 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 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +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/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/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 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= 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/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= @@ -132,32 +104,14 @@ 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/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-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/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -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/containernetworking/cni v1.1.1 h1:ky20T7c0MvKvbMOwS/FrlbNwjEoqJEUUYfsL4b0mc4k= github.com/containernetworking/cni v1.1.1/go.mod h1:sDpYKmGVENF3s6uvMvGgldDWeG8dMxakj/u+i9ht9vw= -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-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 v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -166,47 +120,37 @@ github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14y github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661 h1:lrWnAyy/F72MbxIxFUzKmcMCdt9Oi8RzpAxzTNQHD7o= github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= 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/digitalocean/godo v1.7.5 h1:JOQbAO6QT1GGjor0doT0mXefX2FgUDPOpYh2RaXA+ko= github.com/digitalocean/godo v1.7.5/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= -github.com/digitalocean/godo v1.10.0 h1:uW1/FcvZE/hoixnJcnlmIUvTVNdZCLjRLzmDtRi1xXY= -github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -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 v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.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 v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.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/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.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= 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 h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.13.0 h1:yNZif1OkDfNoDfb9zZa9aXIpejNR4F23Wely0c+Qdqk= 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.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -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/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/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= @@ -214,36 +158,32 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 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-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 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM= -github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +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.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/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -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/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +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 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= 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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= 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.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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -256,8 +196,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -273,13 +211,15 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.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/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 h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= 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= @@ -287,22 +227,18 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.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 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.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -310,66 +246,43 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-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/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/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -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/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.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.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8 h1:TQY0oKtLV15UNYWeSkTxi4McBIyLecsEtbc/VfxvbYA= -github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20220831174802-b8af65262de8/go.mod h1:aw35GB76URgbtxaSSMxbOetbG7YEHHPkIX3/SkTBaWc= -github.com/hashicorp/consul-server-connection-manager v0.1.0 h1:XCweGvMHzra88rYv2zxwwuUOjBUdcQmNKVrnQmt/muo= -github.com/hashicorp/consul-server-connection-manager v0.1.0/go.mod h1:XVVlO+Yk7aiRpspiHZkrrFVn9BJIiOPnQIzqytPxGaU= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.10.1-0.20230106171340-8d923c178919 h1:8aVegJMSv7PIAAa1zqQQ0CT4TKv+Nf7I4rhE6+uDa1U= -github.com/hashicorp/consul/api v1.10.1-0.20230106171340-8d923c178919/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= -github.com/hashicorp/consul/api v1.10.1-0.20230126204442-a43eadd1225b h1:dNIQYhru10Hg+E1oEL8f9CX6MC+8CW5JuQ4jk3g70LA= -github.com/hashicorp/consul/api v1.10.1-0.20230126204442-a43eadd1225b/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= -github.com/hashicorp/consul/api v1.10.1-0.20230203155153-2f149d60ccbf h1:vvsHghmX3LyNUaDe7onYKHyDiny+ystdHKIEujbNj4Q= -github.com/hashicorp/consul/api v1.10.1-0.20230203155153-2f149d60ccbf/go.mod h1:c1u8FzGHcavbEtRW/p1YditvfMgn4QsKNgz2rnCDF7c= +github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d h1:RJ1MZ8JKnfgKQ1kR3IBQAMpOpzXrdseZAYN/QR//MFM= +github.com/hashicorp/consul-k8s/control-plane/cni v0.0.0-20230511143918-bd16ab83383d/go.mod h1:IHIHMzkoMwlv6rLsgwcoFBVYupR7/1pKEOHBMjD4L0k= +github.com/hashicorp/consul-server-connection-manager v0.1.3 h1:fxsZ15XBNNWhV26yBVdCcnxHwSRgf9wqHGS2ZVCQIhc= +github.com/hashicorp/consul-server-connection-manager v0.1.3/go.mod h1:Md2IGKaFJ4ek9GUA0pW1S2R60wpquMOUs27GiD9kZd0= +github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca h1:5UPVYOlJg/HBEJ2q82rkkQ3ZLzeMnF5MOpGcw2kh+XU= +github.com/hashicorp/consul/api v1.10.1-0.20230512003852-bd0eb07ed3ca/go.mod h1:tXfrC6o0yFTgAW46xd5Ic8STHc9oIBcRVBcwhX5KNCQ= github.com/hashicorp/consul/proto-public v0.1.0 h1:O0LSmCqydZi363hsqc6n2v5sMz3usQMXZF6ziK3SzXU= github.com/hashicorp/consul/proto-public v0.1.0/go.mod h1:vs2KkuWwtjkIgA5ezp4YKPzQp4GitV+q/+PvksrA92k= -github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892 h1:jw0NwPmNPr5CxAU04hACdj61JSaJBKZ0FdBo+kwfNp4= -github.com/hashicorp/consul/sdk v0.4.1-0.20221021205723-cc843c4be892/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/consul/sdk v0.13.1 h1:EygWVWWMczTzXGpO93awkHFzfUka6hLYJ0qhETd+6lY= +github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= 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-discover v0.0.0-20200812215701-c4b85f6ed31f h1:7WFMVeuJQp6BkzuTv9O52pzwtEFVUJubKYN+zez8eTI= -github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f/go.mod h1:D4eo8/CN92vm9/9UDG+ldX1/fMFa4kpl8qzyTolus8o= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530 h1:WUwSDou+memX/pb6xnjA0PfAqEEJtdWSrK00kl8ySK8= +github.com/hashicorp/go-discover v0.0.0-20230519164032-214571b6a530/go.mod h1:RH2Jr1/cCsZ1nRLmAOC65hp/gRehf55SsUIYV2+NAxI= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= 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.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE= -github.com/hashicorp/go-immutable-radix v1.3.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 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= 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= @@ -378,12 +291,23 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-netaddrs v0.1.0 h1:TnlYvODD4C/wO+j7cX1z69kV5gOzI87u3OcUinANaW8= github.com/hashicorp/go-netaddrs v0.1.0/go.mod h1:33+a/emi5R5dqRspOuZKO0E+Tuz5WV1F84eRWALkedA= +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-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +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.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.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.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/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-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= @@ -391,49 +315,45 @@ github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2I github.com/hashicorp/go-uuid v1.0.2/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/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= 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.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +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/vic v1.5.1-0.20190403131502-bbfe86ec9443 h1:O/pT5C1Q3mVXMyuqg7yuAWUg/jMZR1/0QTzTRdNR6Uw= github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo= +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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= -github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk= -github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -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/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +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/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/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 h1:JHCT6xuyPUrbbgAPE/3dqlvUKzRHMNuTBKKUb6OeR/k= github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= -github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f h1:ENpDacvnr8faw5ugQmEF1QYk+f/Y9lXFvuYmRxykago= -github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk= 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.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= @@ -441,11 +361,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/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/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/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -453,18 +370,16 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 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/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/linode/linodego v0.7.1 h1:4WZmMpSA2NRwlPZcc0+4Gyn7rr99Evk9bnr0B3gXRKE= github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= -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/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.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 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= @@ -474,33 +389,33 @@ github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZb 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= 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-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +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/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -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/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/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 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= +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 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -508,44 +423,35 @@ 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/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/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/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/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/onsi/ginkgo v1.6.0/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/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= 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.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c h1:vwpFWvAO8DeIZfFeqASzZfsxuWPno9ncAebBEP0N3uE= github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M= 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.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +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/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -555,165 +461,113 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -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.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 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= 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= 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-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +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.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 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= 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/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +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/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOekn6B5G/+GMtks9UKbvRU/CMM/o= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627/go.mod h1:7zjs06qF79/FKAJpBvFx3P8Ww4UTIMAe+lpNXDHziac= -github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6qbIiqJ6/Bqeq25bCLbL7YFmpaFfJDuM= +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/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -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.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/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/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d h1:bVQRCxQvfjNUeRqaY/uT0tFuvuFY0ulgnczuR684Xic= github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= -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.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 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/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.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/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible h1:8uRvJleFpqLsO77WaAh2UrasMOzd8MxXrNj20e7El+Q= -github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4= -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/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/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.480 h1:Dwnfdrk3KXpYRH9Kwrk9sHpZSOmrE7P9LBoNsYUJKR4= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.480/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.480 h1:YEDZmv2ABU8QvwXEVTOQgVEQzDOByhz73vdjL6sERkE= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm v1.0.480/go.mod h1:zaBIuDDs+rC74X8Aog+LSu91GFtHYRYDC196RGTm2jk= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo= github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -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.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= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= 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.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= +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/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= 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.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 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/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-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/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-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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= 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= @@ -724,6 +578,8 @@ 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-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= +golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 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= @@ -736,9 +592,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -747,16 +600,14 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +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-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= @@ -764,15 +615,12 @@ 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-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-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-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= @@ -789,32 +637,26 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-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-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-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-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/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.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-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-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/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 h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/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= @@ -825,17 +667,15 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ 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/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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= 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-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -844,7 +684,6 @@ 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-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -853,8 +692,6 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7w 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-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-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -872,85 +709,71 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/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-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-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= -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-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-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-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-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/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-20220412211240-33da011f77ad/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-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +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.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.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-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -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-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 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.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.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= -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 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +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-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= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-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-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-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= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -970,7 +793,6 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -978,23 +800,18 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY 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-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= -golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +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= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -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= @@ -1010,13 +827,8 @@ google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0 h1:4sAyIHT6ZohtAQDoxws+ez7bROYmUlOVvsUscYCDTqA= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= 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= @@ -1046,7 +858,6 @@ 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= @@ -1056,22 +867,11 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc 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-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/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= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= 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= @@ -1082,17 +882,11 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -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.46.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/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= @@ -1106,9 +900,9 @@ 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/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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1117,17 +911,13 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/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/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= 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/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +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/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -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= @@ -1142,8 +932,6 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C 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= 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/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1151,49 +939,30 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= -k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw= -k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8= -k8s.io/apiextensions-apiserver v0.22.2 h1:zK7qI8Ery7j2CaN23UCFaC1hj7dMiI87n01+nKuewd4= -k8s.io/apiextensions-apiserver v0.22.2/go.mod h1:2E0Ve/isxNl7tWLSUDgi6+cmwHi5fQRdwGVCxbC+KFA= -k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= -k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk= -k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI= -k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= -k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc= -k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U= -k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o= -k8s.io/component-base v0.22.2 h1:vNIvE0AIrLhjX8drH0BgCNJcR4QZxMXcJzBsDplDx9M= -k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllunGug= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0/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.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/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/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/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.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/klog/v2 v2.90.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/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= +k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/controller-runtime v0.10.2 h1:jW8qiY+yMnnPx6O9hu63tgcwaKzd1yLYui+mpvClOOc= -sigs.k8s.io/controller-runtime v0.10.2/go.mod h1:CQp8eyUQZ/Q7PJvnIrB6/hgfTC1kBkGylwsLgOQi1WY= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= +sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= +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/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= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/control-plane/helper/controller/controller.go b/control-plane/helper/controller/controller.go index 6edfb9d89c..12b84b567d 100644 --- a/control-plane/helper/controller/controller.go +++ b/control-plane/helper/controller/controller.go @@ -59,7 +59,7 @@ func (c *Controller) Run(stopCh <-chan struct{}) { // Add an event handler when data is received from the informer. The // event handlers here will block the informer so we just offload them // immediately into a workqueue. - informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + _, err := informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { // convert the resource object into a key (in this case // we are just doing it in the format of 'namespace/name') @@ -78,6 +78,9 @@ func (c *Controller) Run(stopCh <-chan struct{}) { }, DeleteFunc: c.informerDeleteHandler(queue), }) + if err != nil { + c.Log.Error("error adding informer event handlers", err) + } // If the type is a background syncer, then we startup the background // process. diff --git a/control-plane/subcommand/common/common.go b/control-plane/subcommand/common/common.go index e3a569ddf6..00535cb6fe 100644 --- a/control-plane/subcommand/common/common.go +++ b/control-plane/subcommand/common/common.go @@ -36,8 +36,8 @@ const ( // The number of times to attempt ACL Login. numLoginRetries = 100 - raftReplicationTimeout = 2 * time.Second - tokenReadPollingInterval = 100 * time.Millisecond + raftReplicationTimeout = 60 * time.Second + tokenReadPollingInterval = 500 * time.Millisecond ) // Logger returns an hclog instance with log level set and JSON logging enabled/disabled, or an error if level is invalid. @@ -67,7 +67,7 @@ func ZapLogger(level string, jsonLogging bool) (logr.Logger, error) { level = "debug" } if err := zapLevel.UnmarshalText([]byte(level)); err != nil { - return nil, fmt.Errorf("unknown log level %q: %s", level, err.Error()) + return logr.Logger{}, fmt.Errorf("unknown log level %q: %s", level, err.Error()) } if jsonLogging { return zap.New(zap.UseDevMode(false), zap.Level(zapLevel), zap.JSONEncoder()), nil diff --git a/control-plane/subcommand/common/common_test.go b/control-plane/subcommand/common/common_test.go index 521831473a..6021bd0b49 100644 --- a/control-plane/subcommand/common/common_test.go +++ b/control-plane/subcommand/common/common_test.go @@ -8,7 +8,6 @@ import ( "net/url" "os" "testing" - "time" "github.com/hashicorp/consul-k8s/control-plane/helper/go-discover/mocks" "github.com/hashicorp/consul/api" @@ -163,36 +162,33 @@ func TestConsulLogin_TokenNotReplicated(t *testing.T) { func TestConsulLogin_EmptyBearerTokenFile(t *testing.T) { t.Parallel() - require := require.New(t) bearerTokenFile := WriteTempFile(t, "") params := LoginParams{ BearerTokenFile: bearerTokenFile, } _, err := ConsulLogin(nil, params, hclog.NewNullLogger()) - require.EqualError(err, fmt.Sprintf("no bearer token found in %q", bearerTokenFile)) + require.EqualError(t, err, fmt.Sprintf("no bearer token found in %q", bearerTokenFile)) } func TestConsulLogin_BearerTokenFileDoesNotExist(t *testing.T) { t.Parallel() - require := require.New(t) randFileName := fmt.Sprintf("/foo/%d/%d", rand.Int(), rand.Int()) params := LoginParams{ BearerTokenFile: randFileName, } _, err := ConsulLogin(nil, params, hclog.NewNullLogger()) - require.Error(err) - require.Contains(err.Error(), "unable to read bearer token file") + require.Error(t, err) + require.Contains(t, err.Error(), "unable to read bearer token file") } func TestConsulLogin_TokenFileUnwritable(t *testing.T) { t.Parallel() - require := require.New(t) bearerTokenFile := WriteTempFile(t, "foo") client := startMockServer(t) // This is a common.Logger. log, err := Logger("INFO", false) - require.NoError(err) + require.NoError(t, err) randFileName := fmt.Sprintf("/foo/%d/%d", rand.Int(), rand.Int()) params := LoginParams{ AuthMethod: testAuthMethod, @@ -201,13 +197,12 @@ func TestConsulLogin_TokenFileUnwritable(t *testing.T) { NumRetries: 2, } _, err = ConsulLogin(client, params, log) - require.Error(err) - require.Contains(err.Error(), "error writing token to file sink") + require.Error(t, err) + require.Contains(t, err.Error(), "error writing token to file sink") } func TestWriteFileWithPerms_InvalidOutputFile(t *testing.T) { t.Parallel() - rand.Seed(time.Now().UnixNano()) randFileName := fmt.Sprintf("/tmp/tmp/tmp/%d", rand.Int()) t.Cleanup(func() { os.RemoveAll(randFileName) @@ -218,7 +213,6 @@ func TestWriteFileWithPerms_InvalidOutputFile(t *testing.T) { func TestWriteFileWithPerms_OutputFileExists(t *testing.T) { t.Parallel() - rand.Seed(time.Now().UnixNano()) randFileName := fmt.Sprintf("/tmp/%d", rand.Int()) err := os.WriteFile(randFileName, []byte("foo"), os.FileMode(0444)) require.NoError(t, err) @@ -236,7 +230,6 @@ func TestWriteFileWithPerms_OutputFileExists(t *testing.T) { func TestWriteFileWithPerms(t *testing.T) { t.Parallel() payload := "foo-foo-foo-foo" - rand.Seed(time.Now().UnixNano()) randFileName := fmt.Sprintf("/tmp/%d", rand.Int()) t.Cleanup(func() { os.RemoveAll(randFileName) diff --git a/control-plane/subcommand/create-federation-secret/command_test.go b/control-plane/subcommand/create-federation-secret/command_test.go index d0f85fa686..86ff8aec18 100644 --- a/control-plane/subcommand/create-federation-secret/command_test.go +++ b/control-plane/subcommand/create-federation-secret/command_test.go @@ -320,7 +320,7 @@ func TestRun_ACLs_K8SNamespaces_ResourcePrefixes(tt *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(cfg *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(cfg *testutil.TestServerConfig) { cfg.CAFile = caFile cfg.CertFile = certFile cfg.KeyFile = keyFile @@ -330,11 +330,11 @@ func TestRun_ACLs_K8SNamespaces_ResourcePrefixes(tt *testing.T) { } }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() // Construct Consul client. client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -359,7 +359,7 @@ func TestRun_ACLs_K8SNamespaces_ResourcePrefixes(tt *testing.T) { // Redefine the client with the bootstrap token set so // subsequent calls will succeed. client, err = api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -444,8 +444,8 @@ func TestRun_ACLs_K8SNamespaces_ResourcePrefixes(tt *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), + "-consul-api-timeout", "10s", } if c.aclsEnabled { flags = append(flags, "-export-replication-token") @@ -503,27 +503,31 @@ func TestRun_WaitsForMeshGatewayInstances(t *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { c.CAFile = caFile c.CertFile = certFile c.KeyFile = keyFile }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() // Create a mesh gateway instance after a delay. meshGWIP := "192.168.0.1" meshGWPort := 443 go func() { - time.Sleep(500 * time.Millisecond) - client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, - Scheme: "https", - TLSConfig: api.TLSConfig{ - CAFile: caFile, - }, + var client *api.Client + timer := &retry.Timer{Timeout: 10 * time.Second, Wait: 500 * time.Millisecond} + retry.RunWith(timer, t, func(r *retry.R) { + client, err = api.NewClient(&api.Config{ + Address: testserver.HTTPSAddr, + Scheme: "https", + TLSConfig: api.TLSConfig{ + CAFile: caFile, + }, + }) + require.NoError(r, err) }) - require.NoError(t, err) + err = client.Agent().ServiceRegister(&api.AgentServiceRegistration{ Name: "mesh-gateway", TaggedAddresses: map[string]api.ServiceAddress{ @@ -551,8 +555,8 @@ func TestRun_WaitsForMeshGatewayInstances(t *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", certFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -572,15 +576,15 @@ func TestRun_MeshGatewayNoWANAddr(t *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { c.CAFile = caFile c.CertFile = certFile c.KeyFile = keyFile }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -605,8 +609,8 @@ func TestRun_MeshGatewayNoWANAddr(t *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), + "-consul-api-timeout", "10s", }) require.Equal(t, 1, exitCode, ui.ErrorWriter.String()) } @@ -643,17 +647,17 @@ func TestRun_MeshGatewayUniqueAddrs(tt *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { c.CAFile = caFile c.CertFile = certFile c.KeyFile = keyFile }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() // Create mesh gateway instances. client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -691,8 +695,8 @@ func TestRun_MeshGatewayUniqueAddrs(tt *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -722,7 +726,7 @@ func TestRun_ReplicationSecretDelay(t *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(cfg *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(cfg *testutil.TestServerConfig) { cfg.CAFile = caFile cfg.CertFile = certFile cfg.KeyFile = keyFile @@ -730,11 +734,11 @@ func TestRun_ReplicationSecretDelay(t *testing.T) { cfg.ACL.DefaultPolicy = "deny" }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() // Construct Consul client. client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -759,7 +763,7 @@ func TestRun_ReplicationSecretDelay(t *testing.T) { // Redefine the client with the bootstrap token set so // subsequent calls will succeed. client, err = api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -804,20 +808,22 @@ func TestRun_ReplicationSecretDelay(t *testing.T) { // Create replication token secret after a delay. go func() { - time.Sleep(400 * time.Millisecond) - _, err := k8s.CoreV1().Secrets("default").Create( - context.Background(), - &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "prefix-" + common.ACLReplicationTokenName + "-acl-token", - Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, - }, - Data: map[string][]byte{ - common.ACLTokenSecretKey: []byte(replicationToken), + timer := &retry.Timer{Timeout: 6 * time.Second, Wait: 400 * time.Millisecond} + retry.RunWith(timer, t, func(r *retry.R) { + _, err := k8s.CoreV1().Secrets("default").Create( + context.Background(), + &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "prefix-" + common.ACLReplicationTokenName + "-acl-token", + Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, + }, + Data: map[string][]byte{ + common.ACLTokenSecretKey: []byte(replicationToken), + }, }, - }, - metav1.CreateOptions{}) - require.NoError(t, err) + metav1.CreateOptions{}) + require.NoError(r, err) + }) }() // Run the command. @@ -833,7 +839,7 @@ func TestRun_ReplicationSecretDelay(t *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), "-export-replication-token", "-consul-api-timeout", "5s", } @@ -857,17 +863,17 @@ func TestRun_UpdatesSecret(t *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { c.CAFile = caFile c.CertFile = certFile c.KeyFile = keyFile }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() // Create a mesh gateway instance. client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -904,8 +910,8 @@ func TestRun_UpdatesSecret(t *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", certFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -946,8 +952,8 @@ func TestRun_UpdatesSecret(t *testing.T) { "-ca-file", caFile, "-server-ca-cert-file", caFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) @@ -975,31 +981,33 @@ func TestRun_ConsulClientDelay(t *testing.T) { k8s := fake.NewSimpleClientset() // Set up Consul server with TLS. Start after a 500ms delay. - var a *testutil.TestServer + var testserver *testutil.TestServer wg := sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() - time.Sleep(500 * time.Millisecond) - var err error - a, err = testutil.NewTestServerConfigT(t, func(cfg *testutil.TestServerConfig) { - cfg.CAFile = caFile - cfg.CertFile = certFile - cfg.KeyFile = keyFile - cfg.Ports = &testutil.TestPortConfig{ - DNS: randomPorts[0], - HTTP: randomPorts[1], - HTTPS: randomPorts[2], - SerfLan: randomPorts[3], - SerfWan: randomPorts[4], - Server: randomPorts[5], - } + timer := &retry.Timer{Timeout: 10 * time.Second, Wait: 500 * time.Millisecond} + retry.RunWith(timer, t, func(r *retry.R) { + var err error + testserver, err = testutil.NewTestServerConfigT(t, func(cfg *testutil.TestServerConfig) { + cfg.CAFile = caFile + cfg.CertFile = certFile + cfg.KeyFile = keyFile + cfg.Ports = &testutil.TestPortConfig{ + DNS: randomPorts[0], + HTTP: randomPorts[1], + HTTPS: randomPorts[2], + SerfLan: randomPorts[3], + SerfWan: randomPorts[4], + Server: randomPorts[5], + } + }) + require.NoError(r, err) }) - require.NoError(t, err) // Construct Consul client. client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -1022,8 +1030,8 @@ func TestRun_ConsulClientDelay(t *testing.T) { require.NoError(t, err) }() defer func() { - if a != nil { - a.Stop() + if testserver != nil { + testserver.Stop() } }() @@ -1062,17 +1070,17 @@ func TestRun_Autoencrypt(t *testing.T) { // Set up Consul server with TLS. caFile, certFile, keyFile := test.GenerateServerCerts(t) - a, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { + testserver, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { c.CAFile = caFile c.CertFile = certFile c.KeyFile = keyFile }) require.NoError(t, err) - defer a.Stop() + defer testserver.Stop() // Create a mesh gateway instance. client, err := api.NewClient(&api.Config{ - Address: a.HTTPSAddr, + Address: testserver.HTTPSAddr, Scheme: "https", TLSConfig: api.TLSConfig{ CAFile: caFile, @@ -1108,8 +1116,8 @@ func TestRun_Autoencrypt(t *testing.T) { // was being used as the CA (since it's not a CA). "-server-ca-cert-file", keyFile, "-server-ca-key-file", keyFile, - "-http-addr", fmt.Sprintf("https://%s", a.HTTPSAddr), - "-consul-api-timeout", "5s", + "-http-addr", fmt.Sprintf("https://%s", testserver.HTTPSAddr), + "-consul-api-timeout", "10s", }) require.Equal(t, 0, exitCode, ui.ErrorWriter.String()) diff --git a/control-plane/subcommand/get-consul-client-ca/command_test.go b/control-plane/subcommand/get-consul-client-ca/command_test.go index 9c48e63712..64f40a01c3 100644 --- a/control-plane/subcommand/get-consul-client-ca/command_test.go +++ b/control-plane/subcommand/get-consul-client-ca/command_test.go @@ -200,9 +200,10 @@ func TestRun_ConsulServerAvailableLater(t *testing.T) { }) require.NoError(t, err) + retrier := &retry.Timer{Timeout: 20 * time.Second, Wait: 1 * time.Second} // get the actual ca cert from consul var expectedCARoot string - retry.Run(t, func(r *retry.R) { + retry.RunWith(retrier, t, func(r *retry.R) { roots, _, err := client.Agent().ConnectCARoots(nil) require.NoError(r, err) require.NotNil(r, roots) diff --git a/control-plane/subcommand/inject-connect/command.go b/control-plane/subcommand/inject-connect/command.go index 93566387cd..769bc098e7 100644 --- a/control-plane/subcommand/inject-connect/command.go +++ b/control-plane/subcommand/inject-connect/command.go @@ -14,8 +14,10 @@ import ( apicommon "github.com/hashicorp/consul-k8s/control-plane/api/common" "github.com/hashicorp/consul-k8s/control-plane/api/v1alpha1" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/constants" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/controllers/endpoints" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/controllers/peering" + "github.com/hashicorp/consul-k8s/control-plane/connect-inject/lifecycle" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/metrics" "github.com/hashicorp/consul-k8s/control-plane/connect-inject/webhook" "github.com/hashicorp/consul-k8s/control-plane/controller" @@ -77,6 +79,13 @@ type Command struct { flagDefaultSidecarProxyMemoryRequest string flagDefaultEnvoyProxyConcurrency int + // Proxy lifecycle settings. + flagDefaultEnableSidecarProxyLifecycle bool + flagDefaultEnableSidecarProxyLifecycleShutdownDrainListeners bool + flagDefaultSidecarProxyLifecycleShutdownGracePeriodSeconds int + flagDefaultSidecarProxyLifecycleGracefulPort string + flagDefaultSidecarProxyLifecycleGracefulShutdownPath string + // Metrics settings. flagDefaultEnableMetrics bool flagEnableGatewayMetrics bool @@ -109,6 +118,9 @@ type Command struct { flagEnableAutoEncrypt bool + // Consul telemetry collector + flagEnableTelemetryCollector bool + // Consul DNS flags. flagEnableConsulDNS bool flagResourcePrefix string @@ -192,6 +204,8 @@ func (c *Command) init() { "Enables updating the CABundle on the webhook within this controller rather than using the web cert manager.") c.flagSet.BoolVar(&c.flagEnableAutoEncrypt, "enable-auto-encrypt", false, "Indicates whether TLS with auto-encrypt should be used when talking to Consul clients.") + c.flagSet.BoolVar(&c.flagEnableTelemetryCollector, "enable-telemetry-collector", false, + "Indicates whether proxies should be registered with configuration to enable forwarding metrics to consul-telemetry-collector") c.flagSet.StringVar(&c.flagLogLevel, "log-level", zapcore.InfoLevel.String(), fmt.Sprintf("Log verbosity level. Supported values (in order of detail) are "+ "%q, %q, %q, and %q.", zapcore.DebugLevel.String(), zapcore.InfoLevel.String(), zapcore.WarnLevel.String(), zapcore.ErrorLevel.String())) @@ -204,6 +218,13 @@ func (c *Command) init() { c.flagSet.StringVar(&c.flagDefaultSidecarProxyMemoryRequest, "default-sidecar-proxy-memory-request", "", "Default sidecar proxy memory request.") c.flagSet.StringVar(&c.flagDefaultSidecarProxyMemoryLimit, "default-sidecar-proxy-memory-limit", "", "Default sidecar proxy memory limit.") + // Proxy lifecycle setting flags. + c.flagSet.BoolVar(&c.flagDefaultEnableSidecarProxyLifecycle, "default-enable-sidecar-proxy-lifecycle", false, "Default for enabling sidecar proxy lifecycle management.") + c.flagSet.BoolVar(&c.flagDefaultEnableSidecarProxyLifecycleShutdownDrainListeners, "default-enable-sidecar-proxy-lifecycle-shutdown-drain-listeners", false, "Default for enabling sidecar proxy listener draining of inbound connections during shutdown.") + c.flagSet.IntVar(&c.flagDefaultSidecarProxyLifecycleShutdownGracePeriodSeconds, "default-sidecar-proxy-lifecycle-shutdown-grace-period-seconds", 0, "Default sidecar proxy shutdown grace period in seconds.") + c.flagSet.StringVar(&c.flagDefaultSidecarProxyLifecycleGracefulPort, "default-sidecar-proxy-lifecycle-graceful-port", strconv.Itoa(constants.DefaultGracefulPort), "Default port for sidecar proxy lifecycle management HTTP endpoints.") + c.flagSet.StringVar(&c.flagDefaultSidecarProxyLifecycleGracefulShutdownPath, "default-sidecar-proxy-lifecycle-graceful-shutdown-path", "/graceful_shutdown", "Default sidecar proxy lifecycle management graceful shutdown path.") + // Metrics setting flags. c.flagSet.BoolVar(&c.flagDefaultEnableMetrics, "default-enable-metrics", false, "Default for enabling connect service metrics.") c.flagSet.BoolVar(&c.flagEnableGatewayMetrics, "enable-gateway-metrics", false, "Allows enabling Consul gateway metrics.") @@ -406,6 +427,14 @@ func (c *Command) Run(args []string) int { return 1 } + lifecycleConfig := lifecycle.Config{ + DefaultEnableProxyLifecycle: c.flagDefaultEnableSidecarProxyLifecycle, + DefaultEnableShutdownDrainListeners: c.flagDefaultEnableSidecarProxyLifecycleShutdownDrainListeners, + DefaultShutdownGracePeriodSeconds: c.flagDefaultSidecarProxyLifecycleShutdownGracePeriodSeconds, + DefaultGracefulPort: c.flagDefaultSidecarProxyLifecycleGracefulPort, + DefaultGracefulShutdownPath: c.flagDefaultSidecarProxyLifecycleGracefulShutdownPath, + } + metricsConfig := metrics.Config{ DefaultEnableMetrics: c.flagDefaultEnableMetrics, EnableGatewayMetrics: c.flagEnableGatewayMetrics, @@ -438,6 +467,7 @@ func (c *Command) Run(args []string) int { ReleaseName: c.flagReleaseName, ReleaseNamespace: c.flagReleaseNamespace, EnableAutoEncrypt: c.flagEnableAutoEncrypt, + EnableTelemetryCollector: c.flagEnableTelemetryCollector, Context: ctx, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", endpoints.Controller{}) @@ -621,6 +651,7 @@ func (c *Command) Run(args []string) int { DefaultProxyMemoryRequest: sidecarProxyMemoryRequest, DefaultProxyMemoryLimit: sidecarProxyMemoryLimit, DefaultEnvoyProxyConcurrency: c.flagDefaultEnvoyProxyConcurrency, + LifecycleConfig: lifecycleConfig, MetricsConfig: metricsConfig, InitContainerResources: initResources, ConsulPartition: c.consul.Partition, diff --git a/control-plane/subcommand/server-acl-init/command.go b/control-plane/subcommand/server-acl-init/command.go index 698da2a25c..a444f65aaa 100644 --- a/control-plane/subcommand/server-acl-init/command.go +++ b/control-plane/subcommand/server-acl-init/command.go @@ -22,12 +22,11 @@ import ( "github.com/hashicorp/consul/api" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-netaddrs" + vaultApi "github.com/hashicorp/vault/api" "github.com/mitchellh/cli" "github.com/mitchellh/mapstructure" "golang.org/x/text/cases" "golang.org/x/text/language" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) @@ -86,8 +85,10 @@ type Command struct { flagEnableInjectK8SNSMirroring bool // Enables mirroring of k8s namespaces into Consul for Connect inject flagInjectK8SNSMirroringPrefix string // Prefix added to Consul namespaces created when mirroring injected services - // Flag to support a custom bootstrap token. - flagBootstrapTokenFile string + // Flags for the secrets backend. + flagSecretsBackend SecretsBackendType + flagBootstrapTokenSecretName string + flagBootstrapTokenSecretKey string flagLogLevel string flagLogJSON bool @@ -98,7 +99,9 @@ type Command struct { // flagFederation indicates if federation has been enabled in the cluster. flagFederation bool - clientset kubernetes.Interface + backend SecretsBackend // for unit testing. + clientset kubernetes.Interface + vaultClient *vaultApi.Client watcher consul.ServerConnectionManager @@ -194,9 +197,14 @@ func (c *Command) init() { c.flags.BoolVar(&c.flagFederation, "federation", false, "Toggle for when federation has been enabled.") - c.flags.StringVar(&c.flagBootstrapTokenFile, "bootstrap-token-file", "", - "Path to file containing ACL token for creating policies and tokens. This token must have 'acl:write' permissions."+ - "When provided, servers will not be bootstrapped and their policies and tokens will not be updated.") + c.flags.StringVar((*string)(&c.flagSecretsBackend), "secrets-backend", "kubernetes", + `The secrets backend to use. Either "vault" or "kubernetes". Defaults to "kubernetes"`) + c.flags.StringVar(&c.flagBootstrapTokenSecretName, "bootstrap-token-secret-name", "", + "The name of the Vault or Kuberenetes secret for the bootstrap token. This token must have `ac::write` permission "+ + "in order to create policies and tokens. If not provided or if the secret is empty, then this command will "+ + "bootstrap ACLs and write the bootstrap token to this secret.") + c.flags.StringVar(&c.flagBootstrapTokenSecretKey, "bootstrap-token-secret-key", "", + "The key within the Vault or Kuberenetes secret containing the bootstrap token.") c.flags.DurationVar(&c.flagTimeout, "timeout", 10*time.Minute, "How long we'll try to bootstrap ACLs for before timing out, e.g. 1ms, 2s, 3m") @@ -231,6 +239,7 @@ func (c *Command) Help() string { // The function will retry its tasks indefinitely until they are complete. func (c *Command) Run(args []string) int { c.once.Do(c.init) + defer c.quitVaultAgent() if err := c.flags.Parse(args); err != nil { return 1 } @@ -264,16 +273,6 @@ func (c *Command) Run(args []string) int { } } - var providedBootstrapToken string - if c.flagBootstrapTokenFile != "" { - var err error - providedBootstrapToken, err = loadTokenFromFile(c.flagBootstrapTokenFile) - if err != nil { - c.UI.Error(err.Error()) - return 1 - } - } - var cancel context.CancelFunc c.ctx, cancel = context.WithTimeout(context.Background(), c.flagTimeout) // The context will only ever be intentionally ended by the timeout. @@ -306,8 +305,12 @@ func (c *Command) Run(args []string) int { c.UI.Error(err.Error()) } - var bootstrapToken string + if err := c.configureSecretsBackend(); err != nil { + c.log.Error(err.Error()) + return 1 + } + var bootstrapToken string if c.flagACLReplicationTokenFile != "" && !c.flagCreateACLReplicationToken { // If ACL replication is enabled, we don't need to ACL bootstrap the servers // since they will be performing replication. @@ -316,21 +319,7 @@ func (c *Command) Run(args []string) int { c.log.Info("ACL replication is enabled so skipping Consul server ACL bootstrapping") bootstrapToken = aclReplicationToken } else { - // Check if we've already been bootstrapped. - var bootTokenSecretName string - if providedBootstrapToken != "" { - c.log.Info("Using provided bootstrap token") - bootstrapToken = providedBootstrapToken - } else { - bootTokenSecretName = c.withPrefix("bootstrap-acl-token") - bootstrapToken, err = c.getBootstrapToken(bootTokenSecretName) - if err != nil { - c.log.Error(fmt.Sprintf("Unexpected error looking for preexisting bootstrap Secret: %s", err)) - return 1 - } - } - - bootstrapToken, err = c.bootstrapServers(ipAddrs, bootstrapToken, bootTokenSecretName) + bootstrapToken, err = c.bootstrapServers(ipAddrs, c.backend) if err != nil { c.log.Error(err.Error()) return 1 @@ -806,24 +795,6 @@ func (c *Command) configureGateway(gatewayParams ConfigureGatewayParams, consulC return nil } -// getBootstrapToken returns the existing bootstrap token if there is one by -// reading the Kubernetes Secret with name secretName. -// If there is no bootstrap token yet, then it returns an empty string (not an error). -func (c *Command) getBootstrapToken(secretName string) (string, error) { - secret, err := c.clientset.CoreV1().Secrets(c.flagK8sNamespace).Get(c.ctx, secretName, metav1.GetOptions{}) - if err != nil { - if k8serrors.IsNotFound(err) { - return "", nil - } - return "", err - } - token, ok := secret.Data[common.ACLTokenSecretKey] - if !ok { - return "", fmt.Errorf("secret %q does not have data key 'token'", secretName) - } - return string(token), nil -} - func (c *Command) configureKubeClient() error { config, err := subcommand.K8SConfig(c.k8s.KubeConfig()) if err != nil { @@ -836,6 +807,55 @@ func (c *Command) configureKubeClient() error { return nil } +// configureSecretsBackend configures either the Kubernetes or Vault +// secrets backend based on flags. +func (c *Command) configureSecretsBackend() error { + if c.backend != nil { + // support a fake backend in unit tests + return nil + } + secretName := c.flagBootstrapTokenSecretName + if secretName == "" { + secretName = c.withPrefix("bootstrap-acl-token") + } + + secretKey := c.flagBootstrapTokenSecretKey + if secretKey == "" { + secretKey = common.ACLTokenSecretKey + } + + switch c.flagSecretsBackend { + case SecretsBackendTypeKubernetes: + c.backend = &KubernetesSecretsBackend{ + ctx: c.ctx, + clientset: c.clientset, + k8sNamespace: c.flagK8sNamespace, + secretName: secretName, + secretKey: secretKey, + } + return nil + case SecretsBackendTypeVault: + cfg := vaultApi.DefaultConfig() + cfg.Address = "" + cfg.AgentAddress = "http://127.0.0.1:8200" + vaultClient, err := vaultApi.NewClient(cfg) + if err != nil { + return fmt.Errorf("Error initializing Vault client: %w", err) + } + + c.vaultClient = vaultClient // must set this for c.quitVaultAgent. + c.backend = &VaultSecretsBackend{ + vaultClient: c.vaultClient, + secretName: secretName, + secretKey: secretKey, + } + return nil + default: + validValues := []SecretsBackendType{SecretsBackendTypeKubernetes, SecretsBackendTypeVault} + return fmt.Errorf("Invalid value for -secrets-backend: %q. Valid values are %v.", c.flagSecretsBackend, validValues) + } +} + // untilSucceeds runs op until it returns a nil error. // If c.cmdTimeout is cancelled it will exit. func (c *Command) untilSucceeds(opName string, op func() error) error { @@ -962,6 +982,10 @@ func (c *Command) validateFlags() error { return errors.New("-consul-api-timeout must be set to a value greater than 0") } + //if c.flagVaultNamespace != "" && c.flagSecretsBackend != SecretsBackendTypeVault { + // return fmt.Errorf("-vault-namespace not supported for -secrets-backend=%q", c.flagSecretsBackend) + //} + return nil } @@ -977,6 +1001,28 @@ func loadTokenFromFile(tokenFile string) (string, error) { return strings.TrimSpace(string(tokenBytes)), nil } +func (c *Command) quitVaultAgent() { + if c.vaultClient == nil { + return + } + + // Tell the Vault agent sidecar to quit. Without this, the Job does not + // complete because the Vault agent does not stop. This retries because it + // does not know exactly when the Vault agent sidecar will start. + err := c.untilSucceeds("tell Vault agent to quit", func() error { + // TODO: RawRequest is deprecated, but there is also not a high level + // method for this in the Vault client. + // nolint:staticcheck // SA1004 ignore + _, err := c.vaultClient.RawRequest( + c.vaultClient.NewRequest("POST", "/agent/v1/quit"), + ) + return err + }) + if err != nil { + c.log.Error("Error telling Vault agent to quit", "error", err) + } +} + const ( consulDefaultNamespace = "default" consulDefaultPartition = "default" diff --git a/control-plane/subcommand/server-acl-init/command_ent_test.go b/control-plane/subcommand/server-acl-init/command_ent_test.go index e31b787e4b..fa56f3bb3a 100644 --- a/control-plane/subcommand/server-acl-init/command_ent_test.go +++ b/control-plane/subcommand/server-acl-init/command_ent_test.go @@ -222,7 +222,6 @@ func TestRun_ConnectInject_NamespaceMirroring(t *testing.T) { // a non-default partition. func TestRun_AnonymousToken_CreatedFromNonDefaultPartition(t *testing.T) { bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" - tokenFile := common.WriteTempFile(t, bootToken) server := partitionedSetup(t, bootToken, "test") k8s := fake.NewSimpleClientset() setUpK8sServiceAccount(t, k8s, ns) @@ -231,6 +230,7 @@ func TestRun_AnonymousToken_CreatedFromNonDefaultPartition(t *testing.T) { cmd := Command{ UI: ui, clientset: k8s, + backend: &FakeSecretsBackend{bootstrapToken: bootToken}, } cmd.init() args := []string{ @@ -239,7 +239,6 @@ func TestRun_AnonymousToken_CreatedFromNonDefaultPartition(t *testing.T) { "-grpc-port=" + strings.Split(server.GRPCAddr, ":")[1], "-resource-prefix=" + resourcePrefix, "-k8s-namespace=" + ns, - "-bootstrap-token-file", tokenFile, "-allow-dns", "-partition=test", "-enable-namespaces", diff --git a/control-plane/subcommand/server-acl-init/command_test.go b/control-plane/subcommand/server-acl-init/command_test.go index 3111f58820..ffe60af593 100644 --- a/control-plane/subcommand/server-acl-init/command_test.go +++ b/control-plane/subcommand/server-acl-init/command_test.go @@ -60,13 +60,6 @@ func TestRun_FlagValidation(t *testing.T) { "-resource-prefix=prefix"}, ExpErr: "unable to read token from file \"/notexist\": open /notexist: no such file or directory", }, - { - Flags: []string{ - "-bootstrap-token-file=/notexist", - "-addresses=localhost", - "-resource-prefix=prefix"}, - ExpErr: "unable to read token from file \"/notexist\": open /notexist: no such file or directory", - }, { Flags: []string{ "-addresses=localhost", @@ -407,7 +400,6 @@ func TestRun_TokensWithProvidedBootstrapToken(t *testing.T) { for _, c := range cases { t.Run(c.TestName, func(t *testing.T) { bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" - tokenFile := common.WriteTempFile(t, bootToken) k8s, testAgent := completeBootstrappedSetup(t, bootToken) setUpK8sServiceAccount(t, k8s, ns) @@ -417,11 +409,11 @@ func TestRun_TokensWithProvidedBootstrapToken(t *testing.T) { cmd := Command{ UI: ui, clientset: k8s, + backend: &FakeSecretsBackend{bootstrapToken: bootToken}, } cmdArgs := append([]string{ "-timeout=1m", "-k8s-namespace", ns, - "-bootstrap-token-file", tokenFile, "-addresses", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0], "-http-port", strings.Split(testAgent.TestServer.HTTPAddr, ":")[1], "-grpc-port", strings.Split(testAgent.TestServer.GRPCAddr, ":")[1], @@ -915,7 +907,6 @@ func TestRun_ErrorsOnDuplicateACLPolicy(t *testing.T) { // Create Consul with ACLs already bootstrapped so that we can // then seed it with our manually created policy. bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" - tokenFile := common.WriteTempFile(t, bootToken) k8s, testAgent := completeBootstrappedSetup(t, bootToken) setUpK8sServiceAccount(t, k8s, ns) @@ -938,11 +929,11 @@ func TestRun_ErrorsOnDuplicateACLPolicy(t *testing.T) { cmd := Command{ UI: ui, clientset: k8s, + backend: &FakeSecretsBackend{bootstrapToken: bootToken}, } cmdArgs := []string{ "-timeout=1s", "-k8s-namespace", ns, - "-bootstrap-token-file", tokenFile, "-resource-prefix=" + resourcePrefix, "-k8s-namespace=" + ns, "-addresses", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0], @@ -1453,272 +1444,223 @@ func TestRun_ClientPolicyAndBindingRuleRetry(t *testing.T) { // Test if there is an old bootstrap Secret we still try to create and set // server tokens. func TestRun_AlreadyBootstrapped(t *testing.T) { - t.Parallel() - cases := map[string]bool{ - "token saved in k8s secret": true, - "token provided via file": false, - } - - for name, tokenFromK8sSecret := range cases { - t.Run(name, func(t *testing.T) { - k8s := fake.NewSimpleClientset() - - type APICall struct { - Method string - Path string - } - var consulAPICalls []APICall - - // Start the Consul server. - consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Record all the API calls made. - consulAPICalls = append(consulAPICalls, APICall{ - Method: r.Method, - Path: r.URL.Path, - }) - switch r.URL.Path { - case "/v1/agent/self": - fmt.Fprintln(w, `{"Config": {"Datacenter": "dc1", "PrimaryDatacenter": "dc1"}}`) - case "/v1/acl/tokens": - fmt.Fprintln(w, `[]`) - case "/v1/acl/token": - fmt.Fprintln(w, `{}`) - case "/v1/acl/policy": - fmt.Fprintln(w, `{}`) - case "/v1/agent/token/acl_agent_token": - fmt.Fprintln(w, `{}`) - case "/v1/acl/auth-method": - fmt.Fprintln(w, `{}`) - case "/v1/acl/role/name/release-name-consul-client-acl-role": - w.WriteHeader(404) - case "/v1/acl/role": - fmt.Fprintln(w, `{}`) - case "/v1/acl/binding-rules": - fmt.Fprintln(w, `[]`) - case "/v1/acl/binding-rule": - fmt.Fprintln(w, `{}`) - default: - w.WriteHeader(500) - fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) - } - })) - defer consulServer.Close() + k8s := fake.NewSimpleClientset() - serverURL, err := url.Parse(consulServer.URL) - require.NoError(t, err) - port, err := strconv.Atoi(serverURL.Port()) - require.NoError(t, err) - setUpK8sServiceAccount(t, k8s, ns) + type APICall struct { + Method string + Path string + } + var consulAPICalls []APICall - cmdArgs := []string{ - "-timeout=500ms", - "-resource-prefix=" + resourcePrefix, - "-k8s-namespace=" + ns, - "-addresses=" + serverURL.Hostname(), - "-http-port=" + serverURL.Port(), - } + // Start the Consul server. + consulServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Record all the API calls made. + consulAPICalls = append(consulAPICalls, APICall{ + Method: r.Method, + Path: r.URL.Path, + }) + switch r.URL.Path { + case "/v1/agent/self": + fmt.Fprintln(w, `{"Config": {"Datacenter": "dc1", "PrimaryDatacenter": "dc1"}}`) + case "/v1/acl/tokens": + fmt.Fprintln(w, `[]`) + case "/v1/acl/token": + fmt.Fprintln(w, `{}`) + case "/v1/acl/policy": + fmt.Fprintln(w, `{}`) + case "/v1/agent/token/acl_agent_token": + fmt.Fprintln(w, `{}`) + case "/v1/acl/auth-method": + fmt.Fprintln(w, `{}`) + case "/v1/acl/role/name/release-name-consul-client-acl-role": + w.WriteHeader(404) + case "/v1/acl/role": + fmt.Fprintln(w, `{}`) + case "/v1/acl/binding-rules": + fmt.Fprintln(w, `[]`) + case "/v1/acl/binding-rule": + fmt.Fprintln(w, `{}`) + default: + w.WriteHeader(500) + fmt.Fprintln(w, "Mock Server not configured for this route: "+r.URL.Path) + } + })) + defer consulServer.Close() - // Create the bootstrap secret. - if tokenFromK8sSecret { - _, err = k8s.CoreV1().Secrets(ns).Create( - context.Background(), - &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: resourcePrefix + "-bootstrap-acl-token", - Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, - }, - Data: map[string][]byte{ - "token": []byte("old-token"), - }, - }, - metav1.CreateOptions{}) - require.NoError(t, err) - } else { - // Write token to a file. - bootTokenFile, err := os.CreateTemp("", "") - require.NoError(t, err) - defer os.RemoveAll(bootTokenFile.Name()) + serverURL, err := url.Parse(consulServer.URL) + require.NoError(t, err) + port, err := strconv.Atoi(serverURL.Port()) + require.NoError(t, err) + setUpK8sServiceAccount(t, k8s, ns) - _, err = bootTokenFile.WriteString("old-token") - require.NoError(t, err) + cmdArgs := []string{ + "-timeout=500ms", + "-resource-prefix=" + resourcePrefix, + "-k8s-namespace=" + ns, + "-addresses=" + serverURL.Hostname(), + "-http-port=" + serverURL.Port(), + } - require.NoError(t, err) - cmdArgs = append(cmdArgs, "-bootstrap-token-file", bootTokenFile.Name()) - } + // Create the bootstrap secret. + _, err = k8s.CoreV1().Secrets(ns).Create( + context.Background(), + &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourcePrefix + "-bootstrap-acl-token", + Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, + }, + Data: map[string][]byte{ + "token": []byte("old-token"), + }, + }, + metav1.CreateOptions{}) + require.NoError(t, err) - // Run the command. - ui := cli.NewMockUi() - cmd := Command{ - UI: ui, - clientset: k8s, - watcher: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), - } + // Run the command. + ui := cli.NewMockUi() + cmd := Command{ + UI: ui, + clientset: k8s, + watcher: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), + } - responseCode := cmd.Run(cmdArgs) - require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) + responseCode := cmd.Run(cmdArgs) + require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) - // Test that the Secret is the same. - if tokenFromK8sSecret { - secret, err := k8s.CoreV1().Secrets(ns).Get(context.Background(), resourcePrefix+"-bootstrap-acl-token", metav1.GetOptions{}) - require.NoError(t, err) - require.Contains(t, secret.Data, "token") - require.Equal(t, "old-token", string(secret.Data["token"])) - } + // Test that the Secret is the same. + secret, err := k8s.CoreV1().Secrets(ns).Get(context.Background(), resourcePrefix+"-bootstrap-acl-token", metav1.GetOptions{}) + require.NoError(t, err) + require.Contains(t, secret.Data, "token") + require.Equal(t, "old-token", string(secret.Data["token"])) - // Test that the expected API calls were made. - require.Equal(t, []APICall{ - // We expect calls for updating the server policy, setting server tokens, - // and updating client policy. - { - "PUT", - "/v1/acl/policy", - }, - { - "GET", - "/v1/acl/tokens", - }, - { - "PUT", - "/v1/acl/token", - }, - { - "PUT", - "/v1/agent/token/agent", - }, - { - "PUT", - "/v1/agent/token/acl_agent_token", - }, - { - "GET", - "/v1/agent/self", - }, - { - "PUT", - "/v1/acl/auth-method", - }, - { - "PUT", - "/v1/acl/policy", - }, - { - "GET", - "/v1/acl/role/name/release-name-consul-client-acl-role", - }, - { - "PUT", - "/v1/acl/role", - }, - { - "GET", - "/v1/acl/binding-rules", - }, - { - "PUT", - "/v1/acl/binding-rule", - }, - }, consulAPICalls) - }) - } + // Test that the expected API calls were made. + require.Equal(t, []APICall{ + // We expect calls for updating the server policy, setting server tokens, + // and updating client policy. + { + "PUT", + "/v1/acl/policy", + }, + { + "GET", + "/v1/acl/tokens", + }, + { + "PUT", + "/v1/acl/token", + }, + { + "PUT", + "/v1/agent/token/agent", + }, + { + "PUT", + "/v1/agent/token/acl_agent_token", + }, + { + "GET", + "/v1/agent/self", + }, + { + "PUT", + "/v1/acl/auth-method", + }, + { + "PUT", + "/v1/acl/policy", + }, + { + "GET", + "/v1/acl/role/name/release-name-consul-client-acl-role", + }, + { + "PUT", + "/v1/acl/role", + }, + { + "GET", + "/v1/acl/binding-rules", + }, + { + "PUT", + "/v1/acl/binding-rule", + }, + }, consulAPICalls) } // Test if there is an old bootstrap Secret and the server token exists // that we don't try and recreate the token. func TestRun_AlreadyBootstrapped_ServerTokenExists(t *testing.T) { - t.Parallel() - cases := map[string]bool{ - "token saved in k8s secret": true, - "token provided via file": false, - } - - for name, tokenInK8sSecret := range cases { - t.Run(name, func(t *testing.T) { - - // First set everything up with ACLs bootstrapped. - bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" - k8s, testAgent := completeBootstrappedSetup(t, bootToken) - setUpK8sServiceAccount(t, k8s, ns) - - cmdArgs := []string{ - "-timeout=1m", - "-k8s-namespace", ns, - "-addresses", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0], - "-http-port", strings.Split(testAgent.TestServer.HTTPAddr, ":")[1], - "-grpc-port", strings.Split(testAgent.TestServer.GRPCAddr, ":")[1], - "-resource-prefix", resourcePrefix, - } - - if tokenInK8sSecret { - _, err := k8s.CoreV1().Secrets(ns).Create(context.Background(), &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: resourcePrefix + "-bootstrap-acl-token", - }, - Data: map[string][]byte{ - "token": []byte(bootToken), - }, - }, metav1.CreateOptions{}) - require.NoError(t, err) - } else { - // Write token to a file. - bootTokenFile, err := os.CreateTemp("", "") - require.NoError(t, err) - defer os.RemoveAll(bootTokenFile.Name()) + // First set everything up with ACLs bootstrapped. + bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + k8s, testAgent := completeBootstrappedSetup(t, bootToken) + setUpK8sServiceAccount(t, k8s, ns) - _, err = bootTokenFile.WriteString(bootToken) - require.NoError(t, err) + cmdArgs := []string{ + "-timeout=1m", + "-k8s-namespace", ns, + "-addresses", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0], + "-http-port", strings.Split(testAgent.TestServer.HTTPAddr, ":")[1], + "-grpc-port", strings.Split(testAgent.TestServer.GRPCAddr, ":")[1], + "-resource-prefix", resourcePrefix, + } - require.NoError(t, err) - cmdArgs = append(cmdArgs, "-bootstrap-token-file", bootTokenFile.Name()) - } + _, err := k8s.CoreV1().Secrets(ns).Create(context.Background(), &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourcePrefix + "-bootstrap-acl-token", + }, + Data: map[string][]byte{ + "token": []byte(bootToken), + }, + }, metav1.CreateOptions{}) + require.NoError(t, err) - consulClient, err := api.NewClient(&api.Config{ - Address: testAgent.TestServer.HTTPAddr, - Token: bootToken, - }) - require.NoError(t, err) - ui := cli.NewMockUi() - cmd := Command{ - UI: ui, - clientset: k8s, - } + consulClient, err := api.NewClient(&api.Config{ + Address: testAgent.TestServer.HTTPAddr, + Token: bootToken, + }) + require.NoError(t, err) + ui := cli.NewMockUi() + cmd := Command{ + UI: ui, + clientset: k8s, + } - cmd.init() - // Create the server policy and token _before_ we run the command. - agentPolicyRules, err := cmd.agentRules() - require.NoError(t, err) - policy, _, err := consulClient.ACL().PolicyCreate(&api.ACLPolicy{ - Name: "agent-token", - Description: "Agent Token Policy", - Rules: agentPolicyRules, - }, nil) - require.NoError(t, err) - _, _, err = consulClient.ACL().TokenCreate(&api.ACLToken{ - Description: fmt.Sprintf("Server Token for %s", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0]), - Policies: []*api.ACLTokenPolicyLink{ - { - Name: policy.Name, - }, - }, - }, nil) - require.NoError(t, err) + cmd.init() + // Create the server policy and token _before_ we run the command. + agentPolicyRules, err := cmd.agentRules() + require.NoError(t, err) + policy, _, err := consulClient.ACL().PolicyCreate(&api.ACLPolicy{ + Name: "agent-token", + Description: "Agent Token Policy", + Rules: agentPolicyRules, + }, nil) + require.NoError(t, err) + _, _, err = consulClient.ACL().TokenCreate(&api.ACLToken{ + Description: fmt.Sprintf("Server Token for %s", strings.Split(testAgent.TestServer.HTTPAddr, ":")[0]), + Policies: []*api.ACLTokenPolicyLink{ + { + Name: policy.Name, + }, + }, + }, nil) + require.NoError(t, err) - // Run the command. - responseCode := cmd.Run(cmdArgs) - require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) + // Run the command. + responseCode := cmd.Run(cmdArgs) + require.Equal(t, 0, responseCode, ui.ErrorWriter.String()) - // Check that only one server token exists, i.e. it didn't create an - // extra token. - tokens, _, err := consulClient.ACL().TokenList(nil) - require.NoError(t, err) - count := 0 - for _, token := range tokens { - if len(token.Policies) == 1 && token.Policies[0].Name == policy.Name { - count++ - } - } - require.Equal(t, 1, count) - }) + // Check that only one server token exists, i.e. it didn't create an + // extra token. + tokens, _, err := consulClient.ACL().TokenList(nil) + require.NoError(t, err) + count := 0 + for _, token := range tokens { + if len(token.Policies) == 1 && token.Policies[0].Name == policy.Name { + count++ + } } + require.Equal(t, 1, count) } // Test if -set-server-tokens is false (i.e. servers are disabled), we skip bootstrapping of the servers @@ -1728,7 +1670,6 @@ func TestRun_SkipBootstrapping_WhenServersAreDisabled(t *testing.T) { k8s := fake.NewSimpleClientset() bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" - tokenFile := common.WriteTempFile(t, bootToken) type APICall struct { Method string @@ -1766,6 +1707,7 @@ func TestRun_SkipBootstrapping_WhenServersAreDisabled(t *testing.T) { UI: ui, clientset: k8s, watcher: test.MockConnMgrForIPAndPort(serverURL.Hostname(), port), + backend: &FakeSecretsBackend{bootstrapToken: bootToken}, } responseCode := cmd.Run([]string{ "-timeout=500ms", @@ -1773,7 +1715,6 @@ func TestRun_SkipBootstrapping_WhenServersAreDisabled(t *testing.T) { "-k8s-namespace=" + ns, "-addresses=" + serverURL.Hostname(), "-http-port=" + serverURL.Port(), - "-bootstrap-token-file=" + tokenFile, "-set-server-tokens=false", "-client=false", // disable client token, so there are fewer calls }) diff --git a/control-plane/subcommand/server-acl-init/create_or_update.go b/control-plane/subcommand/server-acl-init/create_or_update.go index 833b923b90..af67842445 100644 --- a/control-plane/subcommand/server-acl-init/create_or_update.go +++ b/control-plane/subcommand/server-acl-init/create_or_update.go @@ -312,42 +312,37 @@ func (c *Command) createOrUpdateACLPolicy(policy api.ACLPolicy, consulClient *ap // Allowing the Consul node name to be configurable also requires any sync // policy to be updated in case the node name has changed. if isPolicyExistsErr(err, policy.Name) { - if c.flagEnableNamespaces || c.flagSyncCatalog { - c.log.Info(fmt.Sprintf("Policy %q already exists, updating", policy.Name)) + c.log.Info(fmt.Sprintf("Policy %q already exists, updating", policy.Name)) - // The policy ID is required in any PolicyUpdate call, so first we need to - // get the existing policy to extract its ID. - existingPolicies, _, err := consulClient.ACL().PolicyList(&api.QueryOptions{}) - if err != nil { - return err - } - - // Find the policy that matches our name and description - // and that's the ID we need - for _, existingPolicy := range existingPolicies { - if existingPolicy.Name == policy.Name && existingPolicy.Description == policy.Description { - policy.ID = existingPolicy.ID - } - } + // The policy ID is required in any PolicyUpdate call, so first we need to + // get the existing policy to extract its ID. + existingPolicies, _, err := consulClient.ACL().PolicyList(&api.QueryOptions{}) + if err != nil { + return err + } - // This shouldn't happen, because we're looking for a policy - // only after we've hit a `Policy already exists` error. - // The only time it might happen is if a user has manually created a policy - // with this name but used a different description. In this case, - // we don't want to overwrite the policy so we just error. - if policy.ID == "" { - return fmt.Errorf("policy found with name %q but not with expected description %q; "+ - "if this policy was created manually it must be renamed to something else because this name is reserved by consul-k8s", - policy.Name, policy.Description) + // Find the policy that matches our name and description + // and that's the ID we need + for _, existingPolicy := range existingPolicies { + if existingPolicy.Name == policy.Name && existingPolicy.Description == policy.Description { + policy.ID = existingPolicy.ID } + } - // Update the policy now that we've found its ID - _, _, err = consulClient.ACL().PolicyUpdate(&policy, &api.WriteOptions{}) - return err - } else { - c.log.Info(fmt.Sprintf("Policy %q already exists, skipping update", policy.Name)) - return nil + // This shouldn't happen, because we're looking for a policy + // only after we've hit a `Policy already exists` error. + // The only time it might happen is if a user has manually created a policy + // with this name but used a different description. In this case, + // we don't want to overwrite the policy so we just error. + if policy.ID == "" { + return fmt.Errorf("policy found with name %q but not with expected description %q; "+ + "if this policy was created manually it must be renamed to something else because this name is reserved by consul-k8s", + policy.Name, policy.Description) } + + // Update the policy now that we've found its ID + _, _, err = consulClient.ACL().PolicyUpdate(&policy, &api.WriteOptions{}) + return err } return err } diff --git a/control-plane/subcommand/server-acl-init/create_or_update_test.go b/control-plane/subcommand/server-acl-init/create_or_update_test.go index 259707f85d..23ab46347f 100644 --- a/control-plane/subcommand/server-acl-init/create_or_update_test.go +++ b/control-plane/subcommand/server-acl-init/create_or_update_test.go @@ -67,3 +67,71 @@ func TestCreateOrUpdateACLPolicy_ErrorsIfDescriptionDoesNotMatch(t *testing.T) { require.NoError(err) require.Equal(policyDescription, rereadPolicy.Description) } + +func TestCreateOrUpdateACLPolicy(t *testing.T) { + require := require.New(t) + ui := cli.NewMockUi() + k8s := fake.NewSimpleClientset() + cmd := Command{ + UI: ui, + clientset: k8s, + log: hclog.NewNullLogger(), + } + cmd.init() + // Start Consul. + bootToken := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" + svr, err := testutil.NewTestServerConfigT(t, func(c *testutil.TestServerConfig) { + c.ACL.Enabled = true + c.ACL.Tokens.InitialManagement = bootToken + }) + require.NoError(err) + defer svr.Stop() + svr.WaitForLeader(t) + + // Get a Consul client. + consul, err := api.NewClient(&api.Config{ + Address: svr.HTTPAddr, + Token: bootToken, + }) + require.NoError(err) + connectInjectRule, err := cmd.injectRules() + require.NoError(err) + aclReplRule, err := cmd.aclReplicationRules() + require.NoError(err) + policyDescription := "policy-description" + policyName := "policy-name" + cases := []struct { + Name string + PolicyDescription string + PolicyName string + Rules string + }{ + { + Name: "create", + PolicyDescription: policyDescription, + PolicyName: policyName, + Rules: connectInjectRule, + }, + { + Name: "update", + PolicyDescription: policyDescription, + PolicyName: policyName, + Rules: aclReplRule, + }, + } + for _, tt := range cases { + t.Run(tt.Name, func(t *testing.T) { + err = cmd.createOrUpdateACLPolicy(api.ACLPolicy{ + Name: tt.PolicyName, + Description: tt.PolicyDescription, + Rules: tt.Rules, + }, consul) + require.Nil(err) + policy, _, err := consul.ACL().PolicyReadByName(tt.PolicyName, nil) + require.Nil(err) + require.Equal(tt.Rules, policy.Rules) + require.Equal(tt.PolicyName, policy.Name) + require.Equal(tt.PolicyDescription, policy.Description) + }) + } +} diff --git a/control-plane/subcommand/server-acl-init/k8s_secrets_backend.go b/control-plane/subcommand/server-acl-init/k8s_secrets_backend.go new file mode 100644 index 0000000000..4e3efcf65b --- /dev/null +++ b/control-plane/subcommand/server-acl-init/k8s_secrets_backend.go @@ -0,0 +1,62 @@ +package serveraclinit + +import ( + "context" + "fmt" + + "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" + apiv1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" +) + +const SecretsBackendTypeKubernetes SecretsBackendType = "kubernetes" + +type KubernetesSecretsBackend struct { + ctx context.Context + clientset kubernetes.Interface + k8sNamespace string + secretName string + secretKey string +} + +var _ SecretsBackend = (*KubernetesSecretsBackend)(nil) + +// BootstrapToken returns the existing bootstrap token if there is one by +// reading the Kubernetes Secret. If there is no bootstrap token yet, then +// it returns an empty string (not an error). +func (b *KubernetesSecretsBackend) BootstrapToken() (string, error) { + secret, err := b.clientset.CoreV1().Secrets(b.k8sNamespace).Get(b.ctx, b.secretName, metav1.GetOptions{}) + if err != nil { + if k8serrors.IsNotFound(err) { + return "", nil + } + return "", err + } + token, ok := secret.Data[b.secretKey] + if !ok { + return "", fmt.Errorf("secret %q does not have data key %q", b.secretName, b.secretKey) + } + return string(token), nil + +} + +// WriteBootstrapToken writes the given bootstrap token to the Kubernetes Secret. +func (b *KubernetesSecretsBackend) WriteBootstrapToken(bootstrapToken string) error { + secret := &apiv1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: b.secretName, + Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, + }, + Data: map[string][]byte{ + b.secretKey: []byte(bootstrapToken), + }, + } + _, err := b.clientset.CoreV1().Secrets(b.k8sNamespace).Create(b.ctx, secret, metav1.CreateOptions{}) + return err +} + +func (b *KubernetesSecretsBackend) BootstrapTokenSecretName() string { + return b.secretName +} diff --git a/control-plane/subcommand/server-acl-init/rules.go b/control-plane/subcommand/server-acl-init/rules.go index ee6ae41e40..02831dc4de 100644 --- a/control-plane/subcommand/server-acl-init/rules.go +++ b/control-plane/subcommand/server-acl-init/rules.go @@ -151,8 +151,10 @@ partition "{{ .PartitionName }}" { operator = "write" acl = "write" {{- end }} + {{- if .EnableNamespaces }} namespace_prefix "" { + policy = "write" {{- end }} service_prefix "" { policy = "write" @@ -167,7 +169,7 @@ namespace_prefix "" { {{- if .EnablePartitions }} } {{- end }} - ` +` return c.renderRules(apiGatewayRulesTpl) } diff --git a/control-plane/subcommand/server-acl-init/rules_test.go b/control-plane/subcommand/server-acl-init/rules_test.go index 22e63ed0ce..622ed341a8 100644 --- a/control-plane/subcommand/server-acl-init/rules_test.go +++ b/control-plane/subcommand/server-acl-init/rules_test.go @@ -143,6 +143,7 @@ func TestAPIGatewayControllerRules(t *testing.T) { cases := []struct { Name string EnableNamespaces bool + Partition string Expected string }{ { @@ -165,6 +166,7 @@ acl = "write" operator = "write" acl = "write" namespace_prefix "" { + policy = "write" service_prefix "" { policy = "write" intentions = "write" @@ -172,6 +174,26 @@ namespace_prefix "" { node_prefix "" { policy = "read" } +}`, + }, + { + Name: "Namespaces are enabled, partitions enabled", + EnableNamespaces: true, + Partition: "Default", + Expected: ` +partition "Default" { + mesh = "write" + acl = "write" +namespace_prefix "" { + policy = "write" + service_prefix "" { + policy = "write" + intentions = "write" + } + node_prefix "" { + policy = "read" + } +} }`, }, } @@ -180,7 +202,9 @@ namespace_prefix "" { t.Run(tt.Name, func(t *testing.T) { cmd := Command{ flagEnableNamespaces: tt.EnableNamespaces, - consulFlags: &flags.ConsulFlags{}, + consulFlags: &flags.ConsulFlags{ + Partition: tt.Partition, + }, } meshGatewayRules, err := cmd.apiGatewayControllerRules() diff --git a/control-plane/subcommand/server-acl-init/secrets_backend.go b/control-plane/subcommand/server-acl-init/secrets_backend.go new file mode 100644 index 0000000000..4b4d5c2fc4 --- /dev/null +++ b/control-plane/subcommand/server-acl-init/secrets_backend.go @@ -0,0 +1,18 @@ +package serveraclinit + +type SecretsBackendType string + +type SecretsBackend interface { + // BootstrapToken fetches the bootstrap token from the backend. If the + // token is not found or empty, implementations should return an empty + // string (not an error). + BootstrapToken() (string, error) + + // WriteBootstrapToken writes the given bootstrap token to the backend. + // Implementations of this method do not need to retry the write until + // successful. + WriteBootstrapToken(string) error + + // BootstrapTokenSecretName returns the name of the bootstrap token secret. + BootstrapTokenSecretName() string +} diff --git a/control-plane/subcommand/server-acl-init/servers.go b/control-plane/subcommand/server-acl-init/servers.go index 2dc8f8ab67..01e9a58145 100644 --- a/control-plane/subcommand/server-acl-init/servers.go +++ b/control-plane/subcommand/server-acl-init/servers.go @@ -1,7 +1,6 @@ package serveraclinit import ( - "errors" "fmt" "net" "net/http" @@ -9,29 +8,30 @@ import ( "time" "github.com/hashicorp/consul/api" - apiv1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/hashicorp/consul-k8s/control-plane/consul" - "github.com/hashicorp/consul-k8s/control-plane/subcommand/common" ) // bootstrapServers bootstraps ACLs and ensures each server has an ACL token. -// If bootstrapToken is not empty then ACLs are already bootstrapped. -func (c *Command) bootstrapServers(serverAddresses []net.IPAddr, bootstrapToken, bootTokenSecretName string) (string, error) { +// If a bootstrap is found in the secrets backend, then skip ACL bootstrapping. +// Otherwise, bootstrap ACLs and write the bootstrap token to the secrets backend. +func (c *Command) bootstrapServers(serverAddresses []net.IPAddr, backend SecretsBackend) (string, error) { // Pick the first server address to connect to for bootstrapping and set up connection. firstServerAddr := fmt.Sprintf("%s:%d", serverAddresses[0].IP.String(), c.consulFlags.HTTPPort) - if bootstrapToken == "" { - c.log.Info("No bootstrap token from previous installation found, continuing on to bootstrapping") + bootstrapToken, err := backend.BootstrapToken() + if err != nil { + return "", fmt.Errorf("Unexpected error fetching bootstrap token secret: %w", err) + } - var err error - bootstrapToken, err = c.bootstrapACLs(firstServerAddr, bootTokenSecretName) + if bootstrapToken != "" { + c.log.Info("Found bootstrap token in secrets backend", "secret", backend.BootstrapTokenSecretName()) + } else { + c.log.Info("No bootstrap token found in secrets backend, continuing to ACL bootstrapping", "secret", backend.BootstrapTokenSecretName()) + bootstrapToken, err = c.bootstrapACLs(firstServerAddr, backend) if err != nil { return "", err } - } else { - c.log.Info(fmt.Sprintf("ACLs already bootstrapped - retrieved bootstrap token from Secret %q", bootTokenSecretName)) } // We should only create and set server tokens when servers are running within this cluster. @@ -47,7 +47,7 @@ func (c *Command) bootstrapServers(serverAddresses []net.IPAddr, bootstrapToken, // bootstrapACLs makes the ACL bootstrap API call and writes the bootstrap token // to a kube secret. -func (c *Command) bootstrapACLs(firstServerAddr, bootTokenSecretName string) (string, error) { +func (c *Command) bootstrapACLs(firstServerAddr string, backend SecretsBackend) (string, error) { config := c.consulFlags.ConsulClientConfig().APIClientConfig config.Address = firstServerAddr // Exempting this particular use of the http client from using global.consulAPITimeout @@ -78,9 +78,12 @@ func (c *Command) bootstrapACLs(firstServerAddr, bootTokenSecretName string) (st // Check if already bootstrapped. if strings.Contains(err.Error(), "Unexpected response code: 403") { - unrecoverableErr = errors.New("ACLs already bootstrapped but the ACL token was not written to a Kubernetes secret." + - " We can't proceed because the bootstrap token is lost." + - " You must reset ACLs.") + unrecoverableErr = fmt.Errorf( + "ACLs already bootstrapped but unable to find the bootstrap token in the secrets backend."+ + " We can't proceed without a bootstrap token."+ + " Store a token with `acl:write` permission in the secret %q.", + backend.BootstrapTokenSecretName(), + ) return nil } @@ -98,21 +101,12 @@ func (c *Command) bootstrapACLs(firstServerAddr, bootTokenSecretName string) (st return "", err } - // Write bootstrap token to a Kubernetes secret. - err = c.untilSucceeds(fmt.Sprintf("writing bootstrap Secret %q", bootTokenSecretName), + // Write bootstrap token to the secrets backend. + err = c.untilSucceeds(fmt.Sprintf("writing bootstrap Secret %q", backend.BootstrapTokenSecretName()), func() error { - secret := &apiv1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: bootTokenSecretName, - Labels: map[string]string{common.CLILabelKey: common.CLILabelValue}, - }, - Data: map[string][]byte{ - common.ACLTokenSecretKey: []byte(bootstrapToken), - }, - } - _, err := c.clientset.CoreV1().Secrets(c.flagK8sNamespace).Create(c.ctx, secret, metav1.CreateOptions{}) - return err - }) + return backend.WriteBootstrapToken(bootstrapToken) + }, + ) return bootstrapToken, err } diff --git a/control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go b/control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go new file mode 100644 index 0000000000..5c9a63f1a5 --- /dev/null +++ b/control-plane/subcommand/server-acl-init/test_fake_secrets_backend.go @@ -0,0 +1,20 @@ +package serveraclinit + +type FakeSecretsBackend struct { + bootstrapToken string +} + +func (b *FakeSecretsBackend) BootstrapToken() (string, error) { + return b.bootstrapToken, nil +} + +func (*FakeSecretsBackend) BootstrapTokenSecretName() string { + return "fake-bootstrap-token" +} + +func (b *FakeSecretsBackend) WriteBootstrapToken(token string) error { + b.bootstrapToken = token + return nil +} + +var _ SecretsBackend = (*FakeSecretsBackend)(nil) diff --git a/control-plane/subcommand/server-acl-init/vault_secrets_backend.go b/control-plane/subcommand/server-acl-init/vault_secrets_backend.go new file mode 100644 index 0000000000..2706567878 --- /dev/null +++ b/control-plane/subcommand/server-acl-init/vault_secrets_backend.go @@ -0,0 +1,66 @@ +package serveraclinit + +import ( + "fmt" + + "github.com/hashicorp/vault/api" +) + +const SecretsBackendTypeVault SecretsBackendType = "vault" + +type VaultSecretsBackend struct { + vaultClient *api.Client + secretName string + secretKey string +} + +var _ SecretsBackend = (*VaultSecretsBackend)(nil) + +// BootstrapToken returns the bootstrap token stored in Vault. +// If not found this returns an empty string (not an error). +func (b *VaultSecretsBackend) BootstrapToken() (string, error) { + secret, err := b.vaultClient.Logical().Read(b.secretName) + if err != nil { + return "", err + } + if secret == nil || secret.Data == nil { + // secret not found or empty. + return "", nil + } + // Grab secret.Data["data"][secretKey]. + dataRaw, found := secret.Data["data"] + if !found { + return "", nil + } + data, ok := dataRaw.(map[string]interface{}) + if !ok { + return "", nil + } + tokRaw, found := data[b.secretKey] + if !found { + return "", nil + } + if tok, ok := tokRaw.(string); ok { + return tok, nil + } + return "", fmt.Errorf("Unexpected data. To resolve this, "+ + "`vault kv put %[1]s=` if Consul is already ACL bootstrapped. "+ + "If not ACL bootstrapped, `vault kv put %[1]s=\"\"`", b.secretKey, b.secretKey) +} + +// BootstrapTokenSecretName returns the name of the bootstrap token secret. +func (b *VaultSecretsBackend) BootstrapTokenSecretName() string { + return b.secretName +} + +// WriteBootstrapToken writes the bootstrap token to Vault. +func (b *VaultSecretsBackend) WriteBootstrapToken(bootstrapToken string) error { + _, err := b.vaultClient.Logical().Write(b.secretName, + map[string]interface{}{ + "data": map[string]interface{}{ + b.secretKey: bootstrapToken, + }, + }, + ) + return err +} diff --git a/control-plane/subcommand/sync-catalog/command.go b/control-plane/subcommand/sync-catalog/command.go index f890b44f34..e146a76667 100644 --- a/control-plane/subcommand/sync-catalog/command.go +++ b/control-plane/subcommand/sync-catalog/command.go @@ -64,6 +64,10 @@ type Command struct { flagK8SNSMirroringPrefix string // Prefix added to Consul namespaces created when mirroring flagCrossNamespaceACLPolicy string // The name of the ACL policy to add to every created namespace if ACLs are enabled + // Flags to support Kubernetes Ingress resources + flagEnableIngress bool // Register services using the hostname from an ingress resource + flagLoadBalancerIPs bool // Use the load balancer IP of an ingress resource instead of the hostname + clientset kubernetes.Interface // ready indicates whether this controller is ready to sync services. This will be changed to true once the @@ -148,6 +152,11 @@ func (c *Command) init() { "[Enterprise Only] Name of the ACL policy to attach to all created Consul namespaces to allow service "+ "discovery across Consul namespaces. Only necessary if ACLs are enabled.") + c.flags.BoolVar(&c.flagEnableIngress, "enable-ingress", false, + "[Enterprise Only] Enables namespaces, in either a single Consul namespace or mirrored.") + c.flags.BoolVar(&c.flagLoadBalancerIPs, "loadBalancer-ips", false, + "[Enterprise Only] Enables namespaces, in either a single Consul namespace or mirrored.") + c.consul = &flags.ConsulFlags{} c.k8s = &flags.K8SFlags{} flags.Merge(c.flags, c.consul.Flags()) @@ -291,6 +300,8 @@ func (c *Command) Run(args []string) int { EnableK8SNSMirroring: c.flagEnableK8SNSMirroring, K8SNSMirroringPrefix: c.flagK8SNSMirroringPrefix, ConsulNodeName: c.flagConsulNodeName, + EnableIngress: c.flagEnableIngress, + SyncLoadBalancerIPs: c.flagLoadBalancerIPs, }, } diff --git a/control-plane/subcommand/tls-init/command_test.go b/control-plane/subcommand/tls-init/command_test.go index 33493bba16..ae3cbd8982 100644 --- a/control-plane/subcommand/tls-init/command_test.go +++ b/control-plane/subcommand/tls-init/command_test.go @@ -395,7 +395,9 @@ func TestRun_CreatesServerCertificatesWithExpiryWithinSpecifiedDays(t *testing.T certBlock, _ := pem.Decode(newServerCert) certificate, err := x509.ParseCertificate(certBlock.Bytes) require.NoError(t, err) - require.Equal(t, time.Now().AddDate(1, 0, 0).Unix(), certificate.NotAfter.Unix()) + + // Add 365 days instead of 1 year to account for leap years + require.Equal(t, time.Now().AddDate(0, 0, 365).Unix(), certificate.NotAfter.Unix()) } func TestRun_CreatesServerCertificatesWithProvidedHosts(t *testing.T) { diff --git a/control-plane/version/version.go b/control-plane/version/version.go index 933f072f35..5cf34d7c8c 100644 --- a/control-plane/version/version.go +++ b/control-plane/version/version.go @@ -14,7 +14,7 @@ var ( // // Version must conform to the format expected by // github.com/hashicorp/go-version for tests to work. - Version = "1.1.0" + Version = "1.1.5" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release diff --git a/hack/aws-acceptance-test-cleanup/go.mod b/hack/aws-acceptance-test-cleanup/go.mod index 13e8f48909..ac4f7b0d1a 100644 --- a/hack/aws-acceptance-test-cleanup/go.mod +++ b/hack/aws-acceptance-test-cleanup/go.mod @@ -1,6 +1,6 @@ module github.com/hashicorp/consul-helm/hack/aws-acceptance-test-cleanup -go 1.19 +go 1.20 require ( github.com/aws/aws-sdk-go v1.38.63 diff --git a/hack/copy-crds-to-chart/go.mod b/hack/copy-crds-to-chart/go.mod index 73b1f10306..c224f8f244 100644 --- a/hack/copy-crds-to-chart/go.mod +++ b/hack/copy-crds-to-chart/go.mod @@ -1,3 +1,3 @@ module github.com/hashicorp/consul-k8s/hack/copy-crds-to-chart -go 1.19 +go 1.20 diff --git a/hack/helm-reference-gen/go.mod b/hack/helm-reference-gen/go.mod index 7e41675f18..36e1ff3a8d 100644 --- a/hack/helm-reference-gen/go.mod +++ b/hack/helm-reference-gen/go.mod @@ -1,6 +1,6 @@ module github.com/hashicorp/consul-k8s/hack/helm-reference-gen -go 1.19 +go 1.20 require ( github.com/stretchr/testify v1.6.1