From 3b18e8de94552fe09929be96527531c9c9b7a9ce Mon Sep 17 00:00:00 2001 From: Michael Zalimeni Date: Mon, 16 Oct 2023 21:30:26 +0000 Subject: [PATCH] backport of commit 2c3a6d119e73aecd464a7bb21eb0562a77fdbc61 --- .changelog/164.txt | 3 - .changelog/188.txt | 3 - .changelog/239.txt | 3 - .changelog/242.txt | 3 - .github/workflows/bot-auto-approve.yaml | 2 +- .github/workflows/build.yml | 38 +- .github/workflows/changelog-checker.yml | 2 +- .../workflows/consul-dataplane-checks.yaml | 11 +- .github/workflows/jira-issues.yaml | 6 +- .github/workflows/jira-pr.yaml | 8 +- .golangci.yml | 6 - CHANGELOG.md | 37 + Makefile | 7 +- cmd/consul-dataplane/config.go | 372 ------ cmd/consul-dataplane/config_test.go | 1017 ----------------- cmd/consul-dataplane/duration.go | 40 - cmd/consul-dataplane/duration_test.go | 66 -- cmd/consul-dataplane/env.go | 60 +- cmd/consul-dataplane/flags.go | 196 +--- cmd/consul-dataplane/main.go | 343 +++--- go.mod | 9 +- go.sum | 12 +- integration-tests/main_test.go | 36 +- internal/bootstrap/bootstrap_tpl.go | 6 + pkg/consuldp/bootstrap.go | 42 +- pkg/consuldp/bootstrap_test.go | 140 +-- pkg/consuldp/config.go | 24 +- pkg/consuldp/consul_dataplane.go | 10 +- pkg/consuldp/consul_dataplane_test.go | 34 +- pkg/consuldp/lifecycle.go | 93 +- pkg/consuldp/lifecycle_test.go | 170 +-- pkg/consuldp/metrics_test.go | 2 +- .../TestBootstrapConfig/access-logs.golden | 6 + .../testdata/TestBootstrapConfig/basic.golden | 6 + .../central-telemetry-config.golden | 6 + .../custom-prometheus-scrape-path.golden | 6 + .../TestBootstrapConfig/hcp-metrics.golden | 6 + .../TestBootstrapConfig/ready-listener.golden | 6 + .../unix-socket-xds-server.golden | 6 + pkg/envoy/get_process_attr.go | 15 - pkg/envoy/get_process_attr_windows.go | 15 - pkg/envoy/proxy.go | 73 +- pkg/version/version.go | 2 +- 43 files changed, 495 insertions(+), 2453 deletions(-) delete mode 100644 .changelog/164.txt delete mode 100644 .changelog/188.txt delete mode 100644 .changelog/239.txt delete mode 100644 .changelog/242.txt delete mode 100644 .golangci.yml delete mode 100644 cmd/consul-dataplane/config.go delete mode 100644 cmd/consul-dataplane/config_test.go delete mode 100644 cmd/consul-dataplane/duration.go delete mode 100644 cmd/consul-dataplane/duration_test.go delete mode 100644 pkg/envoy/get_process_attr.go delete mode 100644 pkg/envoy/get_process_attr_windows.go diff --git a/.changelog/164.txt b/.changelog/164.txt deleted file mode 100644 index 03a978de..00000000 --- a/.changelog/164.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -Add the `-config-file` flag to support reading configuration options from a JSON file. -``` \ No newline at end of file diff --git a/.changelog/188.txt b/.changelog/188.txt deleted file mode 100644 index f039a85f..00000000 --- a/.changelog/188.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -In order to support Windows, write Envoy bootstrap configuration to a regular file instead of a named pipe. -``` diff --git a/.changelog/239.txt b/.changelog/239.txt deleted file mode 100644 index d562d630..00000000 --- a/.changelog/239.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -Add graceful_startup endpoint and postStart hook in order to guarantee that dataplane starts up before application container. -``` diff --git a/.changelog/242.txt b/.changelog/242.txt deleted file mode 100644 index cf6d0b37..00000000 --- a/.changelog/242.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -Make consul dataplane handle bootstrap param response for Catalog and Mesh V2 resources -``` diff --git a/.github/workflows/bot-auto-approve.yaml b/.github/workflows/bot-auto-approve.yaml index 20cbb383..5127029a 100644 --- a/.github/workflows/bot-auto-approve.yaml +++ b/.github/workflows/bot-auto-approve.yaml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest if: github.actor == 'hc-github-team-consul-core' steps: - - uses: hmarr/auto-approve-action@44888193675f29a83e04faf4002fa8c0b537b1e4 # v3.2.1 + - uses: hmarr/auto-approve-action@v3 # TSCCR: no entry for repository "hmarr/auto-approve-action" with: review-message: "Auto approved Consul Bot automated PR" github-token: ${{ secrets.MERGE_APPROVE_TOKEN }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 626301de..3b6d3e7e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,7 @@ jobs: outputs: go-version: ${{ steps.get-go-version.outputs.go-version }} steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Determine Go version id: get-go-version # We use .go-version as our source of truth for current Go @@ -35,7 +35,7 @@ jobs: outputs: product-version: ${{ steps.get-product-version.outputs.product-version }} steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: get product version id: get-product-version run: | @@ -49,7 +49,7 @@ jobs: filepath: ${{ steps.generate-metadata-file.outputs.filepath }} steps: - name: "Checkout directory" - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Generate metadata file id: generate-metadata-file uses: hashicorp/actions-generate-metadata@v1 @@ -82,7 +82,7 @@ jobs: name: Go ${{ needs.get-go-version.outputs.go-version }} ${{ matrix.goos }} ${{ matrix.goarch }} ${{ matrix.fips }} build steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: hashicorp/actions-go-build@v0.1.3 with: @@ -150,7 +150,7 @@ jobs: GOARCH: ${{ matrix.goarch }} steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: hashicorp/actions-go-build@v0.1.3 with: @@ -182,12 +182,14 @@ jobs: version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + # Strip everything but MAJOR.MINOR from the version string and add a `-dev` suffix # This naming convention will be used ONLY for per-commit dev images - name: Set docker dev tag run: | - echo "dev_tag=${{ env.version }}" >> $GITHUB_ENV + version="${{ env.version }}" + echo "dev_tag=${version%.*}-dev" >> $GITHUB_ENV - name: Docker Build (Action) if: ${{ !matrix.fips }} @@ -248,7 +250,7 @@ jobs: version: ${{needs.get-product-version.outputs.product-version}}${{ matrix.fips }} steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: hashicorp/actions-docker-build@v1 with: version: ${{env.version}} @@ -271,11 +273,13 @@ jobs: repo: ${{ github.event.repository.name }} version: ${{ needs.get-product-version.outputs.product-version }}${{ matrix.fips }} steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + # Strip everything but MAJOR.MINOR from the version string and add a `-dev` suffix # This naming convention will be used ONLY for per-commit dev images - name: Set docker dev tag run: | - echo "dev_tag=${{ env.version }}" >> $GITHUB_ENV + version="${{ env.version }}" + echo "dev_tag=${version%.*}-dev" >> $GITHUB_ENV - name: Docker Build (Action) if: ${{ !matrix.fips }} @@ -329,16 +333,16 @@ jobs: strategy: matrix: server: + - version: v1.14.4 + image: docker.mirror.hashicorp.services/hashicorp/consul:1.14.4 - version: v1.15.0-dev image: hashicorppreview/consul:1.15-dev - version: v1.16.0-dev image: hashicorppreview/consul:1.16-dev - - version: v1.17.0-dev - image: hashicorppreview/consul:1.17-dev dataplane: - - image_suffix: "" + - image_suffix: "dev" docker_target: "release-default" - - image_suffix: "-ubi" + - image_suffix: "dev-ubi" docker_target: "release-ubi" env: repo: ${{ github.event.repository.name }} @@ -347,7 +351,7 @@ jobs: - name: Set docker dev tag run: | version="${{ env.version }}" - echo "dev_tag=${{ env.version }}${{ matrix.dataplane.image_suffix }}" >> $GITHUB_ENV + echo "dev_tag=${version%.*}-${{ matrix.dataplane.image_suffix }}" >> $GITHUB_ENV - name: Set image tarball run: | @@ -356,8 +360,8 @@ jobs: with: name: ${{env.image_tarball}} - run: docker load --input ${{env.image_tarball}} - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 - - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 + - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 with: # pinning this to 1.20.5 because this issue in go-testcontainers occurs # in 1.20.6 with the error "http: invalid Host header, host port waiting failed" diff --git a/.github/workflows/changelog-checker.yml b/.github/workflows/changelog-checker.yml index 1bf13b9d..c1af6371 100644 --- a/.github/workflows/changelog-checker.yml +++ b/.github/workflows/changelog-checker.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 # by default the checkout action doesn't checkout all branches diff --git a/.github/workflows/consul-dataplane-checks.yaml b/.github/workflows/consul-dataplane-checks.yaml index 087a2b98..4452891a 100644 --- a/.github/workflows/consul-dataplane-checks.yaml +++ b/.github/workflows/consul-dataplane-checks.yaml @@ -1,5 +1,4 @@ name: consul-dataplane-checks - on: push: branches: @@ -14,7 +13,7 @@ jobs: outputs: go-version: ${{ steps.get-go-version.outputs.go-version }} steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: Determine Go version id: get-go-version # We use .go-version as our source of truth for current Go @@ -28,7 +27,7 @@ jobs: - get-go-version runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 with: go-version: ${{ needs.get-go-version.outputs.go-version }} @@ -39,7 +38,7 @@ jobs: - get-go-version runs-on: ubuntu-latest steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 with: # pinning this to 1.20.5 because this issue in go-testcontainers occurs @@ -61,6 +60,6 @@ jobs: - uses: actions/setup-go@fac708d6674e30b6ba41289acaab6d4b75aa0753 # v4.0.1 with: go-version: ${{ needs.get-go-version.outputs.go-version }} - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - name: golangci-lint - uses: golangci/golangci-lint-action@639cd343e1d3b897ff35927a75193d57cfcba299 # v3.6.0 + uses: golangci/golangci-lint-action@08e2f20817b15149a52b5b3ebe7de50aff2ba8c5 # v3.4.0 diff --git a/.github/workflows/jira-issues.yaml b/.github/workflows/jira-issues.yaml index d9f7e194..9bba702c 100644 --- a/.github/workflows/jira-issues.yaml +++ b/.github/workflows/jira-issues.yaml @@ -13,7 +13,7 @@ jobs: name: Jira Community Issue sync steps: - name: Login - uses: atlassian/gajira-login@ca13f8850ea309cf44a6e4e0c49d9aa48ac3ca4c # v3 + uses: atlassian/gajira-login@45fd029b9f1d6d8926c6f04175aa80c0e42c9026 # v3.0.1 env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} @@ -70,14 +70,14 @@ jobs: - name: Close ticket if: ( github.event.action == 'closed' || github.event.action == 'deleted' ) && steps.search.outputs.issue - uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 + uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 with: issue: ${{ steps.search.outputs.issue }} transition: "Closed" - name: Reopen ticket if: github.event.action == 'reopened' && steps.search.outputs.issue - uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 + uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 with: issue: ${{ steps.search.outputs.issue }} transition: "To Do" diff --git a/.github/workflows/jira-pr.yaml b/.github/workflows/jira-pr.yaml index 7e593394..eb559078 100644 --- a/.github/workflows/jira-pr.yaml +++ b/.github/workflows/jira-pr.yaml @@ -11,7 +11,7 @@ jobs: name: Jira sync steps: - name: Login - uses: atlassian/gajira-login@ca13f8850ea309cf44a6e4e0c49d9aa48ac3ca4c # v3 + uses: atlassian/gajira-login@45fd029b9f1d6d8926c6f04175aa80c0e42c9026 # v3.0.1 env: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} @@ -37,7 +37,7 @@ jobs: id: is-team-member run: | TEAM=consul - ROLE="$(gh api orgs/hashicorp/teams/${TEAM}/memberships/${{ github.actor }} | jq -r '.role | select(.!=null)')" + ROLE="$(hub api orgs/hashicorp/teams/${TEAM}/memberships/${{ github.actor }} | jq -r '.role | select(.!=null)')" if [[ -n ${ROLE} ]]; then echo "Actor ${{ github.actor }} is a ${TEAM} team member" echo "MESSAGE=true" >> $GITHUB_OUTPUT @@ -84,14 +84,14 @@ jobs: - name: Close ticket if: ( github.event.action == 'closed' || github.event.action == 'deleted' ) && steps.search.outputs.issue - uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 + uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 with: issue: ${{ steps.search.outputs.issue }} transition: "Closed" - name: Reopen ticket if: github.event.action == 'reopened' && steps.search.outputs.issue - uses: atlassian/gajira-transition@4749176faf14633954d72af7a44d7f2af01cc92b # v3 + uses: atlassian/gajira-transition@38fc9cd61b03d6a53dd35fcccda172fe04b36de3 # v3.0.1 with: issue: ${{ steps.search.outputs.issue }} transition: "To Do" diff --git a/.golangci.yml b/.golangci.yml deleted file mode 100644 index 78d6e7df..00000000 --- a/.golangci.yml +++ /dev/null @@ -1,6 +0,0 @@ -issues: - exclude-rules: - # Allow usage of deprecated values. - - linters: [ staticcheck ] - text: 'SA1019:' - path: "(pkg/consuldp/bootstrap.go)" \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e153204..26f63c20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,40 @@ +## 1.2.2 (September 5, 2023) + +SECURITY: + +* Update to Go 1.20.7 and Envoy 1.26.4 within the Dockerfile. [[GH-235](https://github.com/hashicorp/consul-dataplane/pull/235)] + +BUG FIXES: + +* Fix a bug where container user was unable to bind to privileged ports (< 1024). The consul-dataplane container now requires the NET_BIND_SERVICE capability. [[GH-238](https://github.com/hashicorp/consul-dataplane/pull/238)] + +## 1.2.1 (August 9, 2023) + +SECURITY: + +* Upgrade to use Go 1.20.7 and `x/net/http` 0.12.0. + This resolves [CVE-2023-29406](https://github.com/advisories/GHSA-f8f7-69v5-w4vx)(`net/http`). [[GH-219](https://github.com/hashicorp/consul-dataplane/pull/219)] +* 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-227](https://github.com/hashicorp/consul-dataplane/pull/227)] + +FEATURES: + +* Add -shutdown-drain-listeners, -shutdown-grace-period, -graceful-shutdown-path and -graceful-port flags to configure proxy lifecycle management settings for the Envoy container. [[GH-100](https://github.com/hashicorp/consul-dataplane/pull/100)] +* Add HTTP server with configurable port and endpoint path for initiating graceful shutdown. [[GH-115](https://github.com/hashicorp/consul-dataplane/pull/115)] +* Catch SIGTERM and SIGINT to initate graceful shutdown in accordance with proxy lifecycle management configuration. [[GH-130](https://github.com/hashicorp/consul-dataplane/pull/130)] + +IMPROVEMENTS: + +* connect: Add capture group labels from Envoy cluster FQDNs to Envoy exported metric labels [[GH-184](https://github.com/hashicorp/consul-dataplane/pull/184)] + +BUG FIXES: + +* Add support for envoy-extra-args. Fixes [Envoy extra-args annotation crashing consul-dataplane container](https://github.com/hashicorp/consul-k8s/issues/1846). [[GH-133](https://github.com/hashicorp/consul-dataplane/pull/133)] +* Fix a bug where exiting envoy would inadvertently throw an error [[GH-175](https://github.com/hashicorp/consul-dataplane/pull/175)] +* Fix a bug with Envoy potentially starting with incomplete configuration by not waiting enough for initial xDS configuration. [[GH-140](https://github.com/hashicorp/consul-dataplane/pull/140)] + + ## 1.2.0 (June 28, 2023) SECURITY: diff --git a/Makefile b/Makefile index a9b86d3d..e723b1e5 100644 --- a/Makefile +++ b/Makefile @@ -8,8 +8,8 @@ GOBIN ?= $(GOPATH)/bin # Get local ARCH; on Intel Mac, 'uname -m' returns x86_64 which we turn into amd64. # Not using 'go env GOOS/GOARCH' here so 'make docker' will work without local Go install. -ARCH ?= $(shell A=$$(uname -m); [ $$A = x86_64 ] && A=amd64; echo $$A) -OS ?= $(shell uname | tr [[:upper:]] [[:lower:]]) +ARCH = $(shell A=$$(uname -m); [ $$A = x86_64 ] && A=amd64; echo $$A) +OS = $(shell uname | tr [[:upper:]] [[:lower:]]) PLATFORM = $(OS)/$(ARCH) DIST = dist/$(PLATFORM) BIN = $(DIST)/$(BIN_NAME) @@ -89,7 +89,6 @@ copy-bootstrap-config: sed '/github.com\/hashicorp\/consul\/api/d' | \ sed 's/api.IntentionDefaultNamespace/"default"/g' | \ sed '1s:^:// Code generated by make copy-bootstrap-config. DO NOT EDIT.\n:' | \ - sed '/"initial_metadata": \[/,/\]/d' | \ gofmt \ > $(BOOTSTRAP_PACKAGE_DIR)/$$file; \ done @@ -111,7 +110,7 @@ else $(error Cannot generate changelog without LAST_RELEASE_GIT_TAG) endif -INTEGRATION_TESTS_SERVER_IMAGE ?= hashicorppreview/consul:1.15-dev +INTEGRATION_TESTS_SERVER_IMAGE ?= hashicorppreview/consul:1.14-dev INTEGRATION_TESTS_DATAPLANE_IMAGE ?= $(PRODUCT_NAME)/release-default:$(VERSION) .PHONY: expand-integration-tests-output-dir diff --git a/cmd/consul-dataplane/config.go b/cmd/consul-dataplane/config.go deleted file mode 100644 index 1823ddd4..00000000 --- a/cmd/consul-dataplane/config.go +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package main - -import ( - "encoding/json" - "os" - "strings" - - "dario.cat/mergo" - - "github.com/hashicorp/consul-dataplane/pkg/consuldp" -) - -type FlagOpts struct { - dataplaneConfig DataplaneConfigFlags - - printVersion bool - configFile string -} - -type DataplaneConfigFlags struct { - Consul ConsulFlags `json:"consul,omitempty"` - Service ServiceFlags `json:"service,omitempty"` - Proxy ProxyFlags `json:"proxy,omitempty"` - Logging LogFlags `json:"logging,omitempty"` - XDSServer XDSServerFlags `json:"xdsServer,omitempty"` - DNSServer DNSServerFlags `json:"dnsServer,omitempty"` - Telemetry TelemetryFlags `json:"telemetry,omitempty"` - Envoy EnvoyFlags `json:"envoy,omitempty"` -} - -type ConsulFlags struct { - Addresses *string `json:"addresses,omitempty"` - GRPCPort *int `json:"grpcPort,omitempty"` - ServerWatchDisabled *bool `json:"serverWatchDisabled,omitempty"` - - TLS TLSFlags `json:"tls,omitempty"` - Credentials CredentialsFlags `json:"credentials,omitempty"` -} - -type TLSFlags struct { - Disabled *bool `json:"disabled,omitempty"` - CACertsPath *string `json:"caCertsPath,omitempty"` - ServerName *string `json:"serverName,omitempty"` - CertFile *string `json:"certFile,omitempty"` - KeyFile *string `json:"keyFile,omitempty"` - InsecureSkipVerify *bool `json:"insecureSkipVerify,omitempty"` -} - -type CredentialsFlags struct { - Type *string `json:"type,omitempty"` - Static StaticCredentialsFlags `json:"static,omitempty"` - Login LoginCredentialsFlags `json:"login,omitempty"` -} - -type StaticCredentialsFlags struct { - Token *string `json:"token,omitempty"` -} - -type LoginCredentialsFlags struct { - AuthMethod *string `json:"authMethod,omitempty"` - Namespace *string `json:"namespace,omitempty"` - Partition *string `json:"partition,omitempty"` - Datacenter *string `json:"datacenter,omitempty"` - BearerToken *string `json:"bearerToken,omitempty"` - BearerTokenPath *string `json:"bearerTokenPath,omitempty"` - Meta map[string]string `json:"meta,omitempty"` -} - -type ServiceFlags struct { - NodeName *string `json:"nodeName,omitempty"` - NodeID *string `json:"nodeID,omitempty"` - ServiceID *string `json:"serviceID,omitempty"` - ServiceIDPath *string `json:"serviceIDPath,omitempty"` - Namespace *string `json:"namespace,omitempty"` - Partition *string `json:"partition,omitempty"` -} - -func (pf ProxyFlags) IsEmpty() bool { - return pf.NodeName == nil && - pf.NodeID == nil && - pf.ID == nil && - pf.IDPath == nil && - pf.Namespace == nil && - pf.Partition == nil -} - -type ProxyFlags struct { - NodeName *string `json:"nodeName,omitempty"` - NodeID *string `json:"nodeID,omitempty"` - ID *string `json:"id,omitempty"` - IDPath *string `json:"idPath,omitempty"` - Namespace *string `json:"namespace,omitempty"` - Partition *string `json:"partition,omitempty"` -} - -type XDSServerFlags struct { - BindAddr *string `json:"bindAddress,omitempty"` - BindPort *int `json:"bindPort,omitempty"` -} - -type DNSServerFlags struct { - BindAddr *string `json:"bindAddress,omitempty"` - BindPort *int `json:"bindPort,omitempty"` -} - -type LogFlags struct { - Name string - LogLevel *string `json:"logLevel,omitempty"` - LogJSON *bool `json:"logJSON,omitempty"` -} - -type TelemetryFlags struct { - UseCentralConfig *bool `json:"useCentralConfig"` - Prometheus PrometheusTelemetryFlags `json:"prometheus,omitempty"` -} - -type PrometheusTelemetryFlags struct { - RetentionTime *Duration `json:"retentionTime,omitempty"` - CACertsPath *string `json:"caCertsPath,omitempty"` - KeyFile *string `json:"keyFile,omitempty"` - CertFile *string `json:"certFile,omitempty"` - ServiceMetricsURL *string `json:"serviceMetricsURL,omitempty"` - ScrapePath *string `json:"scrapePath,omitempty"` - MergePort *int `json:"mergePort,omitempty"` -} - -type EnvoyFlags struct { - AdminBindAddr *string `json:"adminBindAddress,omitempty"` - AdminBindPort *int `json:"adminBindPort,omitempty"` - ReadyBindAddr *string `json:"readyBindAddress,omitempty"` - ReadyBindPort *int `json:"readyBindPort,omitempty"` - Concurrency *int `json:"concurrency,omitempty"` - DrainTimeSeconds *int `json:"drainTimeSeconds,omitempty"` - DrainStrategy *string `json:"drainStrategy,omitempty"` - - ShutdownDrainListenersEnabled *bool `json:"shutdownDrainListenersEnabled,omitempty"` - ShutdownGracePeriodSeconds *int `json:"shutdownGracePeriodSeconds,omitempty"` - GracefulShutdownPath *string `json:"gracefulShutdownPath,omitempty"` - GracefulPort *int `json:"gracefulPort,omitempty"` - DumpEnvoyConfigOnExitEnabled *bool `json:"dumpEnvoyConfigOnExitEnabled,omitempty"` - //Time in seconds to wait for dataplane to be ready. - StartupGracePeriodSeconds *int `json:"startupGracePeriodSeconds,omitempty"` - //Endpoint for graceful startup function. - GracefulStartupPath *string `json:"gracefulStartupPath,omitempty"` -} - -const ( - DefaultLogName = "consul-dataplane" -) - -// buildDataplaneConfig builds the necessary config needed for the -// dataplane to start. We begin with the default version of the dataplane -// config(with the default values) followed by merging the file based -// config generated from the `-config-file` input into it. -// Since values given via CLI flags take the most precedence, we finally -// merge the config generated from the flags into the previously -// generated/merged config -func (f *FlagOpts) buildDataplaneConfig(extraArgs []string) (*consuldp.Config, error) { - consulDPDefaultFlags, err := buildDefaultConsulDPFlags() - if err != nil { - return nil, err - } - - if f.configFile != "" { - consulDPFileBasedFlags, err := f.buildConfigFromFile() - if err != nil { - return nil, err - } - - consulDPDefaultFlags, err = mergeConfigs(consulDPDefaultFlags, consulDPFileBasedFlags) - if err != nil { - return nil, err - } - } - - consulDPDefaultFlags, err = mergeConfigs(consulDPDefaultFlags, f.dataplaneConfig) - if err != nil { - return nil, err - } - - consuldpRuntimeConfig, err := constructRuntimeConfig(consulDPDefaultFlags, extraArgs) - if err != nil { - return nil, err - } - - return consuldpRuntimeConfig, nil -} - -// Constructs a config based on the values present in the config json file -func (f *FlagOpts) buildConfigFromFile() (DataplaneConfigFlags, error) { - var cfg DataplaneConfigFlags - data, err := os.ReadFile(f.configFile) - if err != nil { - return DataplaneConfigFlags{}, err - } - - err = json.Unmarshal(data, &cfg) - if err != nil { - return DataplaneConfigFlags{}, err - } - - return cfg, nil -} - -// Constructs a config with the default values -func buildDefaultConsulDPFlags() (DataplaneConfigFlags, error) { - data := ` - { - "consul": { - "grpcPort": 8502, - "serverWatchDisabled": false, - "tls": { - "disabled": false, - "insecureSkipVerify": false - } - }, - "logging": { - "name": "consul-dataplane", - "logJSON": false, - "logLevel": "info" - }, - "telemetry": { - "useCentralConfig": true, - "prometheus": { - "retentionTime": "60s", - "scrapePath": "/metrics", - "mergePort": 20100 - } - }, - "envoy": { - "adminBindAddress": "127.0.0.1", - "adminBindPort": 19000, - "readyBindPort": 0, - "concurrency": 2, - "drainTimeSeconds": 30, - "drainStrategy": "immediate", - "shutdownDrainListenersEnabled": false, - "shutdownGracePeriodSeconds": 0, - "gracefulShutdownPath": "/graceful_shutdown", - "gracefulPort": 20300, - "dumpEnvoyConfigOnExitEnabled": false, - "gracefulStartupPath": "/graceful_startup", - "startupGracePeriodSeconds": 0 - }, - "xdsServer": { - "bindAddress": "127.0.0.1", - "bindPort": 0 - }, - "dnsServer": { - "bindAddress": "127.0.0.1", - "bindPort": -1 - } - }` - - var defaultCfgFlags DataplaneConfigFlags - err := json.Unmarshal([]byte(data), &defaultCfgFlags) - if err != nil { - return DataplaneConfigFlags{}, err - } - - return defaultCfgFlags, nil -} - -// constructRuntimeConfig constructs the final config needed for dataplane to start -// itself after substituting all the user provided inputs -func constructRuntimeConfig(cfg DataplaneConfigFlags, extraArgs []string) (*consuldp.Config, error) { - // Handle deprecated service flags. - var proxyCfg consuldp.ProxyConfig - if !cfg.Proxy.IsEmpty() { - proxyCfg = consuldp.ProxyConfig{ - NodeName: stringVal(cfg.Proxy.NodeName), - NodeID: stringVal(cfg.Proxy.NodeID), - ProxyID: stringVal(cfg.Proxy.ID), - Namespace: stringVal(cfg.Proxy.Namespace), - Partition: stringVal(cfg.Proxy.Partition), - } - } else { - proxyCfg = consuldp.ProxyConfig{ - NodeName: stringVal(cfg.Service.NodeName), - NodeID: stringVal(cfg.Service.NodeID), - ProxyID: stringVal(cfg.Service.ServiceID), - Namespace: stringVal(cfg.Service.Namespace), - Partition: stringVal(cfg.Service.Partition), - } - } - - return &consuldp.Config{ - Consul: &consuldp.ConsulConfig{ - Addresses: stringVal(cfg.Consul.Addresses), - GRPCPort: intVal(cfg.Consul.GRPCPort), - ServerWatchDisabled: boolVal(cfg.Consul.ServerWatchDisabled), - Credentials: &consuldp.CredentialsConfig{ - Type: consuldp.CredentialsType(stringVal(cfg.Consul.Credentials.Type)), - Static: consuldp.StaticCredentialsConfig{ - Token: stringVal(cfg.Consul.Credentials.Static.Token), - }, - Login: consuldp.LoginCredentialsConfig{ - AuthMethod: stringVal(cfg.Consul.Credentials.Login.AuthMethod), - Namespace: stringVal(cfg.Consul.Credentials.Login.Namespace), - Partition: stringVal(cfg.Consul.Credentials.Login.Partition), - Datacenter: stringVal(cfg.Consul.Credentials.Login.Datacenter), - BearerToken: stringVal(cfg.Consul.Credentials.Login.BearerToken), - BearerTokenPath: stringVal(cfg.Consul.Credentials.Login.BearerTokenPath), - Meta: cfg.Consul.Credentials.Login.Meta, - }, - }, - TLS: &consuldp.TLSConfig{ - Disabled: boolVal(cfg.Consul.TLS.Disabled), - CACertsPath: stringVal(cfg.Consul.TLS.CACertsPath), - CertFile: stringVal(cfg.Consul.TLS.CertFile), - KeyFile: stringVal(cfg.Consul.TLS.KeyFile), - ServerName: stringVal(cfg.Consul.TLS.ServerName), - InsecureSkipVerify: boolVal(cfg.Consul.TLS.InsecureSkipVerify), - }, - }, - Proxy: &proxyCfg, - Logging: &consuldp.LoggingConfig{ - Name: DefaultLogName, - LogJSON: boolVal(cfg.Logging.LogJSON), - LogLevel: strings.ToUpper(stringVal(cfg.Logging.LogLevel)), - }, - Envoy: &consuldp.EnvoyConfig{ - AdminBindAddress: stringVal(cfg.Envoy.AdminBindAddr), - AdminBindPort: intVal(cfg.Envoy.AdminBindPort), - ReadyBindAddress: stringVal(cfg.Envoy.ReadyBindAddr), - ReadyBindPort: intVal(cfg.Envoy.ReadyBindPort), - EnvoyConcurrency: intVal(cfg.Envoy.Concurrency), - EnvoyDrainTimeSeconds: intVal(cfg.Envoy.DrainTimeSeconds), - EnvoyDrainStrategy: stringVal(cfg.Envoy.DrainStrategy), - ShutdownDrainListenersEnabled: boolVal(cfg.Envoy.ShutdownDrainListenersEnabled), - ShutdownGracePeriodSeconds: intVal(cfg.Envoy.ShutdownGracePeriodSeconds), - DumpEnvoyConfigOnExitEnabled: boolVal(cfg.Envoy.DumpEnvoyConfigOnExitEnabled), - GracefulShutdownPath: stringVal(cfg.Envoy.GracefulShutdownPath), - GracefulPort: intVal(cfg.Envoy.GracefulPort), - StartupGracePeriodSeconds: intVal(cfg.Envoy.StartupGracePeriodSeconds), - GracefulStartupPath: stringVal(cfg.Envoy.GracefulStartupPath), - ExtraArgs: extraArgs, - }, - Telemetry: &consuldp.TelemetryConfig{ - UseCentralConfig: boolVal(cfg.Telemetry.UseCentralConfig), - Prometheus: consuldp.PrometheusTelemetryConfig{ - RetentionTime: durationVal(cfg.Telemetry.Prometheus.RetentionTime), - CACertsPath: stringVal(cfg.Telemetry.Prometheus.CACertsPath), - CertFile: stringVal(cfg.Telemetry.Prometheus.CertFile), - KeyFile: stringVal(cfg.Telemetry.Prometheus.KeyFile), - ServiceMetricsURL: stringVal(cfg.Telemetry.Prometheus.ServiceMetricsURL), - ScrapePath: stringVal(cfg.Telemetry.Prometheus.ScrapePath), - MergePort: intVal(cfg.Telemetry.Prometheus.MergePort), - }, - }, - XDSServer: &consuldp.XDSServer{ - BindAddress: stringVal(cfg.XDSServer.BindAddr), - BindPort: intVal(cfg.XDSServer.BindPort), - }, - DNSServer: &consuldp.DNSServerConfig{ - BindAddr: stringVal(cfg.DNSServer.BindAddr), - Port: intVal(cfg.DNSServer.BindPort), - }, - }, nil -} - -func mergeConfigs(c1, c2 DataplaneConfigFlags) (DataplaneConfigFlags, error) { - err := mergo.Merge(&c1, c2, mergo.WithOverride, mergo.WithoutDereference) - if err != nil { - return DataplaneConfigFlags{}, err - } - - return c1, nil -} diff --git a/cmd/consul-dataplane/config_test.go b/cmd/consul-dataplane/config_test.go deleted file mode 100644 index 25859065..00000000 --- a/cmd/consul-dataplane/config_test.go +++ /dev/null @@ -1,1017 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package main - -import ( - "encoding/json" - "fmt" - "math/rand" - "os" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/hashicorp/consul-dataplane/pkg/consuldp" -) - -func TestConfigGeneration(t *testing.T) { - type testCase struct { - desc string - flagOpts func() (*FlagOpts, error) - writeConfigFile func(t *testing.T) error - makeExpectedCfg func(f *FlagOpts) *consuldp.Config - wantErr bool - } - - testCases := []testCase{ - { - desc: "able to generate config properly when the config file input is empty", - flagOpts: func() (*FlagOpts, error) { - return generateFlagOptsWithServiceFlags() - }, - makeExpectedCfg: func(flagOpts *FlagOpts) *consuldp.Config { - return &consuldp.Config{ - Consul: &consuldp.ConsulConfig{ - Addresses: stringVal(flagOpts.dataplaneConfig.Consul.Addresses), - GRPCPort: intVal(flagOpts.dataplaneConfig.Consul.GRPCPort), - ServerWatchDisabled: true, - Credentials: &consuldp.CredentialsConfig{ - Type: "static", - Static: consuldp.StaticCredentialsConfig{ - Token: "test-token-123", - }, - Login: consuldp.LoginCredentialsConfig{ - AuthMethod: "test-iam-auth", - BearerToken: "bearer-login", - }, - }, - TLS: &consuldp.TLSConfig{ - Disabled: false, - CACertsPath: "/consul/", - CertFile: "ca-cert.pem", - KeyFile: "key.pem", - ServerName: "tls-server-name", - InsecureSkipVerify: true, - }, - }, - Proxy: &consuldp.ProxyConfig{ - NodeName: "test-node-dc1", - NodeID: "dc1.node.id", - Namespace: "default", - ProxyID: "node1.service1", - Partition: "default", - }, - Logging: &consuldp.LoggingConfig{ - Name: DefaultLogName, - LogJSON: true, - LogLevel: "WARN", - }, - DNSServer: &consuldp.DNSServerConfig{ - BindAddr: "127.0.0.1", - Port: 8604, - }, - XDSServer: &consuldp.XDSServer{ - BindAddress: "127.0.1.0", - BindPort: 0, - }, - Envoy: &consuldp.EnvoyConfig{ - AdminBindAddress: "127.0.1.0", - AdminBindPort: 18000, - ReadyBindAddress: "127.0.1.0", - ReadyBindPort: 18003, - EnvoyConcurrency: 4, - EnvoyDrainStrategy: "test-strategy", - ShutdownDrainListenersEnabled: true, - GracefulShutdownPath: "/graceful_shutdown", - EnvoyDrainTimeSeconds: 30, - GracefulPort: 20300, - GracefulStartupPath: "/graceful_startup", - }, - Telemetry: &consuldp.TelemetryConfig{ - UseCentralConfig: true, - Prometheus: consuldp.PrometheusTelemetryConfig{ - RetentionTime: 10 * time.Second, - ScrapePath: "/metrics", - MergePort: 12000, - CACertsPath: "/consul/", - CertFile: "prom-ca-cert.pem", - KeyFile: "prom-key.pem", - }, - }, - } - }, - wantErr: false, - }, - { - desc: "able to generate config properly with proxy flags when the config file input is empty", - flagOpts: func() (*FlagOpts, error) { - return generateFlagOptsWithProxyFlags() - }, - makeExpectedCfg: func(flagOpts *FlagOpts) *consuldp.Config { - return &consuldp.Config{ - Consul: &consuldp.ConsulConfig{ - Addresses: stringVal(flagOpts.dataplaneConfig.Consul.Addresses), - GRPCPort: intVal(flagOpts.dataplaneConfig.Consul.GRPCPort), - ServerWatchDisabled: true, - Credentials: &consuldp.CredentialsConfig{ - Type: "static", - Static: consuldp.StaticCredentialsConfig{ - Token: "test-token-123", - }, - Login: consuldp.LoginCredentialsConfig{ - AuthMethod: "test-iam-auth", - BearerToken: "bearer-login", - }, - }, - TLS: &consuldp.TLSConfig{ - Disabled: false, - CACertsPath: "/consul/", - CertFile: "ca-cert.pem", - KeyFile: "key.pem", - ServerName: "tls-server-name", - InsecureSkipVerify: true, - }, - }, - Proxy: &consuldp.ProxyConfig{ - NodeName: "test-node-dc1", - NodeID: "dc1.node.id", - Namespace: "default", - ProxyID: "node1.service1", - Partition: "default", - }, - Logging: &consuldp.LoggingConfig{ - Name: DefaultLogName, - LogJSON: true, - LogLevel: "WARN", - }, - DNSServer: &consuldp.DNSServerConfig{ - BindAddr: "127.0.0.1", - Port: 8604, - }, - XDSServer: &consuldp.XDSServer{ - BindAddress: "127.0.1.0", - BindPort: 0, - }, - Envoy: &consuldp.EnvoyConfig{ - AdminBindAddress: "127.0.1.0", - AdminBindPort: 18000, - ReadyBindAddress: "127.0.1.0", - ReadyBindPort: 18003, - EnvoyConcurrency: 4, - EnvoyDrainStrategy: "test-strategy", - ShutdownDrainListenersEnabled: true, - GracefulStartupPath: "/graceful_startup", - GracefulShutdownPath: "/graceful_shutdown", - EnvoyDrainTimeSeconds: 30, - GracefulPort: 20300, - }, - Telemetry: &consuldp.TelemetryConfig{ - UseCentralConfig: true, - Prometheus: consuldp.PrometheusTelemetryConfig{ - RetentionTime: 10 * time.Second, - ScrapePath: "/metrics", - MergePort: 12000, - CACertsPath: "/consul/", - CertFile: "prom-ca-cert.pem", - KeyFile: "prom-key.pem", - }, - }, - } - }, - wantErr: false, - }, - { - desc: "able to override all the config fields with CLI flags when using service flags", - flagOpts: func() (*FlagOpts, error) { - opts, err := generateFlagOptsWithServiceFlags() - if err != nil { - return nil, err - } - opts.dataplaneConfig.Consul.Credentials.Login.BearerTokenPath = strReference("/consul/bearertokenpath/") - opts.dataplaneConfig.Consul.Credentials.Login.Datacenter = strReference("dc100") - opts.dataplaneConfig.Consul.Credentials.Login.Meta = map[string]string{ - "key-1": "value-1", - "key-2": "value-2", - } - opts.dataplaneConfig.Consul.Credentials.Login.Namespace = strReference("default") - opts.dataplaneConfig.Consul.Credentials.Login.Partition = strReference("default") - - opts.dataplaneConfig.Logging.LogJSON = boolReference(false) - opts.dataplaneConfig.DNSServer.BindAddr = strReference("127.0.0.2") - opts.dataplaneConfig.XDSServer.BindPort = intReference(6060) - opts.dataplaneConfig.Envoy.DumpEnvoyConfigOnExitEnabled = boolReference(true) - return opts, nil - }, - makeExpectedCfg: func(flagOpts *FlagOpts) *consuldp.Config { - return &consuldp.Config{ - Consul: &consuldp.ConsulConfig{ - Addresses: stringVal(flagOpts.dataplaneConfig.Consul.Addresses), - GRPCPort: intVal(flagOpts.dataplaneConfig.Consul.GRPCPort), - ServerWatchDisabled: true, - Credentials: &consuldp.CredentialsConfig{ - Type: "static", - Static: consuldp.StaticCredentialsConfig{ - Token: "test-token-123", - }, - Login: consuldp.LoginCredentialsConfig{ - Meta: map[string]string{ - "key-1": "value-1", - "key-2": "value-2", - }, - AuthMethod: "test-iam-auth", - BearerToken: "bearer-login", - BearerTokenPath: "/consul/bearertokenpath/", - Namespace: "default", - Partition: "default", - Datacenter: "dc100", - }, - }, - TLS: &consuldp.TLSConfig{ - Disabled: false, - CACertsPath: "/consul/", - CertFile: "ca-cert.pem", - KeyFile: "key.pem", - ServerName: "tls-server-name", - InsecureSkipVerify: true, - }, - }, - Proxy: &consuldp.ProxyConfig{ - NodeName: "test-node-dc1", - NodeID: "dc1.node.id", - Namespace: "default", - ProxyID: "node1.service1", - Partition: "default", - }, - Logging: &consuldp.LoggingConfig{ - Name: DefaultLogName, - LogJSON: false, - LogLevel: "WARN", - }, - DNSServer: &consuldp.DNSServerConfig{ - BindAddr: "127.0.0.2", - Port: 8604, - }, - XDSServer: &consuldp.XDSServer{ - BindAddress: "127.0.1.0", - BindPort: 6060, - }, - Envoy: &consuldp.EnvoyConfig{ - AdminBindAddress: "127.0.1.0", - AdminBindPort: 18000, - ReadyBindAddress: "127.0.1.0", - ReadyBindPort: 18003, - EnvoyConcurrency: 4, - EnvoyDrainStrategy: "test-strategy", - ShutdownDrainListenersEnabled: true, - GracefulStartupPath: "/graceful_startup", - GracefulShutdownPath: "/graceful_shutdown", - EnvoyDrainTimeSeconds: 30, - GracefulPort: 20300, - DumpEnvoyConfigOnExitEnabled: true, - }, - Telemetry: &consuldp.TelemetryConfig{ - UseCentralConfig: true, - Prometheus: consuldp.PrometheusTelemetryConfig{ - RetentionTime: 10 * time.Second, - ScrapePath: "/metrics", - MergePort: 12000, - CACertsPath: "/consul/", - CertFile: "prom-ca-cert.pem", - KeyFile: "prom-key.pem", - }, - }, - } - }, - wantErr: false, - }, - { - desc: "able to override all the config fields with CLI flags when using proxy flags", - flagOpts: func() (*FlagOpts, error) { - opts, err := generateFlagOptsWithProxyFlags() - if err != nil { - return nil, err - } - opts.dataplaneConfig.Consul.Credentials.Login.BearerTokenPath = strReference("/consul/bearertokenpath/") - opts.dataplaneConfig.Consul.Credentials.Login.Datacenter = strReference("dc100") - opts.dataplaneConfig.Consul.Credentials.Login.Meta = map[string]string{ - "key-1": "value-1", - "key-2": "value-2", - } - opts.dataplaneConfig.Consul.Credentials.Login.Namespace = strReference("default") - opts.dataplaneConfig.Consul.Credentials.Login.Partition = strReference("default") - - opts.dataplaneConfig.Logging.LogJSON = boolReference(false) - opts.dataplaneConfig.DNSServer.BindAddr = strReference("127.0.0.2") - opts.dataplaneConfig.XDSServer.BindPort = intReference(6060) - opts.dataplaneConfig.Envoy.DumpEnvoyConfigOnExitEnabled = boolReference(true) - return opts, nil - }, - makeExpectedCfg: func(flagOpts *FlagOpts) *consuldp.Config { - return &consuldp.Config{ - Consul: &consuldp.ConsulConfig{ - Addresses: stringVal(flagOpts.dataplaneConfig.Consul.Addresses), - GRPCPort: intVal(flagOpts.dataplaneConfig.Consul.GRPCPort), - ServerWatchDisabled: true, - Credentials: &consuldp.CredentialsConfig{ - Type: "static", - Static: consuldp.StaticCredentialsConfig{ - Token: "test-token-123", - }, - Login: consuldp.LoginCredentialsConfig{ - Meta: map[string]string{ - "key-1": "value-1", - "key-2": "value-2", - }, - AuthMethod: "test-iam-auth", - BearerToken: "bearer-login", - BearerTokenPath: "/consul/bearertokenpath/", - Namespace: "default", - Partition: "default", - Datacenter: "dc100", - }, - }, - TLS: &consuldp.TLSConfig{ - Disabled: false, - CACertsPath: "/consul/", - CertFile: "ca-cert.pem", - KeyFile: "key.pem", - ServerName: "tls-server-name", - InsecureSkipVerify: true, - }, - }, - Proxy: &consuldp.ProxyConfig{ - NodeName: "test-node-dc1", - NodeID: "dc1.node.id", - Namespace: "default", - ProxyID: "node1.service1", - Partition: "default", - }, - Logging: &consuldp.LoggingConfig{ - Name: DefaultLogName, - LogJSON: false, - LogLevel: "WARN", - }, - DNSServer: &consuldp.DNSServerConfig{ - BindAddr: "127.0.0.2", - Port: 8604, - }, - XDSServer: &consuldp.XDSServer{ - BindAddress: "127.0.1.0", - BindPort: 6060, - }, - Envoy: &consuldp.EnvoyConfig{ - AdminBindAddress: "127.0.1.0", - AdminBindPort: 18000, - ReadyBindAddress: "127.0.1.0", - ReadyBindPort: 18003, - EnvoyConcurrency: 4, - EnvoyDrainStrategy: "test-strategy", - ShutdownDrainListenersEnabled: true, - GracefulShutdownPath: "/graceful_shutdown", - EnvoyDrainTimeSeconds: 30, - GracefulPort: 20300, - DumpEnvoyConfigOnExitEnabled: true, - GracefulStartupPath: "/graceful_startup", - }, - Telemetry: &consuldp.TelemetryConfig{ - UseCentralConfig: true, - Prometheus: consuldp.PrometheusTelemetryConfig{ - RetentionTime: 10 * time.Second, - ScrapePath: "/metrics", - MergePort: 12000, - CACertsPath: "/consul/", - CertFile: "prom-ca-cert.pem", - KeyFile: "prom-key.pem", - }, - }, - } - }, - wantErr: false, - }, - { - desc: "prefer proxy flags over service flags when both are set", - flagOpts: func() (*FlagOpts, error) { - opts, err := generateFlagOptsWithServiceFlags() - if err != nil { - return nil, err - } - opts.dataplaneConfig.Proxy.ID = strReference("proxy-id") - opts.dataplaneConfig.Proxy.NodeName = strReference("proxy-node-name") - opts.dataplaneConfig.Proxy.NodeID = strReference("proxy-node-id") - opts.dataplaneConfig.Proxy.Namespace = strReference("foo") - opts.dataplaneConfig.Proxy.Partition = strReference("bar") - - opts.dataplaneConfig.XDSServer.BindPort = intReference(6060) - return opts, nil - }, - makeExpectedCfg: func(flagOpts *FlagOpts) *consuldp.Config { - return &consuldp.Config{ - Consul: &consuldp.ConsulConfig{ - Addresses: stringVal(flagOpts.dataplaneConfig.Consul.Addresses), - GRPCPort: intVal(flagOpts.dataplaneConfig.Consul.GRPCPort), - ServerWatchDisabled: true, - Credentials: &consuldp.CredentialsConfig{ - Type: "static", - Static: consuldp.StaticCredentialsConfig{ - Token: "test-token-123", - }, - Login: consuldp.LoginCredentialsConfig{ - AuthMethod: "test-iam-auth", - BearerToken: "bearer-login", - }, - }, - TLS: &consuldp.TLSConfig{ - Disabled: false, - CACertsPath: "/consul/", - CertFile: "ca-cert.pem", - KeyFile: "key.pem", - ServerName: "tls-server-name", - InsecureSkipVerify: true, - }, - }, - Proxy: &consuldp.ProxyConfig{ - NodeName: "proxy-node-name", - NodeID: "proxy-node-id", - Namespace: "foo", - ProxyID: "proxy-id", - Partition: "bar", - }, - Logging: &consuldp.LoggingConfig{ - Name: DefaultLogName, - LogJSON: true, - LogLevel: "WARN", - }, - DNSServer: &consuldp.DNSServerConfig{ - BindAddr: "127.0.0.1", - Port: 8604, - }, - XDSServer: &consuldp.XDSServer{ - BindAddress: "127.0.1.0", - BindPort: 6060, - }, - Envoy: &consuldp.EnvoyConfig{ - AdminBindAddress: "127.0.1.0", - AdminBindPort: 18000, - ReadyBindAddress: "127.0.1.0", - ReadyBindPort: 18003, - EnvoyConcurrency: 4, - EnvoyDrainStrategy: "test-strategy", - ShutdownDrainListenersEnabled: true, - GracefulShutdownPath: "/graceful_shutdown", - GracefulStartupPath: "/graceful_startup", - EnvoyDrainTimeSeconds: 30, - GracefulPort: 20300, - }, - Telemetry: &consuldp.TelemetryConfig{ - UseCentralConfig: true, - Prometheus: consuldp.PrometheusTelemetryConfig{ - RetentionTime: 10 * time.Second, - ScrapePath: "/metrics", - MergePort: 12000, - CACertsPath: "/consul/", - CertFile: "prom-ca-cert.pem", - KeyFile: "prom-key.pem", - }, - }, - } - }, - wantErr: false, - }, - { - desc: "able to generate config properly when config file is given without flag inputs", - flagOpts: func() (*FlagOpts, error) { - opts := &FlagOpts{} - opts.configFile = "test.json" - return opts, nil - }, - writeConfigFile: func(t *testing.T) error { - inputJson := `{ - "consul": { - "addresses": "consul_server.dc1", - "grpcPort": 8502, - "serverWatchDisabled": false - }, - "proxy": { - "nodeName": "test-node-1", - "id": "frontend-service-sidecar-proxy", - "namespace": "default", - "partition": "default" - }, - "envoy": { - "adminBindAddress": "127.0.0.1", - "adminBindPort": 19000 - }, - "logging": { - "logLevel": "info", - "logJSON": false - } - }` - - err := os.WriteFile("test.json", []byte(inputJson), 0600) - if err != nil { - return err - } - - t.Cleanup(func() { - _ = os.Remove("test.json") - }) - return nil - }, - makeExpectedCfg: func(flagOpts *FlagOpts) *consuldp.Config { - return &consuldp.Config{ - Consul: &consuldp.ConsulConfig{ - Addresses: "consul_server.dc1", - GRPCPort: 8502, - ServerWatchDisabled: false, - Credentials: &consuldp.CredentialsConfig{ - Static: consuldp.StaticCredentialsConfig{}, - Login: consuldp.LoginCredentialsConfig{}, - }, - TLS: &consuldp.TLSConfig{}, - }, - Proxy: &consuldp.ProxyConfig{ - NodeName: "test-node-1", - Namespace: "default", - ProxyID: "frontend-service-sidecar-proxy", - Partition: "default", - }, - Logging: &consuldp.LoggingConfig{ - Name: DefaultLogName, - LogJSON: false, - LogLevel: "INFO", - }, - DNSServer: &consuldp.DNSServerConfig{ - BindAddr: "127.0.0.1", - Port: -1, - }, - XDSServer: &consuldp.XDSServer{ - BindAddress: "127.0.0.1", - BindPort: 0, - }, - Envoy: &consuldp.EnvoyConfig{ - AdminBindAddress: "127.0.0.1", - AdminBindPort: 19000, - ReadyBindPort: 0, - EnvoyConcurrency: 2, - EnvoyDrainStrategy: "immediate", - ShutdownDrainListenersEnabled: false, - GracefulShutdownPath: "/graceful_shutdown", - EnvoyDrainTimeSeconds: 30, - GracefulPort: 20300, - DumpEnvoyConfigOnExitEnabled: false, - GracefulStartupPath: "/graceful_startup", - }, - Telemetry: &consuldp.TelemetryConfig{ - UseCentralConfig: true, - Prometheus: consuldp.PrometheusTelemetryConfig{ - RetentionTime: 60 * time.Second, - ScrapePath: "/metrics", - MergePort: 20100, - }, - }, - } - }, - wantErr: false, - }, - { - desc: "test whether CLI flag values override the file values with service flags", - flagOpts: func() (*FlagOpts, error) { - opts, err := generateFlagOptsWithServiceFlags() - if err != nil { - return nil, err - } - opts.configFile = "test.json" - - opts.dataplaneConfig.Logging.LogLevel = strReference("info") - opts.dataplaneConfig.Logging.LogJSON = boolReference(false) - opts.dataplaneConfig.Consul.Credentials.Login.Meta = map[string]string{ - "key1": "value1", - } - - return opts, nil - }, - writeConfigFile: func(t *testing.T) error { - inputJson := `{ - "consul": { - "addresses": "consul_server.dc1", - "grpcPort": 8502, - "serverWatchDisabled": false - }, - "service": { - "nodeName": "test-node-1", - "serviceId": "frontend-service-sidecar-proxy", - "namespace": "default", - "partition": "default" - }, - "envoy": { - "adminBindAddress": "127.0.0.1", - "adminBindPort": 19000 - }, - "logging": { - "logLevel": "warn", - "logJSON": true - } - }` - - err := os.WriteFile("test.json", []byte(inputJson), 0600) - if err != nil { - return err - } - - t.Cleanup(func() { - _ = os.Remove("test.json") - }) - return nil - }, - makeExpectedCfg: func(flagOpts *FlagOpts) *consuldp.Config { - return &consuldp.Config{ - Consul: &consuldp.ConsulConfig{ - Addresses: stringVal(flagOpts.dataplaneConfig.Consul.Addresses), - GRPCPort: intVal(flagOpts.dataplaneConfig.Consul.GRPCPort), - ServerWatchDisabled: true, - Credentials: &consuldp.CredentialsConfig{ - Type: "static", - Static: consuldp.StaticCredentialsConfig{ - Token: "test-token-123", - }, - Login: consuldp.LoginCredentialsConfig{ - AuthMethod: "test-iam-auth", - BearerToken: "bearer-login", - Meta: map[string]string{ - "key1": "value1", - }, - }, - }, - TLS: &consuldp.TLSConfig{ - Disabled: false, - CACertsPath: "/consul/", - CertFile: "ca-cert.pem", - KeyFile: "key.pem", - ServerName: "tls-server-name", - InsecureSkipVerify: true, - }, - }, - Proxy: &consuldp.ProxyConfig{ - NodeName: "test-node-dc1", - NodeID: "dc1.node.id", - Namespace: "default", - ProxyID: "node1.service1", - Partition: "default", - }, - Logging: &consuldp.LoggingConfig{ - Name: DefaultLogName, - LogJSON: false, - LogLevel: "INFO", - }, - DNSServer: &consuldp.DNSServerConfig{ - BindAddr: "127.0.0.1", - Port: 8604, - }, - XDSServer: &consuldp.XDSServer{ - BindAddress: "127.0.1.0", - BindPort: 0, - }, - Envoy: &consuldp.EnvoyConfig{ - AdminBindAddress: "127.0.1.0", - AdminBindPort: 18000, - ReadyBindAddress: "127.0.1.0", - ReadyBindPort: 18003, - EnvoyConcurrency: 4, - EnvoyDrainStrategy: "test-strategy", - ShutdownDrainListenersEnabled: true, - GracefulShutdownPath: "/graceful_shutdown", - EnvoyDrainTimeSeconds: 30, - GracefulPort: 20300, - DumpEnvoyConfigOnExitEnabled: false, - GracefulStartupPath: "/graceful_startup", - }, - Telemetry: &consuldp.TelemetryConfig{ - UseCentralConfig: true, - Prometheus: consuldp.PrometheusTelemetryConfig{ - RetentionTime: 10 * time.Second, - ScrapePath: "/metrics", - MergePort: 12000, - CACertsPath: "/consul/", - CertFile: "prom-ca-cert.pem", - KeyFile: "prom-key.pem", - }, - }, - } - }, - wantErr: false, - }, - { - desc: "test whether CLI flag values override the file values with proxy flags", - flagOpts: func() (*FlagOpts, error) { - opts, err := generateFlagOptsWithProxyFlags() - if err != nil { - return nil, err - } - opts.configFile = "test.json" - - opts.dataplaneConfig.Logging.LogLevel = strReference("info") - opts.dataplaneConfig.Logging.LogJSON = boolReference(false) - opts.dataplaneConfig.Consul.Credentials.Login.Meta = map[string]string{ - "key1": "value1", - } - - return opts, nil - }, - writeConfigFile: func(t *testing.T) error { - inputJson := `{ - "consul": { - "addresses": "consul_server.dc1", - "grpcPort": 8502, - "serverWatchDisabled": false - }, - "proxy": { - "nodeName": "test-node-1", - "proxyId": "frontend-service-sidecar-proxy", - "namespace": "default", - "partition": "default" - }, - "envoy": { - "adminBindAddress": "127.0.0.1", - "adminBindPort": 19000 - }, - "logging": { - "logLevel": "warn", - "logJSON": true - } - }` - - err := os.WriteFile("test.json", []byte(inputJson), 0600) - if err != nil { - return err - } - - t.Cleanup(func() { - _ = os.Remove("test.json") - }) - return nil - }, - makeExpectedCfg: func(flagOpts *FlagOpts) *consuldp.Config { - return &consuldp.Config{ - Consul: &consuldp.ConsulConfig{ - Addresses: stringVal(flagOpts.dataplaneConfig.Consul.Addresses), - GRPCPort: intVal(flagOpts.dataplaneConfig.Consul.GRPCPort), - ServerWatchDisabled: true, - Credentials: &consuldp.CredentialsConfig{ - Type: "static", - Static: consuldp.StaticCredentialsConfig{ - Token: "test-token-123", - }, - Login: consuldp.LoginCredentialsConfig{ - AuthMethod: "test-iam-auth", - BearerToken: "bearer-login", - Meta: map[string]string{ - "key1": "value1", - }, - }, - }, - TLS: &consuldp.TLSConfig{ - Disabled: false, - CACertsPath: "/consul/", - CertFile: "ca-cert.pem", - KeyFile: "key.pem", - ServerName: "tls-server-name", - InsecureSkipVerify: true, - }, - }, - Proxy: &consuldp.ProxyConfig{ - NodeName: "test-node-dc1", - NodeID: "dc1.node.id", - Namespace: "default", - ProxyID: "node1.service1", - Partition: "default", - }, - Logging: &consuldp.LoggingConfig{ - Name: DefaultLogName, - LogJSON: false, - LogLevel: "INFO", - }, - DNSServer: &consuldp.DNSServerConfig{ - BindAddr: "127.0.0.1", - Port: 8604, - }, - XDSServer: &consuldp.XDSServer{ - BindAddress: "127.0.1.0", - BindPort: 0, - }, - Envoy: &consuldp.EnvoyConfig{ - AdminBindAddress: "127.0.1.0", - AdminBindPort: 18000, - ReadyBindAddress: "127.0.1.0", - ReadyBindPort: 18003, - EnvoyConcurrency: 4, - EnvoyDrainStrategy: "test-strategy", - ShutdownDrainListenersEnabled: true, - GracefulShutdownPath: "/graceful_shutdown", - GracefulStartupPath: "/graceful_startup", - EnvoyDrainTimeSeconds: 30, - GracefulPort: 20300, - DumpEnvoyConfigOnExitEnabled: false, - }, - Telemetry: &consuldp.TelemetryConfig{ - UseCentralConfig: true, - Prometheus: consuldp.PrometheusTelemetryConfig{ - RetentionTime: 10 * time.Second, - ScrapePath: "/metrics", - MergePort: 12000, - CACertsPath: "/consul/", - CertFile: "prom-ca-cert.pem", - KeyFile: "prom-key.pem", - }, - }, - } - }, - wantErr: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.desc, func(t *testing.T) { - opts, err := tc.flagOpts() - require.NoError(t, err) - - if tc.writeConfigFile != nil { - require.NoError(t, tc.writeConfigFile(t)) - } - - cfg, err := opts.buildDataplaneConfig(nil) - - if tc.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - expCfg := tc.makeExpectedCfg(opts) - require.Equal(t, expCfg, cfg) - } - }) - } -} - -func generateFlagOptsWithServiceFlags() (*FlagOpts, error) { - data := ` - { - "consul": { - "addresses": "` + fmt.Sprintf("consul.address.server_%d", rand.Int()) + `", - "grpcPort": ` + fmt.Sprintf("%d", rand.Int()) + `, - "serverWatchDisabled": true, - "tls": { - "disabled": false, - "caCertsPath": "/consul/", - "certFile": "ca-cert.pem", - "keyFile": "key.pem", - "serverName": "tls-server-name", - "insecureSkipVerify": true - }, - "credentials": { - "type": "static", - "static": { - "token": "test-token-123" - }, - "login": { - "authMethod": "test-iam-auth", - "bearerToken": "bearer-login" - } - } - }, - "service": { - "nodeName": "test-node-dc1", - "nodeID": "dc1.node.id", - "namespace": "default", - "serviceID": "node1.service1", - "partition": "default" - }, - "logging": { - "logJSON": true, - "logLevel": "warn" - }, - "telemetry": { - "useCentralConfig": true, - "prometheus": { - "retentionTime": "10s", - "scrapePath": "/metrics", - "mergePort": 12000, - "caCertsPath": "/consul/", - "certFile": "prom-ca-cert.pem", - "keyFile": "prom-key.pem" - } - }, - "envoy": { - "adminBindAddress": "127.0.1.0", - "adminBindPort": 18000, - "readyBindAddress": "127.0.1.0", - "readyBindPort": 18003, - "concurrency": 4, - "drainStrategy": "test-strategy", - "shutdownDrainListenersEnabled": true - }, - "xdsServer": { - "bindAddress": "127.0.1.0" - }, - "dnsServer": { - "bindPort": 8604 - } - }` - - var configFlags *DataplaneConfigFlags - err := json.Unmarshal([]byte(data), &configFlags) - if err != nil { - return nil, err - } - - return &FlagOpts{ - dataplaneConfig: *configFlags, - }, nil -} -func generateFlagOptsWithProxyFlags() (*FlagOpts, error) { - data := ` - { - "consul": { - "addresses": "` + fmt.Sprintf("consul.address.server_%d", rand.Int()) + `", - "grpcPort": ` + fmt.Sprintf("%d", rand.Int()) + `, - "serverWatchDisabled": true, - "tls": { - "disabled": false, - "caCertsPath": "/consul/", - "certFile": "ca-cert.pem", - "keyFile": "key.pem", - "serverName": "tls-server-name", - "insecureSkipVerify": true - }, - "credentials": { - "type": "static", - "static": { - "token": "test-token-123" - }, - "login": { - "authMethod": "test-iam-auth", - "bearerToken": "bearer-login" - } - } - }, - "proxy": { - "nodeName": "test-node-dc1", - "nodeID": "dc1.node.id", - "namespace": "default", - "id": "node1.service1", - "partition": "default" - }, - "logging": { - "logJSON": true, - "logLevel": "warn" - }, - "telemetry": { - "useCentralConfig": true, - "prometheus": { - "retentionTime": "10s", - "scrapePath": "/metrics", - "mergePort": 12000, - "caCertsPath": "/consul/", - "certFile": "prom-ca-cert.pem", - "keyFile": "prom-key.pem" - } - }, - "envoy": { - "adminBindAddress": "127.0.1.0", - "adminBindPort": 18000, - "readyBindAddress": "127.0.1.0", - "readyBindPort": 18003, - "concurrency": 4, - "drainStrategy": "test-strategy", - "shutdownDrainListenersEnabled": true - }, - "xdsServer": { - "bindAddress": "127.0.1.0" - }, - "dnsServer": { - "bindPort": 8604 - } - }` - - var configFlags *DataplaneConfigFlags - err := json.Unmarshal([]byte(data), &configFlags) - if err != nil { - return nil, err - } - - return &FlagOpts{ - dataplaneConfig: *configFlags, - }, nil -} - -func strReference(s string) *string { - return &s -} - -func boolReference(b bool) *bool { - return &b -} - -func intReference(i int) *int { - return &i -} diff --git a/cmd/consul-dataplane/duration.go b/cmd/consul-dataplane/duration.go deleted file mode 100644 index c2ad7baa..00000000 --- a/cmd/consul-dataplane/duration.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package main - -import ( - "encoding/json" - "fmt" - "time" -) - -// Duration wraps the time.duration field to support -// unmarshalling JSON values to the time.duration fields -// in destination structs -type Duration struct { - Duration time.Duration -} - -func (d *Duration) UnmarshalJSON(b []byte) error { - var unmarshalledJson interface{} - - err := json.Unmarshal(b, &unmarshalledJson) - if err != nil { - return err - } - - switch value := unmarshalledJson.(type) { - case float64: - d.Duration = time.Duration(value) - case string: - d.Duration, err = time.ParseDuration(value) - if err != nil { - return err - } - default: - return fmt.Errorf("invalid duration: %#v", unmarshalledJson) - } - - return nil -} diff --git a/cmd/consul-dataplane/duration_test.go b/cmd/consul-dataplane/duration_test.go deleted file mode 100644 index 1c22eefd..00000000 --- a/cmd/consul-dataplane/duration_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package main - -import ( - "encoding/json" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -type DurationTestInput struct { - Key1 string `json:"key1"` - Key2 string `json:"key2"` - Duration *Duration `json:"duration,omitempty"` -} - -func TestUnmarshalForStringBasedDurationInput(t *testing.T) { - data := ` - { - "key1": "value1", - "key2": "value2", - "duration": "100s" - } - ` - - d, err := time.ParseDuration("100s") - require.NoError(t, err) - - expectedDuration := &Duration{ - Duration: d, - } - - var durationTestInput *DurationTestInput - err = json.Unmarshal([]byte(data), &durationTestInput) - require.NoError(t, err) - - require.NotNil(t, durationTestInput) - require.NotNil(t, durationTestInput.Duration) - require.Equal(t, expectedDuration.Duration, durationTestInput.Duration.Duration) -} - -func TestUnmarshalForFloatBasedDurationInput(t *testing.T) { - data := ` - { - "key1": "value1", - "key2": "value2", - "duration": 4.5 - } - ` - - in := 4.5 - expectedDuration := &Duration{ - Duration: time.Duration(in), - } - - var durationTestInput *DurationTestInput - err := json.Unmarshal([]byte(data), &durationTestInput) - require.NoError(t, err) - - require.NotNil(t, durationTestInput) - require.NotNil(t, durationTestInput.Duration) - require.Equal(t, expectedDuration.Duration, durationTestInput.Duration.Duration) -} diff --git a/cmd/consul-dataplane/env.go b/cmd/consul-dataplane/env.go index 982dca96..128c7ba4 100644 --- a/cmd/consul-dataplane/env.go +++ b/cmd/consul-dataplane/env.go @@ -12,71 +12,29 @@ import ( ) var ( - asInt = func(s string) (*int, error) { - if s == "" { - return nil, nil - } - - n, err := strconv.Atoi(s) - if err != nil { - return nil, err - } - - return &n, nil - } - - asBool = func(s string) (*bool, error) { - if s == "" { - return nil, nil - } - - b, err := strconv.ParseBool(s) - if err != nil { - return nil, err - } - - return &b, nil - } - - asDuration = func(s string) (*Duration, error) { - if s == "" { - return nil, nil - } - - t, err := time.ParseDuration(s) - if err != nil { - return nil, err - } - - return &Duration{Duration: t}, nil - } - - asString = func(s string) (*string, error) { - if s == "" { - return nil, nil - } - - return &s, nil - } + asInt = strconv.Atoi + asBool = strconv.ParseBool + asDuration = time.ParseDuration + asString = func(s string) (string, error) { return s, nil } ) -func parseEnv[T any](name string, parseFn func(string) (*T, error)) *T { - val, err := parseEnvError(name, parseFn) +func parseEnv[T any](name string, defaultVal T, parseFn func(string) (T, error)) T { + val, err := parseEnvError(name, defaultVal, parseFn) if err != nil { log.Fatal(err) } return val } -func parseEnvError[T any](name string, parseFn func(string) (*T, error)) (*T, error) { +func parseEnvError[T any](name string, defaultVal T, parseFn func(string) (T, error)) (T, error) { valStr, ok := os.LookupEnv(name) if !ok { // Env var is not present in the environment. - return nil, nil + return defaultVal, nil } valT, err := parseFn(valStr) if err != nil { - return nil, fmt.Errorf("unable to parse environment variable %s=%s as %T", name, valStr, valT) + return defaultVal, fmt.Errorf("unable to parse environment variable %s=%s as %T", name, valStr, valT) } return valT, nil } diff --git a/cmd/consul-dataplane/flags.go b/cmd/consul-dataplane/flags.go index 640d8a78..5fb2b59d 100644 --- a/cmd/consul-dataplane/flags.go +++ b/cmd/consul-dataplane/flags.go @@ -12,41 +12,40 @@ import ( "flag" "fmt" "log" - "strconv" "time" ) -func StringVar(fs *flag.FlagSet, p **string, name, env, usage string) { +func StringVar(p *string, name, defaultVal, env, usage string) { usage = includeEnvUsage(env, usage) // The order here is important. The flag will sets the value to the default // value, prior to flag parsing. So after the flag is created, we override // the value to the env var, if it is set, or otherwise the defaultVal. - fs.Var(newStringPtrValue(p), name, usage) - *p = parseEnv(env, asString) + flag.StringVar(p, name, defaultVal, usage) + *p = parseEnv(env, defaultVal, asString) } -func IntVar(fs *flag.FlagSet, p **int, name, env, usage string) { +func IntVar(p *int, name string, defaultVal int, env, usage string) { usage = includeEnvUsage(env, usage) - fs.Var(newIntPtrValue(p), name, usage) - *p = parseEnv(env, asInt) + flag.IntVar(p, name, defaultVal, usage) + *p = parseEnv(env, defaultVal, asInt) } -func BoolVar(fs *flag.FlagSet, p **bool, name, env, usage string) { +func BoolVar(p *bool, name string, defaultVal bool, env, usage string) { usage = includeEnvUsage(env, usage) - fs.Var(newBoolPtrValue(p), name, usage) - *p = parseEnv(env, asBool) + flag.BoolVar(p, name, defaultVal, usage) + *p = parseEnv(env, defaultVal, asBool) } -func DurationVar(fs *flag.FlagSet, p **Duration, name, env, usage string) { +func DurationVar(p *time.Duration, name string, defaultVal time.Duration, env, usage string) { usage = includeEnvUsage(env, usage) - fs.Var(newDurationPtrValue(p), name, usage) - *p = parseEnv(env, asDuration) + flag.DurationVar(p, name, defaultVal, usage) + *p = parseEnv(env, defaultVal, asDuration) } // MapVar supports repeated flags and the environment variables numbered {1,9}. -func MapVar(fs *flag.FlagSet, v flag.Value, name, env, usage string) { +func MapVar(v flag.Value, name, env, usage string) { usage = includeEnvUsage(fmt.Sprintf("%s{1,9}", env), usage) - fs.Var(v, name, usage) + flag.Var(v, name, usage) for varName, value := range multiValueEnv(env) { err := v.Set(value) if err != nil { @@ -58,170 +57,3 @@ func MapVar(fs *flag.FlagSet, v flag.Value, name, env, usage string) { func includeEnvUsage(env, usage string) string { return fmt.Sprintf("%s Environment variable: %s.", usage, env) } - -// stringPtrValue is a flag.Value which stores the value in a *string. -// If the value was not set the pointer is nil. -type stringPtrValue struct { - v **string - b bool -} - -func newStringPtrValue(p **string) *stringPtrValue { - return &stringPtrValue{p, false} -} - -func (s *stringPtrValue) Set(val string) error { - *s.v, s.b = &val, true - return nil -} - -func (s *stringPtrValue) Get() interface{} { - if s.b { - return *s.v - } - return (*string)(nil) -} - -func (s *stringPtrValue) String() string { - if s.b { - return **s.v - } - return "" -} - -// intPtrValue is a flag.Value which stores the value in a *int if it -// can be parsed with strconv.Atoi. If the value was not set the pointer -// is nil. -type intPtrValue struct { - v **int - b bool -} - -func newIntPtrValue(p **int) *intPtrValue { - return &intPtrValue{p, false} -} - -func (s *intPtrValue) Set(val string) error { - n, err := strconv.Atoi(val) - if err != nil { - return err - } - *s.v, s.b = &n, true - return nil -} - -func (s *intPtrValue) Get() interface{} { - if s.b { - return *s.v - } - return (*int)(nil) -} - -func (s *intPtrValue) String() string { - if s.b { - return strconv.Itoa(**s.v) - } - return "" -} - -// boolPtrValue is a flag.Value which stores the value in a *bool if it -// can be parsed with strconv.ParseBool. If the value was not set the -// pointer is nil. -type boolPtrValue struct { - v **bool - b bool -} - -func newBoolPtrValue(p **bool) *boolPtrValue { - return &boolPtrValue{p, false} -} - -func (s *boolPtrValue) IsBoolFlag() bool { return true } - -func (s *boolPtrValue) Set(val string) error { - b, err := strconv.ParseBool(val) - if err != nil { - return err - } - *s.v, s.b = &b, true - return nil -} - -func (s *boolPtrValue) Get() interface{} { - if s.b { - return *s.v - } - return (*bool)(nil) -} - -func (s *boolPtrValue) String() string { - if s.b { - return strconv.FormatBool(**s.v) - } - return "" -} - -// durationPtrValue is a flag.Value which stores the value in a -// *time.Duration if it can be parsed with time.ParseDuration. If the -// value was not set the pointer is nil. -type durationPtrValue struct { - v **Duration - b bool -} - -func newDurationPtrValue(p **Duration) *durationPtrValue { - return &durationPtrValue{p, false} -} - -func (s *durationPtrValue) Set(val string) error { - d, err := time.ParseDuration(val) - if err != nil { - return err - } - *s.v, s.b = &Duration{Duration: d}, true - return nil -} - -func (s *durationPtrValue) Get() interface{} { - if s.b { - return *s.v - } - return (*time.Duration)(nil) -} - -func (s *durationPtrValue) String() string { - if s.b { - return (*(*s).v).Duration.String() - } - return "" -} - -func durationVal(t *Duration) time.Duration { - if t == nil { - return 0 - } - - return t.Duration -} - -func stringVal(s *string) string { - if s == nil { - return "" - } - - return *s -} - -func intVal(v *int) int { - if v == nil { - return 0 - } - return *v -} - -func boolVal(v *bool) bool { - if v == nil { - return false - } - return *v -} diff --git a/cmd/consul-dataplane/main.go b/cmd/consul-dataplane/main.go index ca74a195..bfcb5b4f 100644 --- a/cmd/consul-dataplane/main.go +++ b/cmd/consul-dataplane/main.go @@ -12,158 +12,255 @@ import ( "os/signal" "strings" "syscall" + "time" "github.com/hashicorp/consul-dataplane/pkg/consuldp" "github.com/hashicorp/consul-dataplane/pkg/version" ) var ( - flagOpts *FlagOpts - flags *flag.FlagSet + printVersion bool + + addresses string + grpcPort int + serverWatchDisabled bool + + tlsDisabled bool + tlsCACertsPath string + tlsServerName string + tlsCertFile string + tlsKeyFile string + tlsInsecureSkipVerify bool + + logLevel string + logJSON bool + + nodeName string + nodeID string + serviceID string + serviceIDPath string + namespace string + partition string + + credentialType string + token string + loginAuthMethod string + loginNamespace string + loginPartition string + loginDatacenter string + loginBearerToken string + loginBearerTokenPath string + loginMeta map[string]string + + useCentralTelemetryConfig bool + + promRetentionTime time.Duration + promCACertsPath string + promKeyFile string + promCertFile string + promServiceMetricsURL string + promScrapePath string + promMergePort int + + adminBindAddr string + adminBindPort int + readyBindAddr string + readyBindPort int + envoyConcurrency int + envoyDrainTimeSeconds int + envoyDrainStrategy string + + xdsBindAddr string + xdsBindPort int + + consulDNSBindAddr string + consulDNSPort int + + shutdownDrainListenersEnabled bool + shutdownGracePeriodSeconds int + gracefulShutdownPath string + gracefulPort int + + dumpEnvoyConfigOnExitEnabled bool ) func init() { - flags = flag.NewFlagSet("", flag.ContinueOnError) - flagOpts = &FlagOpts{} - flags.BoolVar(&flagOpts.printVersion, "version", false, "Prints the current version of consul-dataplane.") + flag.BoolVar(&printVersion, "version", false, "Prints the current version of consul-dataplane.") - StringVar(flags, &flagOpts.dataplaneConfig.Consul.Addresses, "addresses", "DP_CONSUL_ADDRESSES", "Consul server gRPC addresses. Value can be:\n"+ + StringVar(&addresses, "addresses", "", "DP_CONSUL_ADDRESSES", "Consul server gRPC addresses. Value can be:\n"+ "1. A DNS name that resolves to server addresses or the DNS name of a load balancer in front of the Consul servers; OR\n"+ "2. An executable command in the format, 'exec='. The executable\n"+ " a) on success - should exit 0 and print to stdout whitespace delimited IP (v4/v6) addresses\n"+ " b) on failure - exit with a non-zero code and optionally print an error message of up to 1024 bytes to stderr.\n"+ " Refer to https://github.com/hashicorp/go-netaddrs#summary for more details and examples.\n") - IntVar(flags, &flagOpts.dataplaneConfig.Consul.GRPCPort, "grpc-port", "DP_CONSUL_GRPC_PORT", "The Consul server gRPC port to which consul-dataplane connects.") + IntVar(&grpcPort, "grpc-port", 8502, "DP_CONSUL_GRPC_PORT", "The Consul server gRPC port to which consul-dataplane connects.") - BoolVar(flags, &flagOpts.dataplaneConfig.Consul.ServerWatchDisabled, "server-watch-disabled", "DP_SERVER_WATCH_DISABLED", "Setting this prevents consul-dataplane from consuming the server update stream. This is useful for situations where Consul servers are behind a load balancer.") + BoolVar(&serverWatchDisabled, "server-watch-disabled", false, "DP_SERVER_WATCH_DISABLED", "Setting this prevents consul-dataplane from consuming the server update stream. This is useful for situations where Consul servers are behind a load balancer.") - StringVar(flags, &flagOpts.dataplaneConfig.Logging.LogLevel, "log-level", "DP_LOG_LEVEL", "Log level of the messages to print. "+ + StringVar(&logLevel, "log-level", "info", "DP_LOG_LEVEL", "Log level of the messages to print. "+ "Available log levels are \"trace\", \"debug\", \"info\", \"warn\", and \"error\".") - BoolVar(flags, &flagOpts.dataplaneConfig.Logging.LogJSON, "log-json", "DP_LOG_JSON", "Enables log messages in JSON format.") - - StringVar(flags, &flagOpts.dataplaneConfig.Service.NodeName, "service-node-name", "DP_SERVICE_NODE_NAME", - "[Deprecated; use -proxy-node-name instead] The name of the Consul node to which the proxy service instance is registered.") - StringVar(flags, &flagOpts.dataplaneConfig.Service.NodeID, "service-node-id", "DP_SERVICE_NODE_ID", - "[Deprecated; use -proxy-node-id instead] The ID of the Consul node to which the proxy service instance is registered.") - StringVar(flags, &flagOpts.dataplaneConfig.Service.ServiceID, "proxy-service-id", "DP_PROXY_SERVICE_ID", - "[Deprecated; use -proxy-id instead] The proxy service instance's ID.") - StringVar(flags, &flagOpts.dataplaneConfig.Service.ServiceIDPath, "proxy-service-id-path", "DP_PROXY_SERVICE_ID_PATH", - "[Deprecated; use -proxy-id-path instead] The path to a file containing the proxy service instance's ID.") - StringVar(flags, &flagOpts.dataplaneConfig.Service.Namespace, "service-namespace", "DP_SERVICE_NAMESPACE", - "[Deprecated; use -proxy-namespace instead] The Consul Enterprise namespace in which the proxy service instance is registered.") - StringVar(flags, &flagOpts.dataplaneConfig.Service.Partition, "service-partition", "DP_SERVICE_PARTITION", - "[Deprecated; use -proxy-partition instead] The Consul Enterprise partition in which the proxy service instance is registered.") - - StringVar(flags, &flagOpts.dataplaneConfig.Proxy.NodeName, "proxy-node-name", "DP_PROXY_NODE_NAME", - "The name of the Consul node to which the proxy service instance is registered."+ - "In Consul's V2 Catalog API, this value is ignored.") - StringVar(flags, &flagOpts.dataplaneConfig.Proxy.NodeID, "proxy-node-id", "DP_PROXY_NODE_ID", - "The ID of the Consul node to which the proxy service instance is registered."+ - "In Consul's V2 Catalog API, this value is ignored.") - StringVar(flags, &flagOpts.dataplaneConfig.Proxy.ID, "proxy-id", "DP_PROXY_ID", - "In Consul's V1 Catalog API, the proxy service instance's ID."+ - "In Consul's V2 Catalog API, the workload ID associated with the proxy.") - StringVar(flags, &flagOpts.dataplaneConfig.Proxy.IDPath, "proxy-id-path", "DP_PROXY_ID_PATH", - "In Consul's V1 Catalog API, the path to a file containing the proxy service instance's ID."+ - "In Consul's V2 Catalog API, the path to a file containing the workload ID associated with the proxy.") - StringVar(flags, &flagOpts.dataplaneConfig.Proxy.Namespace, "proxy-namespace", "DP_PROXY_NAMESPACE", - "The Consul Enterprise namespace in which the proxy service instance (V1 API) or workload (V2 API) is registered.") - StringVar(flags, &flagOpts.dataplaneConfig.Proxy.Partition, "proxy-partition", "DP_PROXY_PARTITION", - "The Consul Enterprise partition in which the proxy service instance (V1 API) or workload (V2 API) is registered.") - - StringVar(flags, &flagOpts.dataplaneConfig.Consul.Credentials.Type, "credential-type", "DP_CREDENTIAL_TYPE", "The type of credentials, either static or login, used to authenticate with Consul servers.") - StringVar(flags, &flagOpts.dataplaneConfig.Consul.Credentials.Static.Token, "static-token", "DP_CREDENTIAL_STATIC_TOKEN", "The ACL token used to authenticate requests to Consul servers when -credential-type is set to static.") - StringVar(flags, &flagOpts.dataplaneConfig.Consul.Credentials.Login.AuthMethod, "login-auth-method", "DP_CREDENTIAL_LOGIN_AUTH_METHOD", "The auth method used to log in.") - StringVar(flags, &flagOpts.dataplaneConfig.Consul.Credentials.Login.Namespace, "login-namespace", "DP_CREDENTIAL_LOGIN_NAMESPACE", "The Consul Enterprise namespace containing the auth method.") - StringVar(flags, &flagOpts.dataplaneConfig.Consul.Credentials.Login.Partition, "login-partition", "DP_CREDENTIAL_LOGIN_PARTITION", "The Consul Enterprise partition containing the auth method.") - StringVar(flags, &flagOpts.dataplaneConfig.Consul.Credentials.Login.Datacenter, "login-datacenter", "DP_CREDENTIAL_LOGIN_DATACENTER", "The datacenter containing the auth method.") - StringVar(flags, &flagOpts.dataplaneConfig.Consul.Credentials.Login.BearerToken, "login-bearer-token", "DP_CREDENTIAL_LOGIN_BEARER_TOKEN", "The bearer token presented to the auth method.") - StringVar(flags, &flagOpts.dataplaneConfig.Consul.Credentials.Login.BearerTokenPath, "login-bearer-token-path", "DP_CREDENTIAL_LOGIN_BEARER_TOKEN_PATH", "The path to a file containing the bearer token presented to the auth method.") - MapVar(flags, (*FlagMapValue)(&flagOpts.dataplaneConfig.Consul.Credentials.Login.Meta), "login-meta", "DP_CREDENTIAL_LOGIN_META", `A set of key/value pairs to attach to the ACL token. Each pair is formatted as "=". This flag may be passed multiple times.`) - - BoolVar(flags, &flagOpts.dataplaneConfig.Telemetry.UseCentralConfig, "telemetry-use-central-config", "DP_TELEMETRY_USE_CENTRAL_CONFIG", "Controls whether the proxy applies the central telemetry configuration.") - - DurationVar(flags, &flagOpts.dataplaneConfig.Telemetry.Prometheus.RetentionTime, "telemetry-prom-retention-time", "DP_TELEMETRY_PROM_RETENTION_TIME", "The duration for prometheus metrics aggregation.") - StringVar(flags, &flagOpts.dataplaneConfig.Telemetry.Prometheus.CACertsPath, "telemetry-prom-ca-certs-path", "DP_TELEMETRY_PROM_CA_CERTS_PATH", "The path to a file or directory containing CA certificates used to verify the Prometheus server's certificate.") - StringVar(flags, &flagOpts.dataplaneConfig.Telemetry.Prometheus.KeyFile, "telemetry-prom-key-file", "DP_TELEMETRY_PROM_KEY_FILE", "The path to the client private key used to serve Prometheus metrics.") - StringVar(flags, &flagOpts.dataplaneConfig.Telemetry.Prometheus.CertFile, "telemetry-prom-cert-file", "DP_TELEMETRY_PROM_CERT_FILE", "The path to the client certificate used to serve Prometheus metrics.") - StringVar(flags, &flagOpts.dataplaneConfig.Telemetry.Prometheus.ServiceMetricsURL, "telemetry-prom-service-metrics-url", "DP_TELEMETRY_PROM_SERVICE_METRICS_URL", "Prometheus metrics at this URL are scraped and included in Consul Dataplane's main Prometheus metrics.") - StringVar(flags, &flagOpts.dataplaneConfig.Telemetry.Prometheus.ScrapePath, "telemetry-prom-scrape-path", "DP_TELEMETRY_PROM_SCRAPE_PATH", "The URL path where Envoy serves Prometheus metrics.") - IntVar(flags, &flagOpts.dataplaneConfig.Telemetry.Prometheus.MergePort, "telemetry-prom-merge-port", "DP_TELEMETRY_PROM_MERGE_PORT", "The port to serve merged Prometheus metrics.") - - StringVar(flags, &flagOpts.dataplaneConfig.Envoy.AdminBindAddr, "envoy-admin-bind-address", "DP_ENVOY_ADMIN_BIND_ADDRESS", "The address on which the Envoy admin server is available.") - IntVar(flags, &flagOpts.dataplaneConfig.Envoy.AdminBindPort, "envoy-admin-bind-port", "DP_ENVOY_ADMIN_BIND_PORT", "The port on which the Envoy admin server is available.") - StringVar(flags, &flagOpts.dataplaneConfig.Envoy.ReadyBindAddr, "envoy-ready-bind-address", "DP_ENVOY_READY_BIND_ADDRESS", "The address on which Envoy's readiness probe is available.") - IntVar(flags, &flagOpts.dataplaneConfig.Envoy.ReadyBindPort, "envoy-ready-bind-port", "DP_ENVOY_READY_BIND_PORT", "The port on which Envoy's readiness probe is available.") - IntVar(flags, &flagOpts.dataplaneConfig.Envoy.Concurrency, "envoy-concurrency", "DP_ENVOY_CONCURRENCY", "The number of worker threads that Envoy uses.") - IntVar(flags, &flagOpts.dataplaneConfig.Envoy.DrainTimeSeconds, "envoy-drain-time-seconds", "DP_ENVOY_DRAIN_TIME", "The time in seconds for which Envoy will drain connections.") - StringVar(flags, &flagOpts.dataplaneConfig.Envoy.DrainStrategy, "envoy-drain-strategy", "DP_ENVOY_DRAIN_STRATEGY", "The behaviour of Envoy during the drain sequence. Determines whether all open connections should be encouraged to drain immediately or to increase the percentage gradually as the drain time elapses.") - - StringVar(flags, &flagOpts.dataplaneConfig.XDSServer.BindAddr, "xds-bind-addr", "DP_XDS_BIND_ADDR", "The address on which the Envoy xDS server is available.") - IntVar(flags, &flagOpts.dataplaneConfig.XDSServer.BindPort, "xds-bind-port", "DP_XDS_BIND_PORT", "The port on which the Envoy xDS server is available.") - - BoolVar(flags, &flagOpts.dataplaneConfig.Consul.TLS.Disabled, "tls-disabled", "DP_TLS_DISABLED", "Communicate with Consul servers over a plaintext connection. Useful for testing, but not recommended for production.") - StringVar(flags, &flagOpts.dataplaneConfig.Consul.TLS.CACertsPath, "ca-certs", "DP_CA_CERTS", "The path to a file or directory containing CA certificates used to verify the server's certificate.") - StringVar(flags, &flagOpts.dataplaneConfig.Consul.TLS.CertFile, "tls-cert", "DP_TLS_CERT", "The path to a client certificate file. This is required if tls.grpc.verify_incoming is enabled on the server.") - StringVar(flags, &flagOpts.dataplaneConfig.Consul.TLS.KeyFile, "tls-key", "DP_TLS_KEY", "The path to a client private key file. This is required if tls.grpc.verify_incoming is enabled on the server.") - StringVar(flags, &flagOpts.dataplaneConfig.Consul.TLS.ServerName, "tls-server-name", "DP_TLS_SERVER_NAME", "The hostname to expect in the server certificate's subject. This is required if -addresses is not a DNS name.") - BoolVar(flags, &flagOpts.dataplaneConfig.Consul.TLS.InsecureSkipVerify, "tls-insecure-skip-verify", "DP_TLS_INSECURE_SKIP_VERIFY", "Do not verify the server's certificate. Useful for testing, but not recommended for production.") - - StringVar(flags, &flagOpts.dataplaneConfig.DNSServer.BindAddr, "consul-dns-bind-addr", "DP_CONSUL_DNS_BIND_ADDR", "The address that will be bound to the consul dns proxy.") - IntVar(flags, &flagOpts.dataplaneConfig.DNSServer.BindPort, "consul-dns-bind-port", "DP_CONSUL_DNS_BIND_PORT", "The port the consul dns proxy will listen on. By default -1 disables the dns proxy") + BoolVar(&logJSON, "log-json", false, "DP_LOG_JSON", "Enables log messages in JSON format.") + + StringVar(&nodeName, "service-node-name", "", "DP_SERVICE_NODE_NAME", "The name of the Consul node to which the proxy service instance is registered.") + StringVar(&nodeID, "service-node-id", "", "DP_SERVICE_NODE_ID", "The ID of the Consul node to which the proxy service instance is registered.") + StringVar(&serviceID, "proxy-service-id", "", "DP_PROXY_SERVICE_ID", "The proxy service instance's ID.") + StringVar(&serviceIDPath, "proxy-service-id-path", "", "DP_PROXY_SERVICE_ID_PATH", "The path to a file containing the proxy service instance's ID.") + StringVar(&namespace, "service-namespace", "", "DP_SERVICE_NAMESPACE", "The Consul Enterprise namespace in which the proxy service instance is registered.") + StringVar(&partition, "service-partition", "", "DP_SERVICE_PARTITION", "The Consul Enterprise partition in which the proxy service instance is registered.") + + StringVar(&credentialType, "credential-type", "", "DP_CREDENTIAL_TYPE", "The type of credentials, either static or login, used to authenticate with Consul servers.") + StringVar(&token, "static-token", "", "DP_CREDENTIAL_STATIC_TOKEN", "The ACL token used to authenticate requests to Consul servers when -credential-type is set to static.") + StringVar(&loginAuthMethod, "login-auth-method", "", "DP_CREDENTIAL_LOGIN_AUTH_METHOD", "The auth method used to log in.") + StringVar(&loginNamespace, "login-namespace", "", "DP_CREDENTIAL_LOGIN_NAMESPACE", "The Consul Enterprise namespace containing the auth method.") + StringVar(&loginPartition, "login-partition", "", "DP_CREDENTIAL_LOGIN_PARTITION", "The Consul Enterprise partition containing the auth method.") + StringVar(&loginDatacenter, "login-datacenter", "", "DP_CREDENTIAL_LOGIN_DATACENTER", "The datacenter containing the auth method.") + StringVar(&loginBearerToken, "login-bearer-token", "", "DP_CREDENTIAL_LOGIN_BEARER_TOKEN", "The bearer token presented to the auth method.") + StringVar(&loginBearerTokenPath, "login-bearer-token-path", "", "DP_CREDENTIAL_LOGIN_BEARER_TOKEN_PATH", "The path to a file containing the bearer token presented to the auth method.") + MapVar((*FlagMapValue)(&loginMeta), "login-meta", "DP_CREDENTIAL_LOGIN_META", `A set of key/value pairs to attach to the ACL token. Each pair is formatted as "=". This flag may be passed multiple times.`) + + BoolVar(&useCentralTelemetryConfig, "telemetry-use-central-config", true, "DP_TELEMETRY_USE_CENTRAL_CONFIG", "Controls whether the proxy applies the central telemetry configuration.") + + DurationVar(&promRetentionTime, "telemetry-prom-retention-time", 60*time.Second, "DP_TELEMETRY_PROM_RETENTION_TIME", "The duration for prometheus metrics aggregation.") + StringVar(&promCACertsPath, "telemetry-prom-ca-certs-path", "", "DP_TELEMETRY_PROM_CA_CERTS_PATH", "The path to a file or directory containing CA certificates used to verify the Prometheus server's certificate.") + StringVar(&promKeyFile, "telemetry-prom-key-file", "", "DP_TELEMETRY_PROM_KEY_FILE", "The path to the client private key used to serve Prometheus metrics.") + StringVar(&promCertFile, "telemetry-prom-cert-file", "", "DP_TELEMETRY_PROM_CERT_FILE", "The path to the client certificate used to serve Prometheus metrics.") + StringVar(&promServiceMetricsURL, "telemetry-prom-service-metrics-url", "", "DP_TELEMETRY_PROM_SERVICE_METRICS_URL", "Prometheus metrics at this URL are scraped and included in Consul Dataplane's main Prometheus metrics.") + StringVar(&promScrapePath, "telemetry-prom-scrape-path", "/metrics", "DP_TELEMETRY_PROM_SCRAPE_PATH", "The URL path where Envoy serves Prometheus metrics.") + IntVar(&promMergePort, "telemetry-prom-merge-port", 20100, "DP_TELEMETRY_PROM_MERGE_PORT", "The port to serve merged Prometheus metrics.") + + StringVar(&adminBindAddr, "envoy-admin-bind-address", "127.0.0.1", "DP_ENVOY_ADMIN_BIND_ADDRESS", "The address on which the Envoy admin server is available.") + IntVar(&adminBindPort, "envoy-admin-bind-port", 19000, "DP_ENVOY_ADMIN_BIND_PORT", "The port on which the Envoy admin server is available.") + StringVar(&readyBindAddr, "envoy-ready-bind-address", "", "DP_ENVOY_READY_BIND_ADDRESS", "The address on which Envoy's readiness probe is available.") + IntVar(&readyBindPort, "envoy-ready-bind-port", 0, "DP_ENVOY_READY_BIND_PORT", "The port on which Envoy's readiness probe is available.") + IntVar(&envoyConcurrency, "envoy-concurrency", 2, "DP_ENVOY_CONCURRENCY", "The number of worker threads that Envoy uses.") + IntVar(&envoyDrainTimeSeconds, "envoy-drain-time-seconds", 30, "DP_ENVOY_DRAIN_TIME", "The time in seconds for which Envoy will drain connections.") + StringVar(&envoyDrainStrategy, "envoy-drain-strategy", "immediate", "DP_ENVOY_DRAIN_STRATEGY", "The behaviour of Envoy during the drain sequence. Determines whether all open connections should be encouraged to drain immediately or to increase the percentage gradually as the drain time elapses.") + + StringVar(&xdsBindAddr, "xds-bind-addr", "127.0.0.1", "DP_XDS_BIND_ADDR", "The address on which the Envoy xDS server is available.") + IntVar(&xdsBindPort, "xds-bind-port", 0, "DP_XDS_BIND_PORT", "The port on which the Envoy xDS server is available.") + + BoolVar(&tlsDisabled, "tls-disabled", false, "DP_TLS_DISABLED", "Communicate with Consul servers over a plaintext connection. Useful for testing, but not recommended for production.") + StringVar(&tlsCACertsPath, "ca-certs", "", "DP_CA_CERTS", "The path to a file or directory containing CA certificates used to verify the server's certificate.") + StringVar(&tlsCertFile, "tls-cert", "", "DP_TLS_CERT", "The path to a client certificate file. This is required if tls.grpc.verify_incoming is enabled on the server.") + StringVar(&tlsKeyFile, "tls-key", "", "DP_TLS_KEY", "The path to a client private key file. This is required if tls.grpc.verify_incoming is enabled on the server.") + StringVar(&tlsServerName, "tls-server-name", "", "DP_TLS_SERVER_NAME", "The hostname to expect in the server certificate's subject. This is required if -addresses is not a DNS name.") + BoolVar(&tlsInsecureSkipVerify, "tls-insecure-skip-verify", false, "DP_TLS_INSECURE_SKIP_VERIFY", "Do not verify the server's certificate. Useful for testing, but not recommended for production.") + + StringVar(&consulDNSBindAddr, "consul-dns-bind-addr", "127.0.0.1", "DP_CONSUL_DNS_BIND_ADDR", "The address that will be bound to the consul dns proxy.") + IntVar(&consulDNSPort, "consul-dns-bind-port", -1, "DP_CONSUL_DNS_BIND_PORT", "The port the consul dns proxy will listen on. By default -1 disables the dns proxy") // Default is false because it will generally be configured appropriately by Helm // configuration or pod annotation. - BoolVar(flags, &flagOpts.dataplaneConfig.Envoy.ShutdownDrainListenersEnabled, "shutdown-drain-listeners", "DP_SHUTDOWN_DRAIN_LISTENERS", "Wait for proxy listeners to drain before terminating the proxy container.") + BoolVar(&shutdownDrainListenersEnabled, "shutdown-drain-listeners", false, "DP_SHUTDOWN_DRAIN_LISTENERS", "Wait for proxy listeners to drain before terminating the proxy container.") // Default is 0 because it will generally be configured appropriately by Helm // configuration or pod annotation. - IntVar(flags, &flagOpts.dataplaneConfig.Envoy.ShutdownGracePeriodSeconds, "shutdown-grace-period-seconds", "DP_SHUTDOWN_GRACE_PERIOD_SECONDS", "Amount of time to wait after receiving a SIGTERM signal before terminating the proxy.") - StringVar(flags, &flagOpts.dataplaneConfig.Envoy.GracefulShutdownPath, "graceful-shutdown-path", "DP_GRACEFUL_SHUTDOWN_PATH", "An HTTP path to serve the graceful shutdown endpoint.") - IntVar(flags, &flagOpts.dataplaneConfig.Envoy.GracefulPort, "graceful-port", "DP_GRACEFUL_PORT", "A port to serve HTTP endpoints for graceful shutdown.") - IntVar(flags, &flagOpts.dataplaneConfig.Envoy.StartupGracePeriodSeconds, "startup-grace-period-seconds", "DP_STARTUP_GRACE_PERIOD_SECONDS", "Amount of time to wait for consul-dataplane startup.") - StringVar(flags, &flagOpts.dataplaneConfig.Envoy.GracefulStartupPath, "graceful-startup-path", "DP_GRACEFUL_STARTUP_PATH", "An HTTP path to serve the graceful startup endpoint.") - // Default is false, may be useful for debugging unexpected termination. - BoolVar(flags, &flagOpts.dataplaneConfig.Envoy.DumpEnvoyConfigOnExitEnabled, "dump-envoy-config-on-exit", "DP_DUMP_ENVOY_CONFIG_ON_EXIT", "Call the Envoy /config_dump endpoint during consul-dataplane controlled shutdown.") + IntVar(&shutdownGracePeriodSeconds, "shutdown-grace-period-seconds", 0, "DP_SHUTDOWN_GRACE_PERIOD_SECONDS", "Amount of time to wait after receiving a SIGTERM signal before terminating the proxy.") + StringVar(&gracefulShutdownPath, "graceful-shutdown-path", "/graceful_shutdown", "DP_GRACEFUL_SHUTDOWN_PATH", "An HTTP path to serve the graceful shutdown endpoint.") + IntVar(&gracefulPort, "graceful-port", 20300, "DP_GRACEFUL_PORT", "A port to serve HTTP endpoints for graceful shutdown.") - flags.StringVar(&flagOpts.configFile, "config-file", "", "The json config file for configuring consul data plane") + // Default is false, may be useful for debugging unexpected termination. + BoolVar(&dumpEnvoyConfigOnExitEnabled, "dump-envoy-config-on-exit", false, "DP_DUMP_ENVOY_CONFIG_ON_EXIT", "Call the Envoy /config_dump endpoint during consul-dataplane controlled shutdown.") } // validateFlags performs semantic validation of the flag values func validateFlags() { - if flagOpts.dataplaneConfig.Logging.LogLevel != nil { - switch strings.ToUpper(*flagOpts.dataplaneConfig.Logging.LogLevel) { - case "TRACE", "DEBUG", "INFO", "WARN", "ERROR": - default: - log.Fatal("invalid log level. valid values - TRACE, DEBUG, INFO, WARN, ERROR") - } - } - - if flagOpts.configFile != "" && !strings.HasSuffix(flagOpts.configFile, ".json") { - log.Fatal("invalid config file format. Should be a json file") + switch strings.ToUpper(logLevel) { + case "TRACE", "DEBUG", "INFO", "WARN", "ERROR": + default: + log.Fatal("invalid log level. valid values - TRACE, DEBUG, INFO, WARN, ERROR") } } func run() error { - err := flags.Parse(os.Args[1:]) - if err != nil { - return err - } + flag.Parse() - if flagOpts.printVersion { + if printVersion { fmt.Printf("Consul Dataplane v%s\n", version.GetHumanVersion()) fmt.Printf("Revision %s\n", version.GitCommit) return nil } readServiceIDFromFile() - readProxyIDFromFile() validateFlags() - consuldpCfg, err := flagOpts.buildDataplaneConfig(flags.Args()) - if err != nil { - return err + consuldpCfg := &consuldp.Config{ + Consul: &consuldp.ConsulConfig{ + Addresses: addresses, + GRPCPort: grpcPort, + Credentials: &consuldp.CredentialsConfig{ + Type: consuldp.CredentialsType(credentialType), + Static: consuldp.StaticCredentialsConfig{ + Token: token, + }, + Login: consuldp.LoginCredentialsConfig{ + AuthMethod: loginAuthMethod, + Namespace: loginNamespace, + Partition: loginPartition, + Datacenter: loginDatacenter, + BearerToken: loginBearerToken, + BearerTokenPath: loginBearerTokenPath, + Meta: loginMeta, + }, + }, + ServerWatchDisabled: serverWatchDisabled, + TLS: &consuldp.TLSConfig{ + Disabled: tlsDisabled, + CACertsPath: tlsCACertsPath, + ServerName: tlsServerName, + CertFile: tlsCertFile, + KeyFile: tlsKeyFile, + InsecureSkipVerify: tlsInsecureSkipVerify, + }, + }, + Service: &consuldp.ServiceConfig{ + NodeName: nodeName, + NodeID: nodeID, + ServiceID: serviceID, + Namespace: namespace, + Partition: partition, + }, + Logging: &consuldp.LoggingConfig{ + Name: "consul-dataplane", + LogLevel: strings.ToUpper(logLevel), + LogJSON: logJSON, + }, + Telemetry: &consuldp.TelemetryConfig{ + UseCentralConfig: useCentralTelemetryConfig, + Prometheus: consuldp.PrometheusTelemetryConfig{ + RetentionTime: promRetentionTime, + CACertsPath: promCACertsPath, + KeyFile: promKeyFile, + CertFile: promCertFile, + ServiceMetricsURL: promServiceMetricsURL, + ScrapePath: promScrapePath, + MergePort: promMergePort, + }, + }, + Envoy: &consuldp.EnvoyConfig{ + AdminBindAddress: adminBindAddr, + AdminBindPort: adminBindPort, + ReadyBindAddress: readyBindAddr, + ReadyBindPort: readyBindPort, + EnvoyConcurrency: envoyConcurrency, + EnvoyDrainTimeSeconds: envoyDrainTimeSeconds, + EnvoyDrainStrategy: envoyDrainStrategy, + ShutdownDrainListenersEnabled: shutdownDrainListenersEnabled, + ShutdownGracePeriodSeconds: shutdownGracePeriodSeconds, + GracefulShutdownPath: gracefulShutdownPath, + GracefulPort: gracefulPort, + DumpEnvoyConfigOnExitEnabled: dumpEnvoyConfigOnExitEnabled, + ExtraArgs: flag.Args(), + }, + XDSServer: &consuldp.XDSServer{ + BindAddress: xdsBindAddr, + BindPort: xdsBindPort, + }, + DNSServer: &consuldp.DNSServerConfig{ + BindAddr: consulDNSBindAddr, + Port: consulDNSPort, + }, } consuldpInstance, err := consuldp.NewConsulDP(consuldpCfg) @@ -201,33 +298,11 @@ func main() { // because this option only really makes sense as a CLI flag (and we handle // all flag parsing here). func readServiceIDFromFile() { - if flagOpts.dataplaneConfig.Service.ServiceID == nil && - flagOpts.dataplaneConfig.Service.ServiceIDPath != nil && - *flagOpts.dataplaneConfig.Service.ServiceIDPath != "" { - id, err := os.ReadFile(*flagOpts.dataplaneConfig.Service.ServiceIDPath) + if serviceID == "" && serviceIDPath != "" { + id, err := os.ReadFile(serviceIDPath) if err != nil { log.Fatalf("failed to read given -proxy-service-id-path: %v", err) } - s := string(id) - flagOpts.dataplaneConfig.Service.ServiceID = &s - } -} - -// readProxyIDFromFile reads the proxy ID from the file specified by the -// -proxy-id-path flag. -// -// We do this here, rather than in the consuldp package's config handling, -// because this option only really makes sense as a CLI flag (and we handle -// all flag parsing here). -func readProxyIDFromFile() { - if flagOpts.dataplaneConfig.Proxy.ID == nil && - flagOpts.dataplaneConfig.Proxy.IDPath != nil && - *flagOpts.dataplaneConfig.Proxy.IDPath != "" { - id, err := os.ReadFile(*flagOpts.dataplaneConfig.Proxy.IDPath) - if err != nil { - log.Fatalf("failed to read given -proxy-id-path: %v", err) - } - s := string(id) - flagOpts.dataplaneConfig.Proxy.ID = &s + serviceID = string(id) } } diff --git a/go.mod b/go.mod index 30a1a328..62c84e9b 100644 --- a/go.mod +++ b/go.mod @@ -2,15 +2,11 @@ module github.com/hashicorp/consul-dataplane go 1.20 -// This replace directive is needed because `api` requires 0.4.1 of proto-public but we need an unreleased version -replace github.com/hashicorp/consul/proto-public v0.4.1 => github.com/hashicorp/consul/proto-public v0.1.2-0.20230929231147-632fd65c091c - require ( - dario.cat/mergo v1.0.0 github.com/adamthesax/grpc-proxy v0.0.0-20220525203857-13e92d14f87a github.com/armon/go-metrics v0.4.1 github.com/hashicorp/consul-server-connection-manager v0.1.3 - github.com/hashicorp/consul/proto-public v0.4.1 + github.com/hashicorp/consul/proto-public v0.4.0 github.com/hashicorp/go-hclog v1.2.2 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-rootcerts v1.0.2 @@ -43,10 +39,9 @@ require ( github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect github.com/stretchr/objx v0.5.0 // indirect - golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect golang.org/x/net v0.13.0 // indirect golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/sys v0.10.0 // indirect golang.org/x/text v0.11.0 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 9180b81a..a8524f92 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -147,8 +145,8 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/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/proto-public v0.1.2-0.20230929231147-632fd65c091c h1:d1ULTfDs6Hha01yfITC55MPGIsQpv0VqQfS45WZHJiY= -github.com/hashicorp/consul/proto-public v0.1.2-0.20230929231147-632fd65c091c/go.mod h1:KAOxsaELPpA7JX10kMeygAskAqsQnu3SPgeruMhYZMU= +github.com/hashicorp/consul/proto-public v0.4.0 h1:amEli9TgZBatDzvqW+k9E2HQEfOrIkIAlAreeP7vIlA= +github.com/hashicorp/consul/proto-public v0.4.0/go.mod h1:yOSsnXuMvtPPs9X9U44fb1xbUyOxY9jxuYs4R+ilxYU= github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -294,8 +292,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= 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= @@ -421,8 +417,8 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/integration-tests/main_test.go b/integration-tests/main_test.go index 01b827fe..966e5c2b 100644 --- a/integration-tests/main_test.go +++ b/integration-tests/main_test.go @@ -80,7 +80,7 @@ func TestMain(m *testing.M) { // - Running consul-datplane for each sidecar, with the "frontend" sidecar's // local listener port for its "backend" upstream exposed to the host. // - Creating proxy-defaults to set the default protocol to HTTP and prometheus -// bind address. Also set access logs on the admin interface of Envoy +// bind address. // - Creating an L7/HTTP intention to allow "frontend" to talk to "backend". // - Making an HTTP request through the "frontend" sidecar's exposed "backend" // port. @@ -88,7 +88,6 @@ func TestMain(m *testing.M) { // - Attempting to make the same request and checking that it fails. // - Making DNS queries against the frontend dataplane's UDP and TCP DNS proxies. // - Scraping the prometheus merged metrics endpoint. -// - Make a call to Envoy's admin interface and check for the access logs. func TestIntegration(t *testing.T) { suite := NewSuite(t, opts) @@ -151,14 +150,13 @@ func TestIntegration(t *testing.T) { RunService(t, suite, backendPod, "backend") backendDataplane := RunDataplane(t, backendPod, suite, DataplaneConfig{ - Addresses: server.Container.ContainerIP, - ServiceNodeName: SyntheticNodeName, - ProxyServiceID: "backend-sidecar", - LoginAuthMethod: authMethod.Name, - LoginBearerToken: authMethod.GenerateToken(t, "backend"), - DNSBindPort: dnsUDPPort.Port(), - ServiceMetricsURL: "http://localhost:8080", - DumpEnvoyConfigOnExitEnabled: true, + Addresses: server.Container.ContainerIP, + ServiceNodeName: SyntheticNodeName, + ProxyServiceID: "backend-sidecar", + LoginAuthMethod: authMethod.Name, + LoginBearerToken: authMethod.GenerateToken(t, "backend"), + DNSBindPort: dnsUDPPort.Port(), + ServiceMetricsURL: "http://localhost:8080", }) frontendPod := RunPod(t, suite, "frontend", []nat.Port{ @@ -282,15 +280,6 @@ func TestIntegration(t *testing.T) { require.Contains(t, metrics, "envoy_server_total_connections") require.Contains(t, metrics, `service_metric{service_name="backend"}`) - // Test access logs (Consul 1.15 or greater) - if semver.Compare(semver.MajorMinor(opts.ServerVersion), "v1.15") >= 0 { - GetEnvoyClusters(t, backendPod.HostIP, backendPod.MappedPorts[EnvoyAdminPort]) - require.Eventuallyf(t, func() bool { - output := backendDataplane.ContainerLogs(t) - return strings.Contains(output, "{\"custom_field_path\":\"/clusters\"}") - }, 30*time.Second, 3*time.Second, "could not find admin access logs in output") - } - // Overwrite deny intention and allow two-way connections to prepare for // testing graceful shutdown server.SetConfigEntry(t, &api.ServiceIntentionsConfigEntry{ @@ -371,4 +360,13 @@ func TestIntegration(t *testing.T) { backendPod.HostIP, backendPod.MappedPorts[upstreamLocalBindPort], ) + + // Test access logs (Consul 1.15 or greater) + if semver.Compare(semver.MajorMinor(opts.ServerVersion), "v1.15") >= 0 { + GetEnvoyClusters(t, backendPod.HostIP, backendPod.MappedPorts[EnvoyAdminPort]) + require.Eventuallyf(t, func() bool { + output := backendDataplane.ContainerLogs(t) + return strings.Contains(output, "{\"custom_field_path\":\"/clusters\"}") + }, 30*time.Second, 3*time.Second, "could not find admin access logs in output") + } } diff --git a/internal/bootstrap/bootstrap_tpl.go b/internal/bootstrap/bootstrap_tpl.go index b6476589..60ab8846 100644 --- a/internal/bootstrap/bootstrap_tpl.go +++ b/internal/bootstrap/bootstrap_tpl.go @@ -294,6 +294,12 @@ const bootstrapTemplate = `{ "api_type": "DELTA_GRPC", "transport_api_version": "V3", "grpc_services": { + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "{{ .Token }}" + } + ], "envoy_grpc": { "cluster_name": "{{ .LocalAgentClusterName }}" } diff --git a/pkg/consuldp/bootstrap.go b/pkg/consuldp/bootstrap.go index 326a1375..6b97c07c 100644 --- a/pkg/consuldp/bootstrap.go +++ b/pkg/consuldp/bootstrap.go @@ -12,7 +12,6 @@ import ( "strings" "github.com/hashicorp/consul/proto-public/pbdataplane" - pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" "github.com/mitchellh/mapstructure" "github.com/hashicorp/consul-dataplane/internal/bootstrap" @@ -29,12 +28,11 @@ const ( // bootstrapConfig generates the Envoy bootstrap config in JSON format. func (cdp *ConsulDataplane) bootstrapConfig(ctx context.Context) (*bootstrap.BootstrapConfig, []byte, error) { - svc := cdp.cfg.Proxy + svc := cdp.cfg.Service envoy := cdp.cfg.Envoy req := &pbdataplane.GetEnvoyBootstrapParamsRequest{ - ServiceId: svc.ProxyID, - ProxyId: svc.ProxyID, + ServiceId: svc.ServiceID, Namespace: svc.Namespace, Partition: svc.Partition, } @@ -62,7 +60,7 @@ func (cdp *ConsulDataplane) bootstrapConfig(ctx context.Context) (*bootstrap.Boo AgentTLS: false, }, ProxyCluster: rsp.Service, - ProxyID: svc.ProxyID, + ProxyID: svc.ServiceID, NodeName: rsp.NodeName, ProxySourceService: rsp.Service, AdminAccessLogConfig: rsp.AccessLogs, @@ -78,11 +76,6 @@ func (cdp *ConsulDataplane) bootstrapConfig(ctx context.Context) (*bootstrap.Boo PrometheusScrapePath: prom.ScrapePath, } - if rsp.Identity != "" { - args.ProxyCluster = rsp.Identity - args.ProxySourceService = rsp.Identity - } - if cdp.xdsServer.listenerNetwork == "unix" { args.GRPC.AgentSocket = cdp.xdsServer.listenerAddress } else { @@ -109,12 +102,8 @@ func (cdp *ConsulDataplane) bootstrapConfig(ctx context.Context) (*bootstrap.Boo } if cdp.cfg.Telemetry.UseCentralConfig { - if rsp.BootstrapConfig != nil { - bootstrapConfig = bootstrapConfigFromCfg(rsp.BootstrapConfig) - } else { - if err := mapstructure.WeakDecode(rsp.Config.AsMap(), &bootstrapConfig); err != nil { - return nil, nil, fmt.Errorf("failed parsing Proxy.Config: %w", err) - } + if err := mapstructure.WeakDecode(rsp.Config.AsMap(), &bootstrapConfig); err != nil { + return nil, nil, fmt.Errorf("failed parsing Proxy.Config: %w", err) } // Envoy is configured with a listener that proxies metrics from its @@ -127,26 +116,7 @@ func (cdp *ConsulDataplane) bootstrapConfig(ctx context.Context) (*bootstrap.Boo } // Note: we pass true for omitDeprecatedTags here - consul-dataplane is clean - // slate, and we don't need to maintain this legacy behavior. + // slate and we don't need to maintain this legacy behavior. cfg, err := bootstrapConfig.GenerateJSON(args, true) return &bootstrapConfig, cfg, err } - -func bootstrapConfigFromCfg(cfg *pbmesh.BootstrapConfig) bootstrap.BootstrapConfig { - return bootstrap.BootstrapConfig{ - StatsdURL: cfg.StatsdUrl, - DogstatsdURL: cfg.DogstatsdUrl, - StatsTags: cfg.StatsTags, - TelemetryCollectorBindSocketDir: cfg.TelemetryCollectorBindSocketDir, - PrometheusBindAddr: cfg.PrometheusBindAddr, - StatsBindAddr: cfg.StatsBindAddr, - ReadyBindAddr: cfg.ReadyBindAddr, - OverrideJSONTpl: cfg.OverrideJsonTpl, - StaticClustersJSON: cfg.StaticClustersJson, - StaticListenersJSON: cfg.StaticListenersJson, - StatsSinksJSON: cfg.StatsSinksJson, - StatsConfigJSON: cfg.StatsConfigJson, - StatsFlushInterval: cfg.StatsFlushInterval, - TracingConfigJSON: cfg.TracingConfigJson, - } -} diff --git a/pkg/consuldp/bootstrap_test.go b/pkg/consuldp/bootstrap_test.go index 28f6be2c..09a30f56 100644 --- a/pkg/consuldp/bootstrap_test.go +++ b/pkg/consuldp/bootstrap_test.go @@ -15,7 +15,6 @@ import ( "testing" "github.com/hashicorp/consul/proto-public/pbdataplane" - pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/structpb" @@ -40,15 +39,14 @@ func TestBootstrapConfig(t *testing.T) { } testCases := map[string]struct { - cfg *Config - rsp *pbdataplane.GetEnvoyBootstrapParamsResponse - rspV2 *pbdataplane.GetEnvoyBootstrapParamsResponse + cfg *Config + rsp *pbdataplane.GetEnvoyBootstrapParamsResponse }{ "access-logs": { &Config{ - Proxy: &ProxyConfig{ - ProxyID: "web-proxy", - NodeName: nodeName, + Service: &ServiceConfig{ + ServiceID: "web-proxy", + NodeName: nodeName, }, Envoy: &EnvoyConfig{ AdminBindAddress: "127.0.0.1", @@ -67,20 +65,12 @@ func TestBootstrapConfig(t *testing.T) { }), AccessLogs: []string{"{\"name\":\"Consul Listener Filter Log\",\"typedConfig\":{\"@type\":\"type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog\",\"logFormat\":{\"jsonFormat\":{\"custom_field\":\"%START_TIME%\"}}}}"}, }, - &pbdataplane.GetEnvoyBootstrapParamsResponse{ - Identity: "web", - NodeName: nodeName, - BootstrapConfig: &pbmesh.BootstrapConfig{ - DogstatsdUrl: "this-should-not-appear-in-generated-config", - }, - AccessLogs: []string{"{\"name\":\"Consul Listener Filter Log\",\"typedConfig\":{\"@type\":\"type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog\",\"logFormat\":{\"jsonFormat\":{\"custom_field\":\"%START_TIME%\"}}}}"}, - }, }, "basic": { &Config{ - Proxy: &ProxyConfig{ - ProxyID: "web-proxy", - NodeName: nodeName, + Service: &ServiceConfig{ + ServiceID: "web-proxy", + NodeName: nodeName, }, Envoy: &EnvoyConfig{ AdminBindAddress: "127.0.0.1", @@ -98,19 +88,12 @@ func TestBootstrapConfig(t *testing.T) { "envoy_dogstatsd_url": "this-should-not-appear-in-generated-config", }), }, - &pbdataplane.GetEnvoyBootstrapParamsResponse{ - Identity: "web", - NodeName: nodeName, - BootstrapConfig: &pbmesh.BootstrapConfig{ - DogstatsdUrl: "this-should-not-appear-in-generated-config", - }, - }, }, "central-telemetry-config": { &Config{ - Proxy: &ProxyConfig{ - ProxyID: "web-proxy", - NodeName: nodeName, + Service: &ServiceConfig{ + ServiceID: "web-proxy", + NodeName: nodeName, }, Envoy: &EnvoyConfig{ AdminBindAddress: "127.0.0.1", @@ -128,18 +111,11 @@ func TestBootstrapConfig(t *testing.T) { "envoy_dogstatsd_url": "udp://127.0.0.1:9125", }), }, - &pbdataplane.GetEnvoyBootstrapParamsResponse{ - Identity: "web", - NodeName: nodeName, - BootstrapConfig: &pbmesh.BootstrapConfig{ - DogstatsdUrl: "udp://127.0.0.1:9125", - }, - }, }, "hcp-metrics": { cfg: &Config{ - Proxy: &ProxyConfig{ - ProxyID: "web-proxy", + Service: &ServiceConfig{ + ServiceID: "web-proxy", NodeName: nodeName, Namespace: "default", }, @@ -160,20 +136,12 @@ func TestBootstrapConfig(t *testing.T) { "envoy_telemetry_collector_bind_socket_dir": "/tmp/consul/hcp-metrics", }), }, - rspV2: &pbdataplane.GetEnvoyBootstrapParamsResponse{ - Identity: "web", - Namespace: "default", - NodeName: nodeName, - BootstrapConfig: &pbmesh.BootstrapConfig{ - TelemetryCollectorBindSocketDir: "/tmp/consul/hcp-metrics", - }, - }, }, "custom-prometheus-scrape-path": { &Config{ - Proxy: &ProxyConfig{ - ProxyID: "web-proxy", - NodeName: nodeName, + Service: &ServiceConfig{ + ServiceID: "web-proxy", + NodeName: nodeName, }, Envoy: &EnvoyConfig{ AdminBindAddress: "127.0.0.1", @@ -195,19 +163,12 @@ func TestBootstrapConfig(t *testing.T) { "envoy_prometheus_bind_addr": "0.0.0.0:20200", }), }, - &pbdataplane.GetEnvoyBootstrapParamsResponse{ - Identity: "web", - NodeName: nodeName, - BootstrapConfig: &pbmesh.BootstrapConfig{ - PrometheusBindAddr: "0.0.0.0:20200", - }, - }, }, "ready-listener": { &Config{ - Proxy: &ProxyConfig{ - ProxyID: "web-proxy", - NodeName: nodeName, + Service: &ServiceConfig{ + ServiceID: "web-proxy", + NodeName: nodeName, }, Envoy: &EnvoyConfig{ AdminBindAddress: "127.0.0.1", @@ -224,16 +185,12 @@ func TestBootstrapConfig(t *testing.T) { Service: "web", NodeName: nodeName, }, - &pbdataplane.GetEnvoyBootstrapParamsResponse{ - Identity: "web", - NodeName: nodeName, - }, }, "unix-socket-xds-server": { &Config{ - Proxy: &ProxyConfig{ - ProxyID: "web-proxy", - NodeName: nodeName, + Service: &ServiceConfig{ + ServiceID: "web-proxy", + NodeName: nodeName, }, Envoy: &EnvoyConfig{ AdminBindAddress: "127.0.0.1", @@ -251,27 +208,19 @@ func TestBootstrapConfig(t *testing.T) { "envoy_dogstatsd_url": "this-should-not-appear-in-generated-config", }), }, - &pbdataplane.GetEnvoyBootstrapParamsResponse{ - Identity: "web", - NodeName: nodeName, - BootstrapConfig: &pbmesh.BootstrapConfig{ - DogstatsdUrl: "this-should-not-appear-in-generated-config", - }, - }, }, } for desc, tc := range testCases { - t.Run(desc+"-v1", func(t *testing.T) { + t.Run(desc, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) client := NewMockDataplaneServiceClient(t) client.EXPECT(). GetEnvoyBootstrapParams(mock.Anything, &pbdataplane.GetEnvoyBootstrapParamsRequest{ - NodeSpec: &pbdataplane.GetEnvoyBootstrapParamsRequest_NodeName{NodeName: tc.cfg.Proxy.NodeName}, - ServiceId: tc.cfg.Proxy.ProxyID, - ProxyId: tc.cfg.Proxy.ProxyID, - Namespace: tc.cfg.Proxy.Namespace, + NodeSpec: &pbdataplane.GetEnvoyBootstrapParamsRequest_NodeName{NodeName: tc.cfg.Service.NodeName}, + ServiceId: tc.cfg.Service.ServiceID, + Namespace: tc.cfg.Service.Namespace, }).Call. Return(tc.rsp, nil) @@ -292,48 +241,13 @@ func TestBootstrapConfig(t *testing.T) { golden(t, bsCfg) validateBootstrapConfig(t, bsCfg) }) - - t.Run(desc+"-v2", func(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - client := NewMockDataplaneServiceClient(t) - client.EXPECT(). - GetEnvoyBootstrapParams(mock.Anything, &pbdataplane.GetEnvoyBootstrapParamsRequest{ - NodeSpec: &pbdataplane.GetEnvoyBootstrapParamsRequest_NodeName{NodeName: tc.cfg.Proxy.NodeName}, - ServiceId: tc.cfg.Proxy.ProxyID, - ProxyId: tc.cfg.Proxy.ProxyID, - Namespace: tc.cfg.Proxy.Namespace, - }).Call. - Return(tc.rspV2, nil) - - dp := &ConsulDataplane{ - cfg: tc.cfg, - dpServiceClient: client, - } - - if strings.HasPrefix(tc.cfg.XDSServer.BindAddress, "unix://") { - dp.xdsServer = &xdsServer{listenerAddress: socketPath, listenerNetwork: "unix"} - } else { - dp.xdsServer = &xdsServer{listenerAddress: fmt.Sprintf("127.0.0.1:%d", xdsBindPort)} - } - - _, bsCfg, err := dp.bootstrapConfig(ctx) - require.NoError(t, err) - - golden(t, bsCfg) - validateBootstrapConfig(t, bsCfg) - }) } } func golden(t *testing.T, actual []byte) { t.Helper() - fileName := strings.TrimSuffix(t.Name(), "-v1") - fileName = strings.TrimSuffix(fileName, "-v2") - - goldenPath := filepath.Join("testdata", fileName+".golden") + goldenPath := filepath.Join("testdata", t.Name()+".golden") if *update { require.NoError(t, os.WriteFile(goldenPath, actual, 0644)) diff --git a/pkg/consuldp/config.go b/pkg/consuldp/config.go index b3177bfe..44d1d966 100644 --- a/pkg/consuldp/config.go +++ b/pkg/consuldp/config.go @@ -209,21 +209,21 @@ type LoggingConfig struct { LogJSON bool } -// ProxyConfig contains details of the proxy service instance. -type ProxyConfig struct { +// ServiceConfig contains details of the proxy service instance. +type ServiceConfig struct { // NodeName is the name of the node to which the proxy service instance is - // registered. Ignored in Consul Catalog V2. + // registered. NodeName string // NodeName is the ID of the node to which the proxy service instance is - // registered. Ignored in Consul Catalog V2. + // registered. NodeID string - // ProxyID is the ID of the proxy service instance or workload. - ProxyID string + // ServiceID is the ID of the proxy service instance. + ServiceID string // Namespace is the Consul Enterprise namespace in which the proxy service - // instance or workload is registered. + // instance is registered. Namespace string // Partition is the Consul Enterprise partition in which the proxy service - // instance or workload is registered. + // instance is registered. Partition string } @@ -291,12 +291,8 @@ type EnvoyConfig struct { ShutdownDrainListenersEnabled bool // ShutdownGracePeriodSeconds is the amount of time to wait after receiving a SIGTERM before terminating the proxy container. ShutdownGracePeriodSeconds int - // GracefulShutdownPath is the path on which the HTTP endpoint to initiate a graceful shutdown of Envoy is served. + // GracefulShutdownPath is the path on which the HTTP endpoint to initiate a graceful shutdown of Envoy is served GracefulShutdownPath string - // StartupGracePeriodSeconds is the amount of time to block application after startup for Envoy proxy to be ready. - StartupGracePeriodSeconds int - // GracefulStartupPath is the path where the HTTP endpoint to initiate a graceful startup of Envoy is served. - GracefulStartupPath string // GracefulPort is the port on which the HTTP server for graceful shutdown endpoints will be available. GracefulPort int // DumpEnvoyConfigOnExitEnabled configures whether to call Envoy's /config_dump endpoint during consul-dataplane controlled shutdown. @@ -318,7 +314,7 @@ type XDSServer struct { type Config struct { DNSServer *DNSServerConfig Consul *ConsulConfig - Proxy *ProxyConfig + Service *ServiceConfig Logging *LoggingConfig Telemetry *TelemetryConfig Envoy *EnvoyConfig diff --git a/pkg/consuldp/consul_dataplane.go b/pkg/consuldp/consul_dataplane.go index 16694bd2..a1e3c331 100644 --- a/pkg/consuldp/consul_dataplane.go +++ b/pkg/consuldp/consul_dataplane.go @@ -77,10 +77,12 @@ func validateConfig(cfg *Config) error { return errors.New("consul addresses not specified") case cfg.Consul.GRPCPort == 0: return errors.New("consul server gRPC port not specified") - case cfg.Proxy == nil: - return errors.New("proxy details not specified") - case cfg.Proxy.ProxyID == "": - return errors.New("proxy ID not specified") + case cfg.Service == nil: + return errors.New("service details not specified") + case cfg.Service.NodeID == "" && cfg.Service.NodeName == "": + return errors.New("node name or ID not specified") + case cfg.Service.ServiceID == "": + return errors.New("proxy service ID not specified") case cfg.Envoy == nil: return errors.New("envoy settings not specified") case cfg.Envoy.AdminBindAddress == "": diff --git a/pkg/consuldp/consul_dataplane_test.go b/pkg/consuldp/consul_dataplane_test.go index d4d6d37a..8ab2bcdd 100644 --- a/pkg/consuldp/consul_dataplane_test.go +++ b/pkg/consuldp/consul_dataplane_test.go @@ -22,9 +22,9 @@ func validConfig() *Config { }, }, }, - Proxy: &ProxyConfig{ - NodeName: "agentless-node", - ProxyID: "web-proxy", + Service: &ServiceConfig{ + NodeName: "agentless-node", + ServiceID: "web-proxy", }, Logging: &LoggingConfig{ LogLevel: "INFO", @@ -88,14 +88,30 @@ func TestNewConsulDPError(t *testing.T) { expectErr: "consul server gRPC port not specified", }, { - name: "missing proxy config", - modFn: func(c *Config) { c.Proxy = nil }, - expectErr: "proxy details not specified", + name: "missing service config", + modFn: func(c *Config) { c.Service = nil }, + expectErr: "service details not specified", }, { - name: "missing proxy id", - modFn: func(c *Config) { c.Proxy.ProxyID = "" }, - expectErr: "proxy ID not specified", + name: "missing node details", + modFn: func(c *Config) { + c.Service.NodeName = "" + c.Service.NodeID = "" + }, + expectErr: "node name or ID not specified", + }, + { + name: "missing node details", + modFn: func(c *Config) { + c.Service.NodeName = "" + c.Service.NodeID = "" + }, + expectErr: "node name or ID not specified", + }, + { + name: "missing service id", + modFn: func(c *Config) { c.Service.ServiceID = "" }, + expectErr: "proxy service ID not specified", }, { name: "missing envoy config", diff --git a/pkg/consuldp/lifecycle.go b/pkg/consuldp/lifecycle.go index beb00464..3efc305d 100644 --- a/pkg/consuldp/lifecycle.go +++ b/pkg/consuldp/lifecycle.go @@ -9,7 +9,6 @@ import ( "net/http" "strconv" "sync" - "sync/atomic" "time" "github.com/hashicorp/go-hclog" @@ -25,7 +24,6 @@ const ( cdpLifecycleUrl = "http://" + cdpLifecycleBindAddr defaultLifecycleShutdownPath = "/graceful_shutdown" - defaultLifecycleStartupPath = "/graceful_startup" ) // lifecycleConfig handles all configuration related to managing the Envoy proxy @@ -38,9 +36,8 @@ type lifecycleConfig struct { shutdownGracePeriodSeconds int gracefulPort int gracefulShutdownPath string - startupGracePeriodSeconds int - gracefulStartupPath string - dumpEnvoyConfigOnExitEnabled bool + + dumpEnvoyConfigOnExitEnabled bool // manager for controlling the Envoy proxy process proxy envoy.ProxyManager @@ -61,9 +58,8 @@ func NewLifecycleConfig(cfg *Config, proxy envoy.ProxyManager) *lifecycleConfig gracefulPort: cfg.Envoy.GracefulPort, gracefulShutdownPath: cfg.Envoy.GracefulShutdownPath, dumpEnvoyConfigOnExitEnabled: cfg.Envoy.DumpEnvoyConfigOnExitEnabled, - startupGracePeriodSeconds: cfg.Envoy.StartupGracePeriodSeconds, - gracefulStartupPath: cfg.Envoy.GracefulStartupPath, - proxy: proxy, + + proxy: proxy, errorExitCh: make(chan struct{}, 1), mu: sync.Mutex{}, @@ -88,11 +84,18 @@ func (m *lifecycleConfig) startLifecycleManager(ctx context.Context) error { // management control mux := http.NewServeMux() - m.logger.Info(fmt.Sprintf("setting graceful shutdown path: %s\n", m.shutdownPath())) - mux.HandleFunc(m.shutdownPath(), m.gracefulShutdownHandler) + // Determine what HTTP endpoint paths to configure for the proxy lifecycle + // management server. These can be set as flags. + cdpLifecycleShutdownPath := defaultLifecycleShutdownPath + if m.gracefulShutdownPath != "" { + cdpLifecycleShutdownPath = m.gracefulShutdownPath + } - m.logger.Info(fmt.Sprintf("setting graceful startup path: %s\n", m.startupPath())) - mux.HandleFunc(m.startupPath(), m.gracefulStartupHandler) + // Set config to allow introspection of default path for testing + m.gracefulShutdownPath = cdpLifecycleShutdownPath + + m.logger.Info(fmt.Sprintf("setting graceful shutdown path: %s\n", cdpLifecycleShutdownPath)) + mux.HandleFunc(cdpLifecycleShutdownPath, m.gracefulShutdownHandler) // Determine what the proxy lifecycle management server bind port is. It can be // set as a flag. @@ -164,15 +167,6 @@ func (m *lifecycleConfig) gracefulShutdown() { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() - if m.dumpEnvoyConfigOnExitEnabled { - m.logger.Info("dumping Envoy config to disk") - err := m.proxy.DumpConfig() - if err != nil { - m.logger.Warn("error while attempting to dump Envoy config to disk", "error", err) - close(m.errorExitCh) - } - } - m.logger.Info(fmt.Sprintf("waiting %d seconds before terminating dataplane proxy", m.shutdownGracePeriodSeconds)) var wg sync.WaitGroup @@ -208,60 +202,3 @@ func (m *lifecycleConfig) gracefulShutdown() { // Wait for context timeout to elapse wg.Wait() } - -func (m *lifecycleConfig) gracefulStartupHandler(rw http.ResponseWriter, _ *http.Request) { - //Unlike in gracefulShutdown, we want to delay the OK response until envoy is ready - //in order to block application container. - m.gracefulStartup() - rw.WriteHeader(http.StatusOK) -} - -// gracefulStartup blocks until the startup grace period has elapsed or we have confirmed that -// Envoy proxy is ready. -func (m *lifecycleConfig) gracefulStartup() { - if m.startupGracePeriodSeconds == 0 { - return - } - - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(m.startupGracePeriodSeconds)*time.Second) - defer cancel() - - var ready atomic.Bool - go func() { - for ctx.Err() == nil { - r, err := m.proxy.Ready() - if err != nil { - m.logger.Info(fmt.Sprintf("error when querying proxy readiness, %s", err.Error())) - } - if r { - ready.Store(true) - cancel() - break - } - time.Sleep(50 * time.Millisecond) - } - }() - - <-ctx.Done() - if !ready.Load() { - m.logger.Warn("grace period elapsed before proxy ready") - } -} - -func (m *lifecycleConfig) shutdownPath() string { - if m.gracefulShutdownPath == "" { - // Set config to allow introspection of default path for testing - m.gracefulShutdownPath = defaultLifecycleShutdownPath - } - - return m.gracefulShutdownPath -} - -func (m *lifecycleConfig) startupPath() string { - if m.gracefulStartupPath == "" { - // Set config to allow introspection of default path for testing - m.gracefulStartupPath = defaultLifecycleStartupPath - } - - return m.gracefulStartupPath -} diff --git a/pkg/consuldp/lifecycle_test.go b/pkg/consuldp/lifecycle_test.go index 24d1a280..6ead73da 100644 --- a/pkg/consuldp/lifecycle_test.go +++ b/pkg/consuldp/lifecycle_test.go @@ -21,8 +21,6 @@ var ( envoyAdminAddr = "127.0.0.1" ) -// TestLifecycleServerClosed tests that the lifecycle manager properly starts up -// and shuts down when the context passed into it is cancelled. func TestLifecycleServerClosed(t *testing.T) { cfg := Config{ Envoy: &EnvoyConfig{ @@ -40,156 +38,35 @@ func TestLifecycleServerClosed(t *testing.T) { require.Eventually(t, func() bool { return !m.running }, time.Second*2, time.Second) -} - -// TestLifecycleServer_Startup the graceful startup functionality of the dataplane -// using different grace period and simulated startup duration configurations. -func TestLifecycleServer_Startup(t *testing.T) { - cases := map[string]struct { - startupGracePeriodSeconds int - gracefulStartupPath string - gracefulPort int - proxyStartupDelaySeconds int - }{ - "startup grace period with default path, no startup time": { - startupGracePeriodSeconds: 5, - }, - "startup time with default path, no grace period": { - proxyStartupDelaySeconds: 5, - }, - "startup time and grace period with default path, grace period > startup time": { - startupGracePeriodSeconds: 10, - proxyStartupDelaySeconds: 5, - }, - "startup time and grace period with default path, grace period < startup time": { - startupGracePeriodSeconds: 5, - proxyStartupDelaySeconds: 10, - }, - "startup time and grace period with custom path, grace period < startup time": { - startupGracePeriodSeconds: 5, - proxyStartupDelaySeconds: 10, - gracefulStartupPath: "/custom_startup", - }, - } - for name, c := range cases { - c := c - log.Printf("config = %v", c) - - t.Run(name, func(t *testing.T) { - // Add a small margin of error for assertions checking expected - // behavior within the shutdown grace period window. - - cfg := Config{ - Envoy: &EnvoyConfig{ - AdminBindAddress: envoyAdminAddr, - AdminBindPort: envoyAdminPort, - GracefulPort: c.gracefulPort, - GracefulStartupPath: c.gracefulStartupPath, - StartupGracePeriodSeconds: c.startupGracePeriodSeconds, - }, - } - m := NewLifecycleConfig(&cfg, &mockProxy{ - startupDelaySeconds: c.proxyStartupDelaySeconds, - }) - - require.NotNil(t, m) - require.NotNil(t, m.proxy) - require.NotNil(t, m.errorExitCh) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - err := m.startLifecycleManager(ctx) - require.NoError(t, err) - - // Have consul-dataplane's lifecycle server start on an open port - // and figure out what port was used so we can make requests to it. - // Conveniently, this seems to wait until the server is ready for requests. - portCh := make(chan int, 1) - if c.gracefulPort == 0 { - m.lifecycleServer.Addr = "127.0.0.1:0" - } - m.lifecycleServer.BaseContext = func(l net.Listener) context.Context { - portCh <- l.Addr().(*net.TCPAddr).Port - return context.Background() - } - - var port int - select { - case port = <-portCh: - case <-time.After(5 * time.Second): - } - - // Check lifecycle server graceful port configuration - if c.gracefulPort != 0 { - require.Equal(t, c.gracefulPort, port, "failed to set lifecycle server port") - } else { - require.NotEqual(t, 0, port, "failed to figure out lifecycle server port") - } - log.Printf("port = %v\n", port) - - // Check lifecycle server graceful startup path configuration - if c.gracefulStartupPath != "" { - require.Equal(t, m.gracefulStartupPath, c.gracefulStartupPath, "failed to set lifecycle server graceful startup HTTP endpoint path") - } - startupURL := fmt.Sprintf("http://127.0.0.1:%d%s", port, m.gracefulStartupPath) - - // Start the mock proxy. - go func() { - fmt.Print("starting go func") - err := m.proxy.Run(ctx) - require.NoError(t, err) - fmt.Print("proxy should be running") - }() - start := time.Now() - log.Printf("sending startup check request to %s\n", startupURL) - resp, err := http.Get(startupURL) - require.NoError(t, err) - require.True(t, resp.StatusCode == 200) - duration := time.Since(start) - var expectedTime int - if c.proxyStartupDelaySeconds < c.startupGracePeriodSeconds { - expectedTime = c.proxyStartupDelaySeconds - } else { - expectedTime = c.startupGracePeriodSeconds - } - require.True(t, duration.Seconds()-float64(time.Duration(expectedTime)) < 1) - require.NoError(t, err) - require.NotNil(t, resp) - - body, err := io.ReadAll(resp.Body) - require.NoError(t, err) - require.NotNil(t, body) - }) - } } -// TestLifecycleServer_Shutdown the graceful shutdown functionality of the dataplane -// with different grace period and listener draining configurations. -func TestLifecycleServer_Shutdown(t *testing.T) { +func TestLifecycleServerEnabled(t *testing.T) { cases := map[string]struct { shutdownDrainListenersEnabled bool shutdownGracePeriodSeconds int gracefulShutdownPath string gracefulPort int }{ - "connection draining disabled without shutdown grace period": { + // TODO: testing the actual Envoy behavior here such as how open or new + // connections are handled should happpen in integration or acceptance tests + "connection draining disabled without grace period": { // All inbound and outbound connections are terminated immediately. }, - "connection draining enabled without shutdown grace period": { + "connection draining enabled without grace period": { // This should immediately send "Connection: close" to inbound HTTP1 // connections, GOAWAY to inbound HTTP2, and terminate connections on // request completion. Outbound connections should start being rejected // immediately. shutdownDrainListenersEnabled: true, }, - "connection draining disabled with shutdown grace period": { + "connection draining disabled with grace period": { // This should immediately terminate any open inbound connections. // Outbound connections should be allowed until the grace period has // elapsed. shutdownGracePeriodSeconds: 5, }, - "connection draining enabled with shutdown grace period": { + "connection draining enabled with grace period": { // This should immediately send "Connection: close" to inbound HTTP1 // connections, GOAWAY to inbound HTTP2, and terminate connections on // request completion. @@ -203,10 +80,11 @@ func TestLifecycleServer_Shutdown(t *testing.T) { shutdownDrainListenersEnabled: true, shutdownGracePeriodSeconds: 5, gracefulShutdownPath: "/quit-nicely", - gracefulPort: 23108, + // TODO: should this be random or use freeport? logic disallows passing + // zero value explicitly + gracefulPort: 23108, }, } - for name, c := range cases { c := c log.Printf("config = %v", c) @@ -267,16 +145,12 @@ func TestLifecycleServer_Shutdown(t *testing.T) { if c.gracefulShutdownPath != "" { require.Equal(t, m.gracefulShutdownPath, c.gracefulShutdownPath, "failed to set lifecycle server graceful shutdown HTTP endpoint path") } - shutdownUrl := fmt.Sprintf("http://127.0.0.1:%d%s", port, m.gracefulShutdownPath) - // Start the mock proxy. - go func() { - err := m.proxy.Run(ctx) - require.NoError(t, err) - }() + // Check lifecycle server graceful shutdown path configuration + url := fmt.Sprintf("http://127.0.0.1:%d%s", port, m.gracefulShutdownPath) + log.Printf("sending request to %s\n", url) - log.Printf("sending request to %s\n", shutdownUrl) - resp, err := http.Get(shutdownUrl) + resp, err := http.Get(url) // HTTP handler is not blocking, so need to wait and check mock // client for expected method calls to proxy manager within @@ -311,19 +185,14 @@ func TestLifecycleServer_Shutdown(t *testing.T) { } type mockProxy struct { - runCalled int - drainCalled int - quitCalled int - killCalled int - isReady bool - startupDelaySeconds int + runCalled int + drainCalled int + quitCalled int + killCalled int } func (p *mockProxy) Run(ctx context.Context) error { p.runCalled++ - time.Sleep(time.Duration(p.startupDelaySeconds) * time.Second) - p.isReady = true - return nil } @@ -344,6 +213,3 @@ func (p *mockProxy) Kill() error { func (p *mockProxy) DumpConfig() error { return nil } -func (p *mockProxy) Ready() (bool, error) { - return p.isReady, nil -} diff --git a/pkg/consuldp/metrics_test.go b/pkg/consuldp/metrics_test.go index 5666e184..3f443b69 100644 --- a/pkg/consuldp/metrics_test.go +++ b/pkg/consuldp/metrics_test.go @@ -170,7 +170,7 @@ func TestMetricsServerEnabled(t *testing.T) { require.IsType(t, &http.Client{}, m.client) require.Greater(t, m.client.(*http.Client).Timeout, time.Duration(0)) - // Mock get requests to Envoy and Proxy instance metrics + // Mock get requests to Envoy and Service instance metrics // so that they return a fake metric string. m.client = &mockClient{} diff --git a/pkg/consuldp/testdata/TestBootstrapConfig/access-logs.golden b/pkg/consuldp/testdata/TestBootstrapConfig/access-logs.golden index 8cb8627c..93788081 100644 --- a/pkg/consuldp/testdata/TestBootstrapConfig/access-logs.golden +++ b/pkg/consuldp/testdata/TestBootstrapConfig/access-logs.golden @@ -169,6 +169,12 @@ "api_type": "DELTA_GRPC", "transport_api_version": "V3", "grpc_services": { + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "" + } + ], "envoy_grpc": { "cluster_name": "consul-dataplane" } diff --git a/pkg/consuldp/testdata/TestBootstrapConfig/basic.golden b/pkg/consuldp/testdata/TestBootstrapConfig/basic.golden index 954706c5..4d415571 100644 --- a/pkg/consuldp/testdata/TestBootstrapConfig/basic.golden +++ b/pkg/consuldp/testdata/TestBootstrapConfig/basic.golden @@ -157,6 +157,12 @@ "api_type": "DELTA_GRPC", "transport_api_version": "V3", "grpc_services": { + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "" + } + ], "envoy_grpc": { "cluster_name": "consul-dataplane" } diff --git a/pkg/consuldp/testdata/TestBootstrapConfig/central-telemetry-config.golden b/pkg/consuldp/testdata/TestBootstrapConfig/central-telemetry-config.golden index a8275d2e..887415e5 100644 --- a/pkg/consuldp/testdata/TestBootstrapConfig/central-telemetry-config.golden +++ b/pkg/consuldp/testdata/TestBootstrapConfig/central-telemetry-config.golden @@ -171,6 +171,12 @@ "api_type": "DELTA_GRPC", "transport_api_version": "V3", "grpc_services": { + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "" + } + ], "envoy_grpc": { "cluster_name": "consul-dataplane" } diff --git a/pkg/consuldp/testdata/TestBootstrapConfig/custom-prometheus-scrape-path.golden b/pkg/consuldp/testdata/TestBootstrapConfig/custom-prometheus-scrape-path.golden index 5454b373..ab51a2ba 100644 --- a/pkg/consuldp/testdata/TestBootstrapConfig/custom-prometheus-scrape-path.golden +++ b/pkg/consuldp/testdata/TestBootstrapConfig/custom-prometheus-scrape-path.golden @@ -246,6 +246,12 @@ "api_type": "DELTA_GRPC", "transport_api_version": "V3", "grpc_services": { + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "" + } + ], "envoy_grpc": { "cluster_name": "consul-dataplane" } diff --git a/pkg/consuldp/testdata/TestBootstrapConfig/hcp-metrics.golden b/pkg/consuldp/testdata/TestBootstrapConfig/hcp-metrics.golden index 95b14442..01e90db0 100644 --- a/pkg/consuldp/testdata/TestBootstrapConfig/hcp-metrics.golden +++ b/pkg/consuldp/testdata/TestBootstrapConfig/hcp-metrics.golden @@ -195,6 +195,12 @@ "api_type": "DELTA_GRPC", "transport_api_version": "V3", "grpc_services": { + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "" + } + ], "envoy_grpc": { "cluster_name": "consul-dataplane" } diff --git a/pkg/consuldp/testdata/TestBootstrapConfig/ready-listener.golden b/pkg/consuldp/testdata/TestBootstrapConfig/ready-listener.golden index bd0c82c7..830c4f8e 100644 --- a/pkg/consuldp/testdata/TestBootstrapConfig/ready-listener.golden +++ b/pkg/consuldp/testdata/TestBootstrapConfig/ready-listener.golden @@ -246,6 +246,12 @@ "api_type": "DELTA_GRPC", "transport_api_version": "V3", "grpc_services": { + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "" + } + ], "envoy_grpc": { "cluster_name": "consul-dataplane" } diff --git a/pkg/consuldp/testdata/TestBootstrapConfig/unix-socket-xds-server.golden b/pkg/consuldp/testdata/TestBootstrapConfig/unix-socket-xds-server.golden index 62340b2a..7b1036b5 100644 --- a/pkg/consuldp/testdata/TestBootstrapConfig/unix-socket-xds-server.golden +++ b/pkg/consuldp/testdata/TestBootstrapConfig/unix-socket-xds-server.golden @@ -156,6 +156,12 @@ "api_type": "DELTA_GRPC", "transport_api_version": "V3", "grpc_services": { + "initial_metadata": [ + { + "key": "x-consul-token", + "value": "" + } + ], "envoy_grpc": { "cluster_name": "consul-dataplane" } diff --git a/pkg/envoy/get_process_attr.go b/pkg/envoy/get_process_attr.go deleted file mode 100644 index 89fc2576..00000000 --- a/pkg/envoy/get_process_attr.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -//go:build !windows -// +build !windows - -package envoy - -import "syscall" - -func getProcessAttr() *syscall.SysProcAttr { - return &syscall.SysProcAttr{ - Setpgid: true, - } -} diff --git a/pkg/envoy/get_process_attr_windows.go b/pkg/envoy/get_process_attr_windows.go deleted file mode 100644 index a347c96c..00000000 --- a/pkg/envoy/get_process_attr_windows.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -//go:build windows -// +build windows - -package envoy - -import "syscall" - -func getProcessAttr() *syscall.SysProcAttr { - return &syscall.SysProcAttr{ - CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, - } -} diff --git a/pkg/envoy/proxy.go b/pkg/envoy/proxy.go index 8b780077..21828b6e 100644 --- a/pkg/envoy/proxy.go +++ b/pkg/envoy/proxy.go @@ -13,6 +13,7 @@ import ( "os/exec" "strings" "sync/atomic" + "syscall" "time" "github.com/hashicorp/go-hclog" @@ -39,8 +40,6 @@ type ProxyManager interface { Drain() error Quit() error Kill() error - DumpConfig() error - Ready() (bool, error) } // Proxy manages an Envoy proxy process. @@ -151,7 +150,9 @@ func (p *Proxy) Run(ctx context.Context) error { // Start Envoy in its own process group to avoid directly receiving // SIGTERM intended for consul-dataplane, let proxy manager handle // graceful shutdown if configured. - p.cmd.SysProcAttr = getProcessAttr() + p.cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + } p.cfg.Logger.Debug("running envoy proxy", "command", strings.Join(p.cmd.Args, " ")) if err := p.cmd.Start(); err != nil { @@ -210,7 +211,10 @@ func (p *Proxy) Quit() error { envoyShutdownUrl := fmt.Sprintf("http://%s:%v/quitquitquit", p.cfg.AdminAddr, p.cfg.AdminBindPort) switch p.getState() { - case stateExited, stateStopped: + case stateExited: + // Nothing to do! + return nil + case stateStopped: // Nothing to do! return nil case stateDraining: @@ -262,45 +266,6 @@ func (p *Proxy) Kill() error { } } -// Dump Envoy config to disk. -func (p *Proxy) DumpConfig() error { - switch p.getState() { - case stateExited: - return errors.New("proxy must be running to dump config") - case stateStopped: - return errors.New("proxy must be running to dump config") - case stateDraining: - return p.dumpConfig() - case stateRunning: - return p.dumpConfig() - default: - return errors.New("proxy must be running to dump config") - } -} - -func (p *Proxy) dumpConfig() error { - envoyConfigDumpUrl := fmt.Sprintf("http://%s:%v/config_dump?include_eds", p.cfg.AdminAddr, p.cfg.AdminBindPort) - - rsp, err := p.client.Get(envoyConfigDumpUrl) - if err != nil { - p.cfg.Logger.Error("envoy: failed to dump config", "error", err) - return err - } - defer rsp.Body.Close() - - config, err := io.ReadAll(rsp.Body) - if err != nil { - p.cfg.Logger.Error("envoy: failed to dump config", "error", err) - return err - } - - if _, err := p.cfg.EnvoyOutputStream.Write(config); err != nil { - p.cfg.Logger.Error("envoy: failed to write config to output stream", "error", err) - } - - return err -} - // Exited returns a channel that is closed when the Envoy process exits. It can // be used to detect and act on process crashes. func (p *Proxy) Exited() chan error { return p.exitedCh } @@ -381,25 +346,3 @@ func removeArgAndGetValue(stringAr []string, key string) ([]string, string) { } return stringAr, "" } - -func (p *Proxy) Ready() (bool, error) { - - switch p.getState() { - case stateExited, stateStopped, stateDraining: - // Nothing to do! - return false, nil - case stateRunning, stateInitial: - // Query ready endpoint to check if proxy is Ready - envoyReadyURL := fmt.Sprintf("http://%s:%v/ready", p.cfg.AdminAddr, p.cfg.AdminBindPort) - rsp, err := p.client.Get(envoyReadyURL) - defer rsp.Body.Close() - if err != nil { - p.cfg.Logger.Error("envoy: admin endpoint not available", "error", err) - return false, err - } - return rsp.StatusCode == 200, nil - default: - return false, nil - } - -} diff --git a/pkg/version/version.go b/pkg/version/version.go index aad29c18..ca3e80c4 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -17,7 +17,7 @@ var ( // // Version must conform to the format expected by github.com/hashicorp/go-version // for tests to work. - Version = "1.3.0" + Version = "1.2.3" // 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