diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..302cfc42 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_size = 4 +indent_style = tab + +[*.{md,yml,yaml}] +indent_size = 2 +indent_style = space diff --git a/.github/labeler.yml b/.github/labeler.yml index dc366d98..b4337344 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,23 +1,27 @@ change: - - head-branch: ['^change/'] + - head-branch: ["^change/"] enhancement: - - head-branch: ['^feature/', '^feat/', '^enhancement/', '^enh/'] + - head-branch: ["^feature/", "^feat/", "^enhancement/", "^enh/"] bug: - - head-branch: ['^fix/', '^bug/'] + - head-branch: ["^fix/", "^bug/"] chore: - - head-branch: ['^chore/'] + - head-branch: ["^chore/"] tests: - - head-branch: ['^tests/', '^test/'] - - changed-files: 'tests/**/*' + - head-branch: ["^tests/", "^test/"] + - changed-files: + - any-glob-to-any-file: "tests/**/*" documentation: - - head-branch: ['^docs/', '^doc/'] - - changed-files: '**/*.md' + - head-branch: ["^docs/", "^doc/"] + - changed-files: + - any-glob-to-any-file: "**/*.md" dependencies: - - head-branch: ['^deps/', '^dep/', '^dependabot/'] - - changed-files: ['go.mod', 'go.sum'] + - head-branch: + ["^deps/", "^dep/", "^dependabot/", "pre-commit-ci-update-config"] + - changed-files: + - any-glob-to-any-file: ["go.mod", "go.sum"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33d9c81e..fa78f902 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,17 +29,22 @@ jobs: runs-on: ubuntu-22.04 permissions: contents: read + strategy: + fail-fast: false + matrix: + go-version: [stable] steps: - name: Checkout Repository uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup Golang Environment - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: - go-version-file: go.mod + go-version: ${{ matrix.go-version }} - name: Run Unit Tests run: make unit-test + build: name: Build Client runs-on: ubuntu-22.04 @@ -60,10 +65,10 @@ jobs: sed -i 's|\${NGINX_PLUS_VERSION}/||g' docker/Dockerfile - name: Set up Docker Buildx - uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 - name: Build Plus Docker Image - uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + uses: docker/build-push-action@94f8f8c2eec4bc3f1d78c1755580779804cb87b2 # v6.0.1 with: file: docker/Dockerfile tags: nginx-plus @@ -75,12 +80,13 @@ jobs: "nginx-repo.key=${{ secrets.NGINX_KEY }}" - name: Test Client - run: | - docker compose up -d - docker compose logs -f test test-no-stream + run: docker compose up test --exit-code-from test + + - name: Test Client No Stream + run: docker compose up test-no-stream --exit-code-from test-no-stream - name: Create/Update Draft - uses: lucacome/draft-release@785af55296512c907875513e397320ae3f1306bb # v1.0.1 + uses: lucacome/draft-release@8a63d32c79a171ae6048e614a8988f0ac3ed56d4 # v1.1.0 id: release-notes with: minor-label: "enhancement" @@ -90,12 +96,12 @@ jobs: if: ${{ github.event_name == 'push' }} - name: Setup Golang Environment - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: - go-version-file: go.mod + go-version: stable - name: Run GoReleaser - uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0 + uses: goreleaser/goreleaser-action@286f3b13b1b49da4ac219696163fb8c1c93e1200 # v6.0.0 with: version: latest args: release --clean diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 158e7f18..29cae928 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -42,7 +42,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -53,14 +53,14 @@ jobs: # queries: security-extended,security-and-quality - name: Setup Golang Environment - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: - go-version-file: go.mod + go-version: stable # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -73,6 +73,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index f3171a6e..21ca570b 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Dependabot metadata id: dependabot-metadata - uses: dependabot/fetch-metadata@v1.6.0 + uses: dependabot/fetch-metadata@v2.1.0 - name: Enable auto-merge for Dependabot PRs run: gh pr merge --auto --squash "$PR_URL" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 1075c8f3..bcaafe65 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -22,6 +22,6 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: "Dependency Review" - uses: actions/dependency-review-action@7bbfa034e752445ea40215fff1c3bf9597993d3f # v3.1.3 + uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3 with: config-file: "nginxinc/k8s-common/dependency-review-config.yml@main" diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml index a8d57589..3b1e67a1 100644 --- a/.github/workflows/fossa.yml +++ b/.github/workflows/fossa.yml @@ -24,6 +24,6 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Scan - uses: fossas/fossa-action@f61a4c0c263690f2ddb54b9822a719c25a7b608f # v1.3.1 + uses: fossas/fossa-action@47ef11b1e1e3812e88dae436ccbd2d0cbd1adab0 # v1.3.3 with: api-key: ${{ secrets.FOSSA_TOKEN }} diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 21249a07..d62990da 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -12,7 +12,7 @@ jobs: pull-requests: write # for actions/labeler to add labels runs-on: ubuntu-22.04 steps: - - uses: actions/labeler@4f052778de9a9b80cb16cfb9079b02287285a4cb # v5.0.0-alpha.1 + - uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5.0.0 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" sync-labels: true diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 846e8eb0..062b0f90 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,9 +4,10 @@ on: pull_request: branches: - main - paths-ignore: - - "**.md" - - "LICENSE" + +defaults: + run: + shell: bash concurrency: group: ${{ github.ref_name }}-lint @@ -24,12 +25,12 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Setup Golang Environment - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: - go-version-file: go.mod + go-version: stable - name: Lint Code - uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 + uses: golangci/golangci-lint-action@a4f60bb28d35aeee14e6880718e0c85ff1882e64 # v6.0.1 actionlint: name: Actionlint @@ -38,7 +39,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - uses: reviewdog/action-actionlint@82693e9e3b239f213108d6e412506f8b54003586 # v1.39.1 + - uses: reviewdog/action-actionlint@2927e858b45218240af952feb1d702cf6365f39a # v1.50.0 with: actionlint_flags: -shellcheck "" @@ -49,8 +50,20 @@ jobs: - name: Checkout Repository uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - uses: DavidAnson/markdownlint-cli2-action@ed4dec634fd2ef689c7061d5647371d8248064f1 # v13.0.0 + - uses: DavidAnson/markdownlint-cli2-action@b4c9feab76d8025d1e83c653fa3990936df0e6c8 # v16.0.0 with: config: .markdownlint-cli2.yaml globs: "**/*.md" fix: false + + yaml-lint: + name: YAML lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install yamllint + run: pip install yamllint + + - name: Lint YAML files + run: yamllint . diff --git a/.github/workflows/notifications.yml b/.github/workflows/notifications.yml index bf763678..c5bed0d1 100644 --- a/.github/workflows/notifications.yml +++ b/.github/workflows/notifications.yml @@ -23,7 +23,7 @@ jobs: actions: read # for 8398a7/action-slack steps: - name: Data - uses: actions/github-script@e69ef5462fd455e02edcaf4dd7708eda96b9eda0 # v7.0.0 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 continue-on-error: true id: data with: @@ -44,7 +44,7 @@ jobs: } - name: Send Notification - uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 # v3.15.1 + uses: 8398a7/action-slack@28ba43ae48961b90635b50953d216767a6bea486 # v3.16.2 with: status: custom custom_payload: | diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index f1cbb2a8..9beee6c7 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -1,6 +1,6 @@ name: OpenSSF Scorecards on: - branch_protection_rule: + branch_protection_rule: # yamllint disable-line rule:empty-values schedule: - cron: "42 15 * * 6" # run every Saturday at 15:42 UTC push: @@ -31,7 +31,7 @@ jobs: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3 with: results_file: results.sarif results_format: sarif @@ -49,7 +49,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: SARIF file path: results.sarif @@ -57,6 +57,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@0ba4244466797eb048eb91a6cd43d5c03ca8bd05 # v2.21.2 + uses: github/codeql-action/upload-sarif@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10 with: sarif_file: results.sarif diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 038bc50e..14d3a0c1 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -13,7 +13,7 @@ jobs: pull-requests: write # for actions/stale to close stale PRs runs-on: ubuntu-22.04 steps: - - uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v8.0.0 + - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: "This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days." diff --git a/.golangci.yml b/.golangci.yml index 5b59549e..bc1fa939 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -14,7 +14,6 @@ linters-settings: - name: error-strings - name: errorf - name: exported - - name: if-return - name: increment-decrement - name: indent-error-flow - name: package-comments @@ -28,32 +27,56 @@ linters-settings: - name: unused-parameter - name: var-declaration - name: var-naming - + govet: + enable-all: true linters: - enable: + enable: + - asasalint - asciicheck + - bidichk + - dupword - errcheck + - errname - errorlint + - exportloopref + - fatcontext + - forcetypeassert + - gocheckcompilerdirectives + - godot - gofmt - gofumpt - goimports - gosec - gosimple + - gosmopolitan - govet - ineffassign + - intrange - makezero - misspell - nilerr - noctx + - nolintlint + - perfsprint + - prealloc - predeclared + - reassign - revive - staticcheck + - stylecheck + - tagalign + - tenv + - thelper + - tparallel - typecheck - unconvert - unparam - unused + - usestdlibvars - wastedassign - disable-all: true + - whitespace + - wrapcheck + disable-all: true issues: max-issues-per-linter: 0 max-same-issues: 0 diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 05a2723d..79aefaa1 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -1,11 +1,12 @@ +version: 2 builds: - skip: true changelog: - skip: true + disable: true announce: slack: enabled: true - channel: '#announcements' - message_template: 'NGINX Plus Go Client {{ .Tag }} is out! Check it out: {{ .ReleaseURL }}' + channel: "#announcements" + message_template: "NGINX Plus Go Client {{ .Tag }} is out! Check it out: {{ .ReleaseURL }}" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9c7b97df..4d22c42f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,47 +1,37 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml - args: [--allow-multiple-documents] - - id: check-added-large-files - - id: check-merge-conflict - - id: check-shebang-scripts-are-executable - - id: check-case-conflict - - id: check-vcs-permalinks - - id: mixed-line-ending - args: [--fix=lf] - - id: no-commit-to-branch - - id: fix-byte-order-marker + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + args: [--allow-multiple-documents] + - id: check-added-large-files + - id: check-merge-conflict + - id: check-shebang-scripts-are-executable + - id: check-case-conflict + - id: check-vcs-permalinks + - id: mixed-line-ending + args: [--fix=lf] + - id: no-commit-to-branch + - id: fix-byte-order-marker -- repo: local - hooks: - - id: golang-diff - name: create-go-diff - entry: bash -c 'git diff -p origin/main > /tmp/diff.patch' - language: system - types: [go] - pass_filenames: false + - repo: https://github.com/golangci/golangci-lint + rev: v1.59.1 + hooks: + - id: golangci-lint-full -- repo: https://github.com/golangci/golangci-lint - rev: v1.53.3 - hooks: - - id: golangci-lint - args: [--new-from-patch=/tmp/diff.patch] + - repo: https://github.com/gitleaks/gitleaks + rev: v8.18.4 + hooks: + - id: gitleaks -- repo: https://github.com/gitleaks/gitleaks - rev: v8.17.0 - hooks: - - id: gitleaks - -- repo: https://github.com/DavidAnson/markdownlint-cli2 - rev: v0.8.1 - hooks: - - id: markdownlint-cli2 + - repo: https://github.com/DavidAnson/markdownlint-cli2 + rev: v0.13.0 + hooks: + - id: markdownlint-cli2 ci: - skip: [golang-diff, golangci-lint] + skip: [golangci-lint-full] diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 00000000..7d0320c4 --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,22 @@ +--- +yaml-files: + - "*.yaml" + - "*.yml" + +ignore-from-file: .gitignore + +extends: default + +rules: + comments: + min-spaces-from-content: 1 + comments-indentation: enable + document-start: disable + empty-values: enable + line-length: + max: 120 + ignore: | + .goreleaser.yml + .github/ + truthy: + check-keys: false diff --git a/CODEOWNERS b/CODEOWNERS index 99341504..77ed024e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @nginxinc/kic +* @nginxinc/integrations diff --git a/client/nginx.go b/client/nginx.go index 5c01a40c..0d8cce0e 100644 --- a/client/nginx.go +++ b/client/nginx.go @@ -37,14 +37,14 @@ var ( defaultWeight = 1 ) -// ErrUnsupportedVer means that client's API version is not supported by NGINX plus API +// ErrUnsupportedVer means that client's API version is not supported by NGINX plus API. var ErrUnsupportedVer = errors.New("API version of the client is not supported by running NGINX Plus") // NginxClient lets you access NGINX Plus API. type NginxClient struct { - apiVersion int - apiEndpoint string httpClient *http.Client + apiEndpoint string + apiVersion int checkAPI bool } @@ -54,38 +54,38 @@ type versions []int // UpstreamServer lets you configure HTTP upstreams. type UpstreamServer struct { - ID int `json:"id,omitempty"` - Server string `json:"server"` MaxConns *int `json:"max_conns,omitempty"` MaxFails *int `json:"max_fails,omitempty"` - FailTimeout string `json:"fail_timeout,omitempty"` - SlowStart string `json:"slow_start,omitempty"` - Route string `json:"route,omitempty"` Backup *bool `json:"backup,omitempty"` Down *bool `json:"down,omitempty"` - Drain bool `json:"drain,omitempty"` Weight *int `json:"weight,omitempty"` + Server string `json:"server"` + FailTimeout string `json:"fail_timeout,omitempty"` + SlowStart string `json:"slow_start,omitempty"` + Route string `json:"route,omitempty"` Service string `json:"service,omitempty"` + ID int `json:"id,omitempty"` + Drain bool `json:"drain,omitempty"` } // StreamUpstreamServer lets you configure Stream upstreams. type StreamUpstreamServer struct { - ID int `json:"id,omitempty"` - Server string `json:"server"` MaxConns *int `json:"max_conns,omitempty"` MaxFails *int `json:"max_fails,omitempty"` - FailTimeout string `json:"fail_timeout,omitempty"` - SlowStart string `json:"slow_start,omitempty"` Backup *bool `json:"backup,omitempty"` Down *bool `json:"down,omitempty"` Weight *int `json:"weight,omitempty"` + Server string `json:"server"` + FailTimeout string `json:"fail_timeout,omitempty"` + SlowStart string `json:"slow_start,omitempty"` Service string `json:"service,omitempty"` + ID int `json:"id,omitempty"` } type apiErrorResponse struct { - Error apiError RequestID string `json:"request_id"` Href string + Error apiError } func (resp *apiErrorResponse) toString() string { @@ -94,14 +94,14 @@ func (resp *apiErrorResponse) toString() string { } type apiError struct { - Status int Text string Code string + Status int } type internalError struct { - apiError err string + apiError } // Error allows internalError to match the Error interface. @@ -119,24 +119,24 @@ func (internalError *internalError) Wrap(err string) *internalError { // Stats represents NGINX Plus stats fetched from the NGINX Plus API. // https://nginx.org/en/docs/http/ngx_http_api_module.html type Stats struct { - NginxInfo NginxInfo - Caches Caches - Processes Processes - Connections Connections - Slabs Slabs - HTTPRequests HTTPRequests - SSL SSL - ServerZones ServerZones Upstreams Upstreams + ServerZones ServerZones StreamServerZones StreamServerZones StreamUpstreams StreamUpstreams - StreamZoneSync *StreamZoneSync - LocationZones LocationZones - Resolvers Resolvers - HTTPLimitRequests HTTPLimitRequests + Slabs Slabs + Caches Caches HTTPLimitConnections HTTPLimitConnections StreamLimitConnections StreamLimitConnections + HTTPLimitRequests HTTPLimitRequests + Resolvers Resolvers + LocationZones LocationZones + StreamZoneSync *StreamZoneSync Workers []*Workers + NginxInfo NginxInfo + SSL SSL + Connections Connections + HTTPRequests HTTPRequests + Processes Processes } // NginxInfo contains general information about NGINX Plus. @@ -144,17 +144,17 @@ type NginxInfo struct { Version string Build string Address string - Generation uint64 LoadTimestamp string `json:"load_timestamp"` Timestamp string + Generation uint64 ProcessID uint64 `json:"pid"` ParentProcessID uint64 `json:"ppid"` } -// Caches is a map of cache stats by cache zone +// Caches is a map of cache stats by cache zone. type Caches = map[string]HTTPCache -// HTTPCache represents a zone's HTTP Cache +// HTTPCache represents a zone's HTTP Cache. type HTTPCache struct { Size uint64 MaxSize uint64 `json:"max_size"` @@ -194,8 +194,8 @@ type Slabs map[string]Slab // Slab represents slab related stats. type Slab struct { - Pages Pages Slots Slots + Pages Pages } // Pages represents the slab memory usage stats. @@ -204,7 +204,7 @@ type Pages struct { Free uint64 } -// Slots is a map of slots by slot size +// Slots is a map of slots by slot size. type Slots map[string]Slot // Slot represents slot related stats. @@ -241,7 +241,7 @@ type VerifyFailures struct { Other uint64 `json:"other"` } -// ServerZones is map of server zone stats by zone name +// ServerZones is map of server zone stats by zone name. type ServerZones map[string]ServerZone // ServerZone represents server zone related stats. @@ -269,19 +269,19 @@ type StreamServerZone struct { SSL SSL } -// StreamZoneSync represents the sync information per each shared memory zone and the sync information per node in a cluster +// StreamZoneSync represents the sync information per each shared memory zone and the sync information per node in a cluster. type StreamZoneSync struct { Zones map[string]SyncZone Status StreamZoneSyncStatus } -// SyncZone represents the synchronization status of a shared memory zone +// SyncZone represents the synchronization status of a shared memory zone. type SyncZone struct { RecordsPending uint64 `json:"records_pending"` RecordsTotal uint64 `json:"records_total"` } -// StreamZoneSyncStatus represents the status of a shared memory zone +// StreamZoneSyncStatus represents the status of a shared memory zone. type StreamZoneSyncStatus struct { BytesIn uint64 `json:"bytes_in"` MsgsIn uint64 `json:"msgs_in"` @@ -301,7 +301,7 @@ type Responses struct { Total uint64 } -// HTTPCodes represents HTTP response codes +// HTTPCodes represents HTTP response codes. type HTTPCodes struct { HTTPContinue uint64 `json:"100,omitempty"` HTTPSwitchingProtocols uint64 `json:"101,omitempty"` @@ -358,11 +358,11 @@ type Upstreams map[string]Upstream // Upstream represents upstream related stats. type Upstream struct { + Zone string Peers []Peer + Queue Queue Keepalives int Zombies int - Zone string - Queue Queue } // StreamUpstreams is a map of stream upstream stats by upstream name. @@ -370,9 +370,9 @@ type StreamUpstreams map[string]StreamUpstream // StreamUpstream represents stream upstream related stats. type StreamUpstream struct { + Zone string Peers []StreamPeer Zombies int - Zone string } // Queue represents queue related stats for an upstream. @@ -384,54 +384,54 @@ type Queue struct { // Peer represents peer (upstream server) related stats. type Peer struct { - ID int Server string Service string Name string - Backup bool - Weight int + Selected string + Downstart string State string - Active uint64 + Responses Responses SSL SSL - MaxConns int `json:"max_conns"` + HealthChecks HealthChecks `json:"health_checks"` Requests uint64 - Responses Responses + ID int + MaxConns int `json:"max_conns"` Sent uint64 Received uint64 Fails uint64 Unavail uint64 - HealthChecks HealthChecks `json:"health_checks"` + Active uint64 Downtime uint64 - Downstart string - Selected string + Weight int HeaderTime uint64 `json:"header_time"` ResponseTime uint64 `json:"response_time"` + Backup bool } // StreamPeer represents peer (stream upstream server) related stats. type StreamPeer struct { - ID int Server string Service string Name string - Backup bool - Weight int + Selected string + Downstart string State string - Active uint64 SSL SSL - MaxConns int `json:"max_conns"` + HealthChecks HealthChecks `json:"health_checks"` Connections uint64 + Received uint64 + ID int ConnectTime int `json:"connect_time"` FirstByteTime int `json:"first_byte_time"` ResponseTime uint64 `json:"response_time"` Sent uint64 - Received uint64 + MaxConns int `json:"max_conns"` Fails uint64 Unavail uint64 - HealthChecks HealthChecks `json:"health_checks"` + Active uint64 Downtime uint64 - Downstart string - Selected string + Weight int + Backup bool } // HealthChecks represents health check related stats for a peer. @@ -442,13 +442,13 @@ type HealthChecks struct { LastPassed bool `json:"last_passed"` } -// LocationZones represents location_zones related stats +// LocationZones represents location_zones related stats. type LocationZones map[string]LocationZone -// Resolvers represents resolvers related stats +// Resolvers represents resolvers related stats. type Resolvers map[string]Resolver -// LocationZone represents location_zones related stats +// LocationZone represents location_zones related stats. type LocationZone struct { Requests int64 Responses Responses @@ -457,20 +457,20 @@ type LocationZone struct { Sent int64 } -// Resolver represents resolvers related stats +// Resolver represents resolvers related stats. type Resolver struct { Requests ResolverRequests `json:"requests"` Responses ResolverResponses `json:"responses"` } -// ResolverRequests represents resolver requests +// ResolverRequests represents resolver requests. type ResolverRequests struct { Name int64 Srv int64 Addr int64 } -// ResolverResponses represents resolver responses +// ResolverResponses represents resolver responses. type ResolverResponses struct { Noerror int64 Formerr int64 @@ -482,12 +482,12 @@ type ResolverResponses struct { Unknown int64 } -// Processes represents processes related stats +// Processes represents processes related stats. type Processes struct { Respawned int64 } -// HTTPLimitRequest represents HTTP Requests Rate Limiting +// HTTPLimitRequest represents HTTP Requests Rate Limiting. type HTTPLimitRequest struct { Passed uint64 Delayed uint64 @@ -496,23 +496,23 @@ type HTTPLimitRequest struct { RejectedDryRun uint64 `json:"rejected_dry_run"` } -// HTTPLimitRequests represents limit requests related stats +// HTTPLimitRequests represents limit requests related stats. type HTTPLimitRequests map[string]HTTPLimitRequest -// LimitConnection represents Connections Limiting +// LimitConnection represents Connections Limiting. type LimitConnection struct { Passed uint64 Rejected uint64 RejectedDryRun uint64 `json:"rejected_dry_run"` } -// HTTPLimitConnections represents limit connections related stats +// HTTPLimitConnections represents limit connections related stats. type HTTPLimitConnections map[string]LimitConnection -// StreamLimitConnections represents limit connections related stats +// StreamLimitConnections represents limit connections related stats. type StreamLimitConnections map[string]LimitConnection -// Workers represents worker connections related stats +// Workers represents worker connections related stats. type Workers struct { ID int ProcessID uint64 `json:"pid"` @@ -520,7 +520,7 @@ type Workers struct { Connections Connections } -// WorkersHTTP represents HTTP worker connections +// WorkersHTTP represents HTTP worker connections. type WorkersHTTP struct { HTTPRequests HTTPRequests `json:"requests"` } @@ -560,7 +560,7 @@ func NewNginxClient(apiEndpoint string, opts ...Option) (*NginxClient, error) { } if c.httpClient == nil { - return nil, fmt.Errorf("http client is not set") + return nil, errors.New("http client is not set") } if !versionSupported(c.apiVersion) { @@ -725,7 +725,7 @@ func (client *NginxClient) UpdateHTTPServers(upstream string, servers []Upstream } // We assume port 80 if no port is set for servers. - var formattedServers []UpstreamServer + formattedServers := make([]UpstreamServer, 0, len(servers)) for _, server := range servers { server.Server = addPortToServer(server.Server) formattedServers = append(formattedServers, server) @@ -757,7 +757,7 @@ func (client *NginxClient) UpdateHTTPServers(upstream string, servers []Upstream return toAdd, toDelete, toUpdate, nil } -// haveSameParameters checks if a given server has the same parameters as a server already present in NGINX. Order matters +// haveSameParameters checks if a given server has the same parameters as a server already present in NGINX. Order matters. func haveSameParameters(newServer UpstreamServer, serverNGX UpstreamServer) bool { newServer.ID = serverNGX.ID @@ -1036,7 +1036,7 @@ func (client *NginxClient) UpdateStreamServers(upstream string, servers []Stream return nil, nil, nil, fmt.Errorf("failed to update stream servers of %v upstream: %w", upstream, err) } - var formattedServers []StreamUpstreamServer + formattedServers := make([]StreamUpstreamServer, 0, len(servers)) for _, server := range servers { server.Server = addPortToServer(server.Server) formattedServers = append(formattedServers, server) @@ -1083,7 +1083,7 @@ func (client *NginxClient) getIDOfStreamServer(upstream string, name string) (in return -1, nil } -// haveSameParametersForStream checks if a given server has the same parameters as a server already present in NGINX. Order matters +// haveSameParametersForStream checks if a given server has the same parameters as a server already present in NGINX. Order matters. func haveSameParametersForStream(newServer StreamUpstreamServer, serverNGX StreamUpstreamServer) bool { newServer.ID = serverNGX.ID if serverNGX.MaxConns != nil && newServer.MaxConns == nil { @@ -1241,7 +1241,7 @@ func (client *NginxClient) GetStats() (*Stats, error) { streamZones := &StreamServerZones{} streamUpstreams := &StreamUpstreams{} limitConnsStream := &StreamLimitConnections{} - streamZoneSync := &StreamZoneSync{} + var streamZoneSync *StreamZoneSync if slices.Contains(endpoints, "stream") { streamEndpoints, err := client.GetAvailableStreamEndpoints() @@ -1330,7 +1330,7 @@ func (client *NginxClient) GetNginxInfo() (*NginxInfo, error) { return &info, nil } -// GetCaches returns Cache stats +// GetCaches returns Cache stats. func (client *NginxClient) GetCaches() (*Caches, error) { var caches Caches err := client.get("http/caches", &caches) @@ -1510,7 +1510,7 @@ func (client *NginxClient) getKeyValPairs(zone string, stream bool) (KeyValPairs base = "stream" } if zone == "" { - return nil, fmt.Errorf("zone required") + return nil, errors.New("zone required") } path := fmt.Sprintf("%v/keyvals/%v", base, zone) @@ -1563,7 +1563,7 @@ func (client *NginxClient) addKeyValPair(zone string, key string, val string, st base = "stream" } if zone == "" { - return fmt.Errorf("zone required") + return errors.New("zone required") } path := fmt.Sprintf("%v/keyvals/%v", base, zone) @@ -1591,7 +1591,7 @@ func (client *NginxClient) modifyKeyValPair(zone string, key string, val string, base = "stream" } if zone == "" { - return fmt.Errorf("zone required") + return errors.New("zone required") } path := fmt.Sprintf("%v/keyvals/%v", base, zone) @@ -1621,7 +1621,7 @@ func (client *NginxClient) deleteKeyValuePair(zone string, key string, stream bo base = "stream" } if zone == "" { - return fmt.Errorf("zone required") + return errors.New("zone required") } // map[string]string can't have a nil value so we use a different type here. @@ -1652,7 +1652,7 @@ func (client *NginxClient) deleteKeyValPairs(zone string, stream bool) error { base = "stream" } if zone == "" { - return fmt.Errorf("zone required") + return errors.New("zone required") } path := fmt.Sprintf("%v/keyvals/%v", base, zone) diff --git a/client/nginx_test.go b/client/nginx_test.go index bb901db0..e2e8ab3b 100644 --- a/client/nginx_test.go +++ b/client/nginx_test.go @@ -9,6 +9,7 @@ import ( ) func TestDetermineUpdates(t *testing.T) { + t.Parallel() maxConns := 1 tests := []struct { updated []UpstreamServer @@ -162,6 +163,7 @@ func TestDetermineUpdates(t *testing.T) { } func TestStreamDetermineUpdates(t *testing.T) { + t.Parallel() maxConns := 1 tests := []struct { updated []StreamUpstreamServer @@ -318,6 +320,7 @@ func TestStreamDetermineUpdates(t *testing.T) { } func TestAddPortToServer(t *testing.T) { + t.Parallel() // More info about addresses http://nginx.org/en/docs/http/ngx_http_upstream_module.html#server tests := []struct { address string @@ -370,6 +373,7 @@ func TestAddPortToServer(t *testing.T) { } func TestHaveSameParameters(t *testing.T) { + t.Parallel() tests := []struct { server UpstreamServer serverNGX UpstreamServer @@ -449,6 +453,7 @@ func TestHaveSameParameters(t *testing.T) { } func TestHaveSameParametersForStream(t *testing.T) { + t.Parallel() tests := []struct { server StreamUpstreamServer serverNGX StreamUpstreamServer @@ -523,8 +528,9 @@ func TestHaveSameParametersForStream(t *testing.T) { } func TestClientWithCheckAPI(t *testing.T) { + t.Parallel() // Create a test server that returns supported API versions - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { _, err := w.Write([]byte(`[4, 5, 6, 7, 8, 9]`)) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -552,6 +558,7 @@ func TestClientWithCheckAPI(t *testing.T) { } func TestClientWithAPIVersion(t *testing.T) { + t.Parallel() // Test creating a new client with a supported API version on the client client, err := NewNginxClient("http://api-url", WithAPIVersion(8)) if err != nil { @@ -572,6 +579,7 @@ func TestClientWithAPIVersion(t *testing.T) { } func TestClientWithHTTPClient(t *testing.T) { + t.Parallel() // Test creating a new client passing a custom HTTP client client, err := NewNginxClient("http://api-url", WithHTTPClient(&http.Client{})) if err != nil { @@ -592,6 +600,7 @@ func TestClientWithHTTPClient(t *testing.T) { } func TestGetStats_NoStreamEndpoint(t *testing.T) { + t.Parallel() ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.RequestURI == "/" { _, err := w.Write([]byte(`[4, 5, 6, 7, 8, 9]`)) @@ -637,12 +646,13 @@ func TestGetStats_NoStreamEndpoint(t *testing.T) { if !reflect.DeepEqual(stats.StreamUpstreams, StreamUpstreams{}) { t.Fatalf("StreamUpstreams: expected %v, actual %v", StreamUpstreams{}, stats.StreamUpstreams) } - if !reflect.DeepEqual(stats.StreamZoneSync, &StreamZoneSync{}) { - t.Fatalf("StreamZoneSync: expected %v, actual %v", &StreamZoneSync{}, stats.StreamZoneSync) + if stats.StreamZoneSync != nil { + t.Fatalf("StreamZoneSync: expected %v, actual %v", nil, stats.StreamZoneSync) } } func TestGetStats_SSL(t *testing.T) { + t.Parallel() ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.RequestURI == "/" { _, err := w.Write([]byte(`[4, 5, 6, 7, 8, 9]`)) diff --git a/compose.yaml b/compose.yaml index 6946feb0..d432656e 100644 --- a/compose.yaml +++ b/compose.yaml @@ -14,7 +14,8 @@ services: default: aliases: - nginx-plus-test - + ports: + - 8080 nginx-no-stream: extends: service: nginx diff --git a/docker/Dockerfile b/docker/Dockerfile index 5598846b..f4ddb379 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,9 +1,9 @@ -# syntax=docker/dockerfile:1.5 +# syntax=docker/dockerfile:1.7 FROM debian:12-slim LABEL maintainer="NGINX Docker Maintainers " -ARG NGINX_PLUS_VERSION=R30 +ARG NGINX_PLUS_VERSION=R32 # Install NGINX Plus # Download certificate and key from the customer portal (https://my.f5.com) diff --git a/release-process.md b/release-process.md new file mode 100644 index 00000000..2ecfbdd6 --- /dev/null +++ b/release-process.md @@ -0,0 +1,29 @@ +# Release Process + +This document outlines the steps involved in the release process for the NGINX Plus Go Client project. + +## Versioning + +The project follows [Semantic Versioning](https://semver.org/) for versioning. + +## Release Planning and Development + +The features that will go into the next release are reflected in the +corresponding [milestone](https://github.com/nginxinc/nginx-plus-go-client/milestones). Refer to +the [Issue Lifecycle](/ISSUE_LIFECYCLE.md) document for information on issue creation and assignment to releases. + +## Releasing a New Version + +1. Create an issue to define and track release-related activities. Choose a title that follows the + format `Release X.Y.Z`. +2. Stop merging any new work into the main branch. +3. Check the release draft under the [GitHub releases](https://github.com/nginxinc/nginx-plus-go-client/releases) page +to ensure that everything is in order. +4. Create and push the release tag in the format `vX.Y.Z`: + + ```bash + git tag -a vX.Y.Z -m "Release vX.Y.Z" + git push origin vX.Y.Z + ``` + + As a result, the CI/CD pipeline will publish the release and announce it in the community Slack. diff --git a/tests/client_test.go b/tests/client_test.go index f580ad71..627631e1 100644 --- a/tests/client_test.go +++ b/tests/client_test.go @@ -48,7 +48,6 @@ func TestStreamClient(t *testing.T) { // test adding a stream server err = c.AddStreamServer(streamUpstream, streamServer) - if err != nil { t.Fatalf("Error when adding a server: %v", err) } @@ -211,7 +210,6 @@ func TestStreamClient(t *testing.T) { // updating with 2 new servers, 1 existing added, deleted, updated, err = c.UpdateStreamServers(streamUpstream, streamServers2) - if err != nil { t.Fatalf("Error when updating servers: %v", err) } @@ -228,7 +226,6 @@ func TestStreamClient(t *testing.T) { // updating with zero servers - removing added, deleted, updated, err = c.UpdateStreamServers(streamUpstream, []client.StreamUpstreamServer{}) - if err != nil { t.Fatalf("Error when updating servers: %v", err) } @@ -326,7 +323,6 @@ func TestClient(t *testing.T) { // test adding a http server err = c.AddHTTPServer(upstream, server) - if err != nil { t.Fatalf("Error when adding a server: %v", err) } @@ -391,7 +387,6 @@ func TestClient(t *testing.T) { // updating with the same servers added, deleted, updated, err = c.UpdateHTTPServers(upstream, servers1) - if err != nil { t.Fatalf("Error when updating servers: %v", err) } @@ -484,7 +479,6 @@ func TestClient(t *testing.T) { // updating with 2 new servers, 1 existing added, deleted, updated, err = c.UpdateHTTPServers(upstream, servers2) - if err != nil { t.Fatalf("Error when updating servers: %v", err) } @@ -501,7 +495,6 @@ func TestClient(t *testing.T) { // updating with zero servers - removing added, deleted, updated, err = c.UpdateHTTPServers(upstream, []client.UpstreamServer{}) - if err != nil { t.Fatalf("Error when updating servers: %v", err) } @@ -1184,11 +1177,12 @@ func TestStreamZoneSync(t *testing.T) { } func compareUpstreamServers(x []client.UpstreamServer, y []client.UpstreamServer) bool { - var xServers []string + xServers := make([]string, 0, len(x)) for _, us := range x { xServers = append(xServers, us.Server) } - var yServers []string + + yServers := make([]string, 0, len(y)) for _, us := range y { yServers = append(yServers, us.Server) } @@ -1197,11 +1191,11 @@ func compareUpstreamServers(x []client.UpstreamServer, y []client.UpstreamServer } func compareStreamUpstreamServers(x []client.StreamUpstreamServer, y []client.StreamUpstreamServer) bool { - var xServers []string + xServers := make([]string, 0, len(x)) for _, us := range x { xServers = append(xServers, us.Server) } - var yServers []string + yServers := make([]string, 0, len(y)) for _, us := range y { yServers = append(yServers, us.Server) }