From 8b1e1ad15705ca7f8e79015fedeec47ca89a93fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:00:37 +0000 Subject: [PATCH] backport of commit 614cb5c17fa28df062426d59b30963510c6ace70 --- .changelog/24415.txt | 3 - .changelog/24642.txt | 3 - .github/pull_request_template.md | 35 ---- .github/workflows/build.yml | 14 +- .github/workflows/checks.yaml | 4 +- .github/workflows/ember-test-audit.yml | 87 ++++++++++ .github/workflows/release.yml | 4 +- .github/workflows/security-scan.yml | 2 +- .github/workflows/test-core.yaml | 6 +- .github/workflows/test-e2e.yml | 4 +- .github/workflows/test-ui.yml | 40 +---- .github/workflows/test-windows.yml | 2 +- CHANGELOG.md | 30 +--- CODEOWNERS | 12 -- .../allocrunner/networking_iptables_test.go | 2 - .../allocrunner/taskrunner/sids_hook_test.go | 4 +- .../taskrunner/task_runner_test.go | 2 - client/lib/numalib/detect_linux.go | 51 ++---- client/lib/numalib/detect_linux_test.go | 69 -------- client/lib/numalib/hw/speeds.go | 4 - dev/hooks/pre-push | 11 +- drivers/shared/executor/executor_test.go | 2 +- e2e/allocexec/docker_exec_test.go | 8 +- e2e/terraform/README.md | 14 +- e2e/terraform/terraform.tfvars | 11 +- e2e/ui/run.sh | 2 +- nomad/periodic_test.go | 37 ++-- nomad/structs/acl.go | 10 -- nomad/structs/actions.go | 4 +- nomad/structs/actions_test.go | 22 +-- nomad/structs/config/workload_id.go | 2 +- nomad/structs/event.go | 3 +- scripts/combine-ui-test-results.js | 51 ------ ui/app/index.html | 1 - ui/test-reporter.js | 159 ------------------ ui/testem.js | 23 --- ui/yarn.lock | 6 +- website/content/docs/concepts/security.mdx | 2 +- website/content/docs/enterprise/sentinel.mdx | 2 +- website/content/docs/job-specification/ui.mdx | 2 - website/package-lock.json | 8 +- website/package.json | 2 +- website/public/img/nomad-ui-block.png | Bin 18682 -> 0 bytes 43 files changed, 182 insertions(+), 578 deletions(-) delete mode 100644 .changelog/24415.txt delete mode 100644 .changelog/24642.txt delete mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/ember-test-audit.yml delete mode 100644 scripts/combine-ui-test-results.js delete mode 100644 ui/test-reporter.js delete mode 100644 website/public/img/nomad-ui-block.png diff --git a/.changelog/24415.txt b/.changelog/24415.txt deleted file mode 100644 index 2d158f8581c..00000000000 --- a/.changelog/24415.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:bug -client: fixed a bug where AMD CPUs were not correctly fingerprinting base speed -``` diff --git a/.changelog/24642.txt b/.changelog/24642.txt deleted file mode 100644 index ef21ae4afd1..00000000000 --- a/.changelog/24642.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:improvement -actions: Nomad Actions names now accept a wider range of names -``` diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 6f45b095e98..00000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,35 +0,0 @@ -### Description - - -### Testing & Reproduction steps - - -### Links - - -### Contributor Checklist -- [ ] **Changelog Entry** If this PR changes user-facing behavior, please generate and add a - changelog entry using the `make cl` command. -- [ ] **Testing** Please add tests to cover any new functionality or to demonstrate bug fixes and - ensure regressions will be caught. -- [ ] **Documentation** If the change impacts user-facing functionality such as the CLI, API, UI, - and job configuration, please update the Nomad website documentation to reflect this. Refer to - the [website README](../website/README.md) for docs guidelines. Please also consider whether the - change requires notes within the [upgrade guide](../website/content/docs/upgrade/upgrade-specific.mdx). - -### Reviewer Checklist -- [ ] **Backport Labels** Please add the correct backport labels as described by the internal - backporting document. -- [ ] **Commit Type** Ensure the correct merge method is selected which should be "squash and merge" - in the majority of situations. The main exceptions are long-lived feature branches or merges where - history should be preserved. -- [ ] **Enterprise PRs** If this is an enterprise only PR, please add any required changelog entry - within the public repository. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc64826b838..e2da17aca4f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -90,7 +90,7 @@ jobs: with: ref: ${{ github.event.inputs.build-ref }} - name: Setup go - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: ${{ needs.get-go-version.outputs.go-version }} @@ -98,7 +98,7 @@ jobs: run: make deps - name: Setup node and yarn - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: node-version: "18" cache-dependency-path: "ui/yarn.lock" @@ -142,7 +142,7 @@ jobs: with: ref: ${{ github.event.inputs.build-ref }} - name: Setup go - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: ${{ needs.get-go-version.outputs.go-version }} @@ -150,7 +150,7 @@ jobs: run: make deps - name: Setup node and yarn - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: node-version: "18" cache-dependency-path: "ui/yarn.lock" @@ -265,7 +265,7 @@ jobs: run: git config --global url.'https://${{ env.ELEVATED_GITHUB_TOKEN }}@github.com'.insteadOf 'https://github.com' - name: Setup go - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: ${{ needs.get-go-version.outputs.go-version }} @@ -273,7 +273,7 @@ jobs: run: make deps - name: Setup node and yarn - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: node-version: "18" cache-dependency-path: "ui/yarn.lock" @@ -356,7 +356,7 @@ jobs: goos: [linux] goarch: [amd64] steps: - - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: ${{needs.get-go-version.outputs.go-version}} - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index edfc984c173..ccb948ce35c 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -41,9 +41,9 @@ jobs: - name: Git config token if: endsWith(github.repository, '-enterprise') run: git config --global url.'https://${{ env.ELEVATED_GITHUB_TOKEN }}@github.com'.insteadOf 'https://github.com' - - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: - cache: ${{ contains(runner.name, 'Github Actions') }} + cache: true go-version-file: .go-version cache-dependency-path: '**/go.sum' - name: Run make check diff --git a/.github/workflows/ember-test-audit.yml b/.github/workflows/ember-test-audit.yml new file mode 100644 index 00000000000..6c1dce0a8c4 --- /dev/null +++ b/.github/workflows/ember-test-audit.yml @@ -0,0 +1,87 @@ +name: Ember test audit comparison +on: + pull_request: + paths: + - '.github/workflows/ember*' + - 'ui/**' + +defaults: + run: + working-directory: ui + +# There’s currently no way to share steps between jobs so there’s a lot of duplication +# for running the audit for the base and PR. +jobs: + time-base: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ github.event.pull_request.base.sha }} + - uses: nanasess/setup-chromedriver@42cc2998329f041de87dc3cfa33a930eacd57eaa # v2.2.2 + - name: Use Node.js + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 + with: + node-version: '18' + - run: yarn --frozen-lockfile + - run: mkdir -p /tmp/test-reports + - run: npx ember-test-audit 1 --json --output ../base-audit.json + - name: Upload result + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + with: + name: base-audit + path: base-audit.json + time-pr: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: nanasess/setup-chromedriver@42cc2998329f041de87dc3cfa33a930eacd57eaa # v2.2.2 + - name: Use Node.js + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 + with: + node-version: '18' + - run: yarn --frozen-lockfile + - run: mkdir -p /tmp/test-reports + - run: npx ember-test-audit 1 --json --output ../pr-audit.json + - name: Upload result + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + with: + name: pr-audit + path: pr-audit.json + compare: + needs: [time-base, time-pr] + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + name: base-audit + - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + name: pr-audit + - uses: backspace/ember-test-audit-comparison-action@21e9492d0033bc7e84b6189ae94537a6ed045cfa # v2 + with: + base-report-path: base-audit.json + comparison-report-path: pr-audit.json + base-identifier: ${{ github.event.pull_request.base.ref }} + comparison-identifier: ${{ github.event.pull_request.head.sha }} + timing-output-path: audit-diff.md + flakiness-output-path: flakiness-report.md + - uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 # v2.9.0 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + path: audit-diff.md + - name: Check for existence of flakiness report + id: check_file + uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 + with: + files: "flakiness-report.md" + - name: comment PR + if: steps.check_file.outputs.files_exists == 'true' + uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + message-path: flakiness-report.md +permissions: + contents: read + pull-requests: write diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 435986e6678..cab2b6062de 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -82,12 +82,12 @@ jobs: echo "go-version=$(cat .go-version)" >> "$GITHUB_OUTPUT" - name: Setup go - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version: ${{ steps.get-go-version.outputs.go-version }} - name: Setup node and yarn - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: node-version: "18" cache-dependency-path: "ui/yarn.lock" diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index a458be15f76..0b32e1e0601 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -37,7 +37,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: cache: ${{ contains(runner.name, 'Github Actions') }} go-version-file: .go-version diff --git a/.github/workflows/test-core.yaml b/.github/workflows/test-core.yaml index 9b006697d10..56972391358 100644 --- a/.github/workflows/test-core.yaml +++ b/.github/workflows/test-core.yaml @@ -59,7 +59,7 @@ jobs: timeout-minutes: 20 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: cache: ${{ contains(runner.name, 'Github Actions') }} go-version-file: .go-version @@ -74,7 +74,7 @@ jobs: timeout-minutes: 8 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: cache: true go-version-file: .go-version @@ -102,7 +102,7 @@ jobs: - quick steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: cache: ${{ contains(runner.name, 'Github Actions') }} go-version-file: .go-version diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 10a5dbc195d..ab08c3047b3 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -53,7 +53,7 @@ jobs: - name: Git config token if: endsWith(github.repository, '-enterprise') run: git config --global url.'https://${{ env.ELEVATED_GITHUB_TOKEN }}@github.com'.insteadOf 'https://github.com' - - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: cache: ${{ contains(runner.name, 'Github Actions') }} go-version-file: .go-version @@ -70,7 +70,7 @@ jobs: - name: Git config token if: endsWith(github.repository, '-enterprise') run: git config --global url.'https://${{ secrets.ELEVATED_GITHUB_TOKEN }}@github.com'.insteadOf 'https://github.com' - - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: cache: ${{ contains(runner.name, 'Github Actions') }} go-version-file: .go-version diff --git a/.github/workflows/test-ui.yml b/.github/workflows/test-ui.yml index 5478460877c..f52096c47ab 100644 --- a/.github/workflows/test-ui.yml +++ b/.github/workflows/test-ui.yml @@ -2,14 +2,14 @@ name: test-ui on: pull_request: paths: - - "ui/**" + - 'ui/**' push: branches: - main - release/** - test-ui paths: - - "ui/**" + - 'ui/**' jobs: pre-test: @@ -36,6 +36,7 @@ jobs: - pre-test runs-on: ${{ endsWith(github.repository, '-enterprise') && fromJSON('["self-hosted", "ondemand", "linux", "type=m7a.2xlarge;m6a.2xlarge"]') || 'ubuntu-latest' }} timeout-minutes: 30 + continue-on-error: true defaults: run: working-directory: ui @@ -43,8 +44,6 @@ jobs: matrix: partition: [1, 2, 3, 4] split: [4] - # Note: If we ever change the number of partitions, we'll need to update the - # finalize.combine step to match steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: ./.github/actions/setup-js @@ -64,19 +63,8 @@ jobs: env: PERCY_TOKEN: ${{ env.PERCY_TOKEN || secrets.PERCY_TOKEN }} PERCY_PARALLEL_NONCE: ${{ needs.pre-test.outputs.nonce }} - run: | - yarn exam:parallel --split=${{ matrix.split }} --partition=${{ matrix.partition }} --json-report=test-results/test-results.json - continue-on-error: true - - name: Express timeout failure - if: ${{ failure() }} - run: exit 1 - - name: Upload partition test results - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: test-results-${{ matrix.partition }} - path: ui/test-results/test-results.json - retention-days: 90 + run: yarn exam:parallel --split=${{ matrix.split }} --partition=${{ matrix.partition }} + finalize: needs: - pre-test @@ -100,24 +88,6 @@ jobs: jwtGithubAudience: ${{ vars.CI_VAULT_AUD }} secrets: |- kv/data/teams/nomad/ui PERCY_TOKEN ; - - name: Download all test results - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 - with: - pattern: test-results-* - path: test-results - - - name: Combine test results for comparison - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - run: node ../scripts/combine-ui-test-results.js - - name: Upload combined results for comparison - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: test-results-${{ github.sha }} - path: ui/combined-test-results.json - retention-days: 90 - - name: finalize env: PERCY_TOKEN: ${{ env.PERCY_TOKEN || secrets.PERCY_TOKEN }} diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 39dc4d93007..7d908185f65 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -52,7 +52,7 @@ jobs: - run: git config --global core.autocrlf false - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Setup go - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: ".go-version" - name: Show installed Go version diff --git a/CHANGELOG.md b/CHANGELOG.md index 3806cbb6e13..5c04911b4e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,18 +94,6 @@ BUG FIXES: * template: Fixed a panic on client restart when using change_mode=script [[GH-24057](https://github.com/hashicorp/nomad/issues/24057)] * ui: Fixes an issue where variables paths would not let namespaced users write variables unless they also had wildcard namespace variable write permissions [[GH-24073](https://github.com/hashicorp/nomad/issues/24073)] -## 1.8.7 Enterprise (November 8, 2024) - -SECURITY: - -* csi: Fixed a bug where a user with csi-write-volume permissions to one namespace can create volumes in another namespace (CVE-2024-10975) [[GH-24396](https://github.com/hashicorp/nomad/issues/24396)] - -BUG FIXES: - -* connect: add validation to ensure that connect native services specify a port [[GH-24329](https://github.com/hashicorp/nomad/issues/24329)] -* keyring: Fixed a panic on server startup when decrypting AEAD key data with empty RSA block [[GH-24383](https://github.com/hashicorp/nomad/issues/24383)] -* scheduler: fixed a bug where resource calculation did not account correctly for poststart tasks [[GH-24297](https://github.com/hashicorp/nomad/issues/24297)] - ## 1.8.6 Enterprise(October 21, 2024) IMPROVEMENTS: @@ -245,7 +233,7 @@ BUG FIXES: * server: Fixed a bug where expiring heartbeats for garbage collected nodes could panic the server [[GH-23383](https://github.com/hashicorp/nomad/issues/23383)] * template: Fix template rendering on Windows [[GH-23432](https://github.com/hashicorp/nomad/issues/23432)] * ui: Actions run from jobs with explicit name properties now work from the web UI [[GH-23553](https://github.com/hashicorp/nomad/issues/23553)] -* ui: Don't show keyboard nav hints when taking a screenshot [[GH-23365](https://github.com/hashicorp/nomad/issues/23365)] +* ui: Dont show keyboard nav hints when taking a screenshot [[GH-23365](https://github.com/hashicorp/nomad/issues/23365)] * ui: Fix an issue where a remotely purged job would prevent redirect from taking place in the web UI [[GH-23492](https://github.com/hashicorp/nomad/issues/23492)] * ui: Fix an issue where access to Job Templates in the UI was restricted to variable.write access [[GH-23458](https://github.com/hashicorp/nomad/issues/23458)] * ui: Fix the Upload Jobspec button on the Run Job page [[GH-23548](https://github.com/hashicorp/nomad/issues/23548)] @@ -342,18 +330,6 @@ BUG FIXES: * ui: Show the namespace in the web UI exec command hint [[GH-20218](https://github.com/hashicorp/nomad/issues/20218)] * windows: Fixed a regression where scanning task processes was inefficient [[GH-20619](https://github.com/hashicorp/nomad/issues/20619)] -## 1.7.15 (November 8, 2024) - -SECURITY: - -* csi: Fixed a bug where a user with csi-write-volume permissions to one namespace can create volumes in another namespace (CVE-2024-10975) [[GH-24396](https://github.com/hashicorp/nomad/issues/24396)] - -BUG FIXES: - -* connect: add validation to ensure that connect native services specify a port [[GH-24329](https://github.com/hashicorp/nomad/issues/24329)] -* deps: Fixed a bug where restarting Nomad could cause an unrelated process with the same PID as a failed executor to be killed [[GH-24265](https://github.com/hashicorp/nomad/issues/24265)] -* scheduler: fixed a bug where resource calculation did not account correctly for poststart tasks [[GH-24297](https://github.com/hashicorp/nomad/issues/24297)] - ## 1.7.14 Enterprise (October 21, 2024) IMPROVEMENTS: @@ -645,7 +621,7 @@ IMPROVEMENTS: * audit (Enterprise): Added ACL token role links to audit log auth objects [[GH-19415](https://github.com/hashicorp/nomad/issues/19415)] * ui: Added a new example template with Task Actions [[GH-19153](https://github.com/hashicorp/nomad/issues/19153)] -* ui: Don't allow new jobspec download until template is populated, and remove group count from jobs index [[GH-19377](https://github.com/hashicorp/nomad/issues/19377)] +* ui: dont allow new jobspec download until template is populated, and remove group count from jobs index [[GH-19377](https://github.com/hashicorp/nomad/issues/19377)] * ui: make the exec window look nicer on mobile screens [[GH-19332](https://github.com/hashicorp/nomad/issues/19332)] BUG FIXES: @@ -720,7 +696,7 @@ IMPROVEMENTS: * ui: for system and sysbatch jobs, now show client name on hover in job panel [[GH-19051](https://github.com/hashicorp/nomad/issues/19051)] * ui: nicer comment styles in UI example jobs [[GH-19037](https://github.com/hashicorp/nomad/issues/19037)] * ui: show plan output warnings alongside placement failures and dry-run info when running a job through the web ui [[GH-19225](https://github.com/hashicorp/nomad/issues/19225)] -* ui: simplify presentation of task event times (10m2.230948s becomes 10m2s etc.) [[GH-18595](https://github.com/hashicorp/nomad/issues/18595)] +* ui: simplify presentation of task event times (10m2.230948s bceomes 10m2s etc.) [[GH-18595](https://github.com/hashicorp/nomad/issues/18595)] * vars: Added a locking feature for Nomad Variables [[GH-18520](https://github.com/hashicorp/nomad/issues/18520)] DEPRECATIONS: diff --git a/CODEOWNERS b/CODEOWNERS index 1c47e5409da..73cfb9f9e0d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -2,15 +2,3 @@ /.release/ @hashicorp/github-nomad-core @hashicorp/nomad-eng /.github/workflows/build.yml @hashicorp/github-nomad-core @hashicorp/nomad-eng - -# codeowner default -* @hashicorp/github-nomad-core @hashicorp/nomad-eng - -# web presence - -/website/ @hashicorp/web-presence @hashicorp/github-nomad-core @hashicorp/nomad-eng - -# education - -/website/content/ @hashicorp/nomad-docs @hashicorp/web-presence @hashicorp/github-nomad-core @hashicorp/nomad-eng -/website/public/ @hashicorp/nomad-docs @hashicorp/web-presence @hashicorp/github-nomad-core @hashicorp/nomad-eng diff --git a/client/allocrunner/networking_iptables_test.go b/client/allocrunner/networking_iptables_test.go index fc9d4ce3fb5..c7751892cb9 100644 --- a/client/allocrunner/networking_iptables_test.go +++ b/client/allocrunner/networking_iptables_test.go @@ -1,8 +1,6 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 -//go:build linux - package allocrunner import ( diff --git a/client/allocrunner/taskrunner/sids_hook_test.go b/client/allocrunner/taskrunner/sids_hook_test.go index 35d0b88294c..0df13f3cb5d 100644 --- a/client/allocrunner/taskrunner/sids_hook_test.go +++ b/client/allocrunner/taskrunner/sids_hook_test.go @@ -1,8 +1,8 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 -//go:build linux -// +build linux +//go:build !windows +// +build !windows // todo(shoenig): Once Connect is supported on Windows, we'll need to make this // set of tests work there too. diff --git a/client/allocrunner/taskrunner/task_runner_test.go b/client/allocrunner/taskrunner/task_runner_test.go index 645b33c3bdd..3430e3a3e80 100644 --- a/client/allocrunner/taskrunner/task_runner_test.go +++ b/client/allocrunner/taskrunner/task_runner_test.go @@ -1,8 +1,6 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 -//go:build linux - package taskrunner import ( diff --git a/client/lib/numalib/detect_linux.go b/client/lib/numalib/detect_linux.go index aacb0bc6c31..1c697127953 100644 --- a/client/lib/numalib/detect_linux.go +++ b/client/lib/numalib/detect_linux.go @@ -30,18 +30,16 @@ func PlatformScanners() []SystemScanner { } const ( - sysRoot = "/sys/devices/system" - nodeOnline = sysRoot + "/node/online" - cpuOnline = sysRoot + "/cpu/online" - distanceFile = sysRoot + "/node/node%d/distance" - cpulistFile = sysRoot + "/node/node%d/cpulist" - cpuDriverFile = sysRoot + "/cpu/cpu%d/cpufreq/scaling_driver" - cpuMaxFile = sysRoot + "/cpu/cpu%d/cpufreq/cpuinfo_max_freq" - cpuCpccNominalFile = sysRoot + "/cpu/cpu%d/acpi_cppc/nominal_freq" - cpuIntelBaseFile = sysRoot + "/cpu/cpu%d/cpufreq/base_frequency" - cpuSocketFile = sysRoot + "/cpu/cpu%d/topology/physical_package_id" - cpuSiblingFile = sysRoot + "/cpu/cpu%d/topology/thread_siblings_list" - deviceFiles = "/sys/bus/pci/devices" + sysRoot = "/sys/devices/system" + nodeOnline = sysRoot + "/node/online" + cpuOnline = sysRoot + "/cpu/online" + distanceFile = sysRoot + "/node/node%d/distance" + cpulistFile = sysRoot + "/node/node%d/cpulist" + cpuMaxFile = sysRoot + "/cpu/cpu%d/cpufreq/cpuinfo_max_freq" + cpuBaseFile = sysRoot + "/cpu/cpu%d/cpufreq/base_frequency" + cpuSocketFile = sysRoot + "/cpu/cpu%d/topology/physical_package_id" + cpuSiblingFile = sysRoot + "/cpu/cpu%d/topology/thread_siblings_list" + deviceFiles = "/sys/bus/pci/devices" ) // pathReaderFn is a path reader function, injected into all value getters to @@ -133,8 +131,8 @@ func (*Sysfs) discoverCores(st *Topology, readerFunc pathReaderFn) { st.nodeIDs = idset.From[hw.NodeID]([]hw.NodeID{0}) const node = 0 const socket = 0 - - base, cpuMax := discoverCoreSpeeds(core, readerFunc) + cpuMax, _ := getNumeric[hw.KHz](cpuMaxFile, 64, readerFunc, core) + base, _ := getNumeric[hw.KHz](cpuBaseFile, 64, readerFunc, core) st.insert(node, socket, core, Performance, cpuMax, base) st.Nodes = st.nodeIDs.Slice() return nil @@ -151,8 +149,9 @@ func (*Sysfs) discoverCores(st *Topology, readerFunc pathReaderFn) { _ = cores.ForEach(func(core hw.CoreID) error { // best effort, zero values are defaults socket, _ := getNumeric[hw.SocketID](cpuSocketFile, 8, readerFunc, core) + cpuMax, _ := getNumeric[hw.KHz](cpuMaxFile, 64, readerFunc, core) + base, _ := getNumeric[hw.KHz](cpuBaseFile, 64, readerFunc, core) siblings, _ := getIDSet[hw.CoreID](cpuSiblingFile, readerFunc, core) - base, cpuMax := discoverCoreSpeeds(core, readerFunc) // if we get an incorrect core number, this means we're not getting the right // data from SysFS. In this case we bail and set default values. @@ -168,28 +167,6 @@ func (*Sysfs) discoverCores(st *Topology, readerFunc pathReaderFn) { } } -func discoverCoreSpeeds(core hw.CoreID, readerFunc pathReaderFn) (hw.KHz, hw.KHz) { - baseSpeed := hw.KHz(0) - maxSpeed := hw.KHz(0) - - driver, _ := getString(cpuDriverFile, readerFunc, core) - - switch driver { - case "acpi-cpufreq": - // Indicates the highest sustained performance level of the processor - baseSpeedMHz, _ := getNumeric[hw.MHz](cpuCpccNominalFile, 64, readerFunc, core) - baseSpeed = baseSpeedMHz.KHz() - default: - // COMPAT(1.9.x): while the `base_frequency` file is specific to the `intel_pstate` scaling driver, we should - // preserve the default while we may uncover more scaling driver specific implementations. - baseSpeed, _ = getNumeric[hw.KHz](cpuIntelBaseFile, 64, readerFunc, core) - } - - maxSpeed, _ = getNumeric[hw.KHz](cpuMaxFile, 64, readerFunc, core) - - return baseSpeed, maxSpeed -} - func getIDSet[T idset.ID](path string, readerFunc pathReaderFn, args ...any) (*idset.Set[T], error) { path = fmt.Sprintf(path, args...) s, err := readerFunc(path) diff --git a/client/lib/numalib/detect_linux_test.go b/client/lib/numalib/detect_linux_test.go index ceffd02c5c2..e253cacb622 100644 --- a/client/lib/numalib/detect_linux_test.go +++ b/client/lib/numalib/detect_linux_test.go @@ -68,37 +68,6 @@ func goodSysData(path string) ([]byte, error) { }[path], nil } -func goodSysDataAMD(path string) ([]byte, error) { - return map[string][]byte{ - "/sys/devices/system/node/online": []byte("0-1"), - "/sys/devices/system/cpu/online": []byte("0-3"), - "/sys/devices/system/node/node0/distance": []byte("10"), - "/sys/devices/system/node/node0/cpulist": []byte("0-3"), - "/sys/devices/system/node/node1/distance": []byte("10"), - "/sys/devices/system/node/node1/cpulist": []byte("0-3"), - "/sys/devices/system/cpu/cpu0/acpi_cppc/nominal_freq": []byte("2450"), - "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq": []byte("3500000"), - "/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver": []byte("acpi-cpufreq"), - "/sys/devices/system/cpu/cpu0/topology/physical_package_id": []byte("0"), - "/sys/devices/system/cpu/cpu0/topology/thread_siblings_list": []byte("0,2"), - "/sys/devices/system/cpu/cpu1/acpi_cppc/nominal_freq": []byte("2450"), - "/sys/devices/system/cpu/cpu1/cpufreq/cpuinfo_max_freq": []byte("3500000"), - "/sys/devices/system/cpu/cpu1/cpufreq/scaling_driver": []byte("acpi-cpufreq"), - "/sys/devices/system/cpu/cpu1/topology/physical_package_id": []byte("0"), - "/sys/devices/system/cpu/cpu1/topology/thread_siblings_list": []byte("1,3"), - "/sys/devices/system/cpu/cpu2/acpi_cppc/nominal_freq": []byte("2450"), - "/sys/devices/system/cpu/cpu2/cpufreq/cpuinfo_max_freq": []byte("3500000"), - "/sys/devices/system/cpu/cpu2/cpufreq/scaling_driver": []byte("acpi-cpufreq"), - "/sys/devices/system/cpu/cpu2/topology/physical_package_id": []byte("0"), - "/sys/devices/system/cpu/cpu2/topology/thread_siblings_list": []byte("0,2"), - "/sys/devices/system/cpu/cpu3/acpi_cppc/nominal_freq": []byte("2450"), - "/sys/devices/system/cpu/cpu3/cpufreq/cpuinfo_max_freq": []byte("3500000"), - "/sys/devices/system/cpu/cpu3/cpufreq/scaling_driver": []byte("acpi-cpufreq"), - "/sys/devices/system/cpu/cpu3/topology/physical_package_id": []byte("0"), - "/sys/devices/system/cpu/cpu3/topology/thread_siblings_list": []byte("1,3"), - }[path], nil -} - func TestSysfs_discoverOnline(t *testing.T) { st := MockTopology(&idset.Set[hw.NodeID]{}, SLIT{}, []Core{}) goodIDSet := idset.From[hw.NodeID]([]uint8{0, 1}) @@ -226,44 +195,6 @@ func TestSysfs_discoverCores(t *testing.T) { }, }, }}, - {"two nodes and good sys AMD data", twoNodes, goodSysDataAMD, &Topology{ - nodeIDs: twoNodes, - Nodes: twoNodes.Slice(), - Cores: []Core{ - { - SocketID: 1, - NodeID: 0, - ID: 0, - Grade: Performance, - BaseSpeed: 2450, - MaxSpeed: 3500, - }, - { - SocketID: 1, - NodeID: 0, - ID: 1, - Grade: Performance, - BaseSpeed: 2450, - MaxSpeed: 3500, - }, - { - SocketID: 1, - NodeID: 0, - ID: 2, - Grade: Performance, - BaseSpeed: 2450, - MaxSpeed: 3500, - }, - { - SocketID: 1, - NodeID: 0, - ID: 3, - Grade: Performance, - BaseSpeed: 2450, - MaxSpeed: 3500, - }, - }, - }}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/client/lib/numalib/hw/speeds.go b/client/lib/numalib/hw/speeds.go index c3b1a029253..23bd010a86c 100644 --- a/client/lib/numalib/hw/speeds.go +++ b/client/lib/numalib/hw/speeds.go @@ -16,10 +16,6 @@ func (khz KHz) MHz() MHz { return MHz(khz / 1000) } -func (mhz MHz) KHz() KHz { - return KHz(mhz * 1000) -} - func (khz KHz) String() string { return strconv.FormatUint(uint64(khz.MHz()), 10) } diff --git a/dev/hooks/pre-push b/dev/hooks/pre-push index 2a62a40f335..0f6cc35f4be 100755 --- a/dev/hooks/pre-push +++ b/dev/hooks/pre-push @@ -31,7 +31,6 @@ if [ -f version/version_ent.go ]; then fi # do not push directly to main, stable-*, release/* -# do not push Enterprise tags # ==================== while read local_ref local_sha remote_ref remote_sha do @@ -46,13 +45,5 @@ do if echo "$remote_ref"|grep -q 'refs/heads/release/.*'; then fail "refusing to push directly to a branch prefixed \`release/\`" fi - - if echo "$remote_ref" | grep -q 'refs/tags/v.*\+ent'; then - fail "refusing to push Nomad Enterprise tag" - fi - - if echo "$remote_ref" | grep -q 'refs/tags/v.*\+pro'; then - fail "refusing to push Nomad Enterprise (pro) tag" - fi - done + diff --git a/drivers/shared/executor/executor_test.go b/drivers/shared/executor/executor_test.go index 6c21a9a9a9f..a0e17e66696 100644 --- a/drivers/shared/executor/executor_test.go +++ b/drivers/shared/executor/executor_test.go @@ -1,7 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 -//go:build linux +//go:build !windows package executor diff --git a/e2e/allocexec/docker_exec_test.go b/e2e/allocexec/docker_exec_test.go index 17c165af4ba..2956d700d81 100644 --- a/e2e/allocexec/docker_exec_test.go +++ b/e2e/allocexec/docker_exec_test.go @@ -7,7 +7,6 @@ import ( "archive/tar" "bytes" "context" - "fmt" "strings" "testing" "time" @@ -28,14 +27,13 @@ func TestDockerAllocExec(t *testing.T) { } func testDockerExecStdin(t *testing.T) { - sub, cleanup := jobs3.Submit(t, "./input/sleepytar.hcl") + _, cleanup := jobs3.Submit(t, "./input/sleepytar.hcl") t.Cleanup(cleanup) client, err := nomadapi.NewClient(nomadapi.DefaultConfig()) must.NoError(t, err) - filter := fmt.Sprintf("JobID == \"%s\"", sub.JobID()) - allocations, _, err := client.Allocations().List(&nomadapi.QueryOptions{Filter: filter}) + allocations, _, err := client.Allocations().List(nil) must.NoError(t, err) must.SliceLen(t, 1, allocations) @@ -87,7 +85,7 @@ func testDockerExecStdin(t *testing.T) { nil, nil, ) - must.NoError(t, err, must.Sprintf("error executing command inside the container: %v", err)) + must.NoError(t, err) must.Zero(t, exitCode) // check the output of tar diff --git a/e2e/terraform/README.md b/e2e/terraform/README.md index a5785654557..b7d77a22bde 100644 --- a/e2e/terraform/README.md +++ b/e2e/terraform/README.md @@ -55,26 +55,18 @@ client_count_ubuntu_jammy_amd64 = "4" client_count_windows_2016_amd64 = "1" ``` -You will also need a Consul Enterprise license file and a Nomad Enterprise license file. +You will also need a Consul Enterprise license file. Optionally, edit the `nomad_local_binary` variable in the `terraform.tfvars` file to change the path to the local binary of -Nomad you'd like to upload, but keep in mind it has to match the OS and the CPU architecture of the nodes (amd64 linux). +Nomad you'd like to upload. Run Terraform apply to deploy the infrastructure: ```sh cd e2e/terraform/ terraform init -terraform apply -var="consul_license=$(cat full_path_to_consul.hclic)" -var="nomad_license=$(cat full_path_to_nomad.hclic)" -``` - -Alternative you can also run `make apply_full` from the terraform directory: - -``` -export NOMAD_LICENSE_PATH=./nomad.hclic -export CONSUL_LICENSE_PATH=./consul.hclic -make apply_full +terraform apply ``` > Note: You will likely see "Connection refused" or "Permission denied" errors diff --git a/e2e/terraform/terraform.tfvars b/e2e/terraform/terraform.tfvars index cb270665a80..324bb7c7ce3 100644 --- a/e2e/terraform/terraform.tfvars +++ b/e2e/terraform/terraform.tfvars @@ -3,8 +3,13 @@ # this default tfvars file expects that you have built nomad # with `make dev` or similar (../../ = this repository root) -# before running `terraform apply` and created the /pkg/goos_goarch/binary -# folder +# before running `terraform apply` nomad_local_binary = "../../pkg/linux_amd64/nomad" -nomad_local_binary_client_windows_2016_amd64 = ["../../pkg/windows_amd64/nomad.exe"] \ No newline at end of file +nomad_local_binary_client_windows_2016_amd64 = ["../../pkg/windows_amd64/nomad.exe"] + +# The Consul server is Consul Enterprise, so provide a license via --var: +# consul_license = + +# For testing Nomad enterprise, also set via --var: +# nomad_license = diff --git a/e2e/ui/run.sh b/e2e/ui/run.sh index a2f5980cef1..a976393c786 100755 --- a/e2e/ui/run.sh +++ b/e2e/ui/run.sh @@ -33,7 +33,7 @@ EOF } -IMAGE="mcr.microsoft.com/playwright:v1.49.0-jammy" +IMAGE="mcr.microsoft.com/playwright:v1.48.0-noble" pushd $(dirname "${BASH_SOURCE[0]}") > /dev/null run_tests() { diff --git a/nomad/periodic_test.go b/nomad/periodic_test.go index 62a9dd68291..1718c434ca9 100644 --- a/nomad/periodic_test.go +++ b/nomad/periodic_test.go @@ -19,7 +19,6 @@ import ( "github.com/hashicorp/nomad/nomad/mock" "github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/testutil" - "github.com/shoenig/test/must" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -89,13 +88,6 @@ func (m *MockJobEvalDispatcher) dispatchedJobs(parent *structs.Job) []*structs.J return jobs } -func (m *MockJobEvalDispatcher) hasJob(id structs.NamespacedID) bool { - m.lock.Lock() - defer m.lock.Unlock() - _, ok := m.Jobs[id] - return ok -} - type times []time.Time func (t times) Len() int { return len(t) } @@ -270,32 +262,39 @@ func TestPeriodicDispatch_Add_TriggersUpdate(t *testing.T) { job := testPeriodicJob(time.Now().Add(10 * time.Second)) // Add it. - must.NoError(t, p.Add(job)) + if err := p.Add(job); err != nil { + t.Fatalf("Add failed %v", err) + } // Update it to be sooner and re-add. expected := time.Now().Round(1 * time.Second).Add(1 * time.Second) - job = job.Copy() job.Periodic.Spec = fmt.Sprintf("%d", expected.Unix()) - must.NoError(t, p.Add(job)) + if err := p.Add(job); err != nil { + t.Fatalf("Add failed %v", err) + } // Check that nothing is created. tuple := structs.NamespacedID{ ID: job.ID, Namespace: job.Namespace, } - must.False(t, m.hasJob(tuple), - must.Sprint("periodic dispatcher created eval too early")) + if _, ok := m.Jobs[tuple]; ok { + t.Fatalf("periodic dispatcher created eval at the wrong time") + } time.Sleep(2 * time.Second) // Check that job was launched correctly. times, err := m.LaunchTimes(p, job.Namespace, job.ID) - must.NoError(t, err, - must.Sprint("failed to get launch times for job")) - must.Len(t, 1, times, - must.Sprint("incorrect number of launch times for job")) - must.Eq(t, expected, times[0], - must.Sprint("periodic dispatcher created eval for wrong time")) + if err != nil { + t.Fatalf("failed to get launch times for job %q", job.ID) + } + if len(times) != 1 { + t.Fatalf("incorrect number of launch times for job %q", job.ID) + } + if times[0] != expected { + t.Fatalf("periodic dispatcher created eval for time %v; want %v", times[0], expected) + } } func TestPeriodicDispatch_Remove_Untracked(t *testing.T) { diff --git a/nomad/structs/acl.go b/nomad/structs/acl.go index eed00046fa8..b60c1c2f1aa 100644 --- a/nomad/structs/acl.go +++ b/nomad/structs/acl.go @@ -474,16 +474,6 @@ func (a *ACLToken) UnmarshalJSON(data []byte) (err error) { return nil } -func (a *ACLToken) Sanitize() *ACLToken { - if a == nil { - return nil - } - - out := a.Copy() - out.SecretID = "" - return out -} - // ACLRole is an abstraction for the ACL system which allows the grouping of // ACL policies into a single object. ACL tokens can be created and linked to // a role; the token then inherits all the permissions granted by the policies. diff --git a/nomad/structs/actions.go b/nomad/structs/actions.go index 231c04cbd2e..2791d825ecb 100644 --- a/nomad/structs/actions.go +++ b/nomad/structs/actions.go @@ -16,8 +16,8 @@ import ( "github.com/hashicorp/go-multierror" ) -// validJobActionName is used to validate a action name. -var validJobActionName = regexp.MustCompile(`^[^\x00\s]{1,128}$`) +// validJobActionName is used to validate a job action name. +var validJobActionName = regexp.MustCompile("^[a-zA-Z0-9-]{1,128}$") type Action struct { Name string diff --git a/nomad/structs/actions_test.go b/nomad/structs/actions_test.go index 03102362d2d..3c7e2071403 100644 --- a/nomad/structs/actions_test.go +++ b/nomad/structs/actions_test.go @@ -178,28 +178,12 @@ func TestAction_Validate(t *testing.T) { expectedError: errors.New(`invalid name 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'`), }, { - name: "invalid character name with spaces", + name: "invalid character name", inputAction: &Action{ - Name: "invalid name with spaces", + Name: `\//?|?|?%&%@$&£@$)`, Command: "env", }, - expectedError: errors.New(`invalid name 'invalid name with spaces'`), - }, - { - name: "invalid character name with nulls", - inputAction: &Action{ - Name: "invalid\x00name", - Command: "env", - }, - expectedError: fmt.Errorf("1 error occurred:\n\t* invalid name 'invalid\x00name'\n\n"), // had to use fmt.Errorf to show the null character - }, - { - name: "Emoji characters are valid", - inputAction: &Action{ - Name: "🇳🇴🇲🇦🇩", - Command: "env", - }, - expectedError: nil, + expectedError: errors.New(`invalid name '\//?|?|?%&%@$&£@$)'`), }, { name: "valid", diff --git a/nomad/structs/config/workload_id.go b/nomad/structs/config/workload_id.go index 5a3f92dbee2..872acd81ef9 100644 --- a/nomad/structs/config/workload_id.go +++ b/nomad/structs/config/workload_id.go @@ -12,7 +12,7 @@ import ( "github.com/hashicorp/nomad/helper/pointer" ) -// WorkloadIdentityConfig is the agent configuration block used to define +// WorkloadIdentityConfig is the agent configuraion block used to define // default workload identities. // // This based on the WorkloadIdentity struct from nomad/structs/workload_id.go diff --git a/nomad/structs/event.go b/nomad/structs/event.go index 466421d38a5..1eca62046f9 100644 --- a/nomad/structs/event.go +++ b/nomad/structs/event.go @@ -155,7 +155,8 @@ type ServiceRegistrationStreamEvent struct { // NewACLTokenEvent takes a token and creates a new ACLTokenEvent. It creates // a copy of the passed in ACLToken and empties out the copied tokens SecretID func NewACLTokenEvent(token *ACLToken) *ACLTokenEvent { - c := token.Sanitize() + c := token.Copy() + c.SecretID = "" return &ACLTokenEvent{ ACLToken: c, diff --git a/scripts/combine-ui-test-results.js b/scripts/combine-ui-test-results.js deleted file mode 100644 index 8d78f423cce..00000000000 --- a/scripts/combine-ui-test-results.js +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env node -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -'use strict'; -const fs = require('fs'); - -const NUM_PARTITIONS = 4; - -function combineResults() { - const results = []; - let duration = 0; - let aggregateSummary = { total: 0, passed: 0, failed: 0 }; - - for (let i = 1; i <= NUM_PARTITIONS; i++) { - try { - const data = JSON.parse( - fs.readFileSync(`../test-results/test-results-${i}/test-results.json`).toString() - ); - results.push(...data.tests); - duration += data.duration; - aggregateSummary.total += data.summary.total; - aggregateSummary.passed += data.summary.passed; - aggregateSummary.failed += data.summary.failed; - } catch (err) { - console.error(`Error reading partition ${i}:`, err); - } - } - - const output = { - timestamp: new Date().toISOString(), - sha: process.env.GITHUB_SHA, - summary: { - total: aggregateSummary.total, - passed: aggregateSummary.passed, - failed: aggregateSummary.failed - }, - duration, - tests: results - }; - - fs.writeFileSync('../ui/combined-test-results.json', JSON.stringify(output, null, 2)); -} - -if (require.main === module) { - combineResults(); -} - -module.exports = combineResults; diff --git a/ui/app/index.html b/ui/app/index.html index df5eb739390..9d49fe8a579 100644 --- a/ui/app/index.html +++ b/ui/app/index.html @@ -24,7 +24,6 @@ {{content-for "body"}} - {{content-for "body-footer"}} diff --git a/ui/test-reporter.js b/ui/test-reporter.js deleted file mode 100644 index ddad7268714..00000000000 --- a/ui/test-reporter.js +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -/* eslint-env node */ -/* eslint-disable no-console */ - -const fs = require('fs'); -const path = require('path'); - -class JsonReporter { - constructor(out, socket, config) { - this.out = out || process.stdout; - this.results = []; - - // Get output file from Testem config, which is set by the --json-report=path argument - this.outputFile = config?.fileOptions?.custom_report_file; - this.generateReport = !!this.outputFile; - - if (this.generateReport) { - console.log( - `[Reporter] Initializing with output file: ${this.outputFile}` - ); - - try { - fs.mkdirSync(path.dirname(this.outputFile), { recursive: true }); - - // Initialize the results file - fs.writeFileSync( - this.outputFile, - JSON.stringify( - { - summary: { total: 0, passed: 0, failed: 0 }, - timestamp: new Date().toISOString(), - tests: [], - }, - null, - 2 - ) - ); - console.log('[Reporter] Initialized results file'); - } catch (err) { - console.error('[Reporter] Error initializing results file:', err); - } - } else { - console.log('[Reporter] No report file configured, skipping JSON output'); - } - - process.on('SIGINT', () => { - console.log('[Reporter] Received SIGINT, finishing up...'); - this.finish(); - process.exit(0); - }); - - this.testCounter = 0; - this.startTime = Date.now(); - } - - filterLogs(logs) { - return logs.filter((log) => { - // Filter out token-related logs - if ( - log.text && - (log.text.includes('Accessor:') || - log.text.includes('log in with a JWT') || - log.text === 'TOKENS:' || - log.text === '=====================================') - ) { - return false; - } - - // Keep non-warning logs that aren't token-related - return log.type !== 'warn'; - }); - } - - report(prefix, data) { - if (!data || !data.name) { - console.log(`[Reporter] Skipping invalid test result: ${data.name}`); - return; - } - - this.testCounter++; - console.log(`[Reporter] Test #${this.testCounter}: ${data.name}`); - - const partitionMatch = data.name.match(/^Exam Partition (\d+) - (.*)/); - - const result = { - name: partitionMatch ? partitionMatch[2] : data.name.trim(), - partition: partitionMatch ? parseInt(partitionMatch[1], 10) : null, - browser: prefix, - passed: !data.failed, - duration: data.runDuration, - error: data.failed ? data.error : null, - logs: this.filterLogs(data.logs || []), - }; - - if (result.passed) { - console.log('- [PASS]'); - } else { - console.log('- [FAIL]'); - console.log('- Error:', result.error); - console.log('- Logs:', result.logs); - } - - this.results.push(result); - } - - writeCurrentResults() { - console.log('[Reporter] Writing current results...'); - try { - const passed = this.results.filter((r) => r.passed).length; - const failed = this.results.filter((r) => !r.passed).length; - const total = this.results.length; - const duration = Date.now() - this.startTime; - - const output = { - summary: { total, passed, failed }, - timestamp: new Date().toISOString(), - duration, - tests: this.results, - }; - - if (this.generateReport) { - fs.writeFileSync(this.outputFile, JSON.stringify(output, null, 2)); - } - - // Print a summary - console.log('\n[Reporter] Test Summary:'); - console.log(`- Total: ${total}`); - console.log(`- Passed: ${passed}`); - console.log(`- Failed: ${failed}`); - console.log(`- Duration: ${duration}ms`); - if (failed > 0) { - console.log('\n[Reporter] Failed Tests:'); - this.results - .filter((r) => !r.passed) - .forEach((r) => { - console.log(`❌ ${r.name}`); - if (r.error) { - console.error(r.error); - } - }); - } - - console.log('[Reporter] Successfully wrote results'); - } catch (err) { - console.error('[Reporter] Error writing results:', err); - } - } - finish() { - console.log('[Reporter] Finishing up...'); - this.writeCurrentResults(); - console.log('[Reporter] Done.'); - } -} - -module.exports = JsonReporter; diff --git a/ui/testem.js b/ui/testem.js index c937a5760fe..7d1869af9ef 100644 --- a/ui/testem.js +++ b/ui/testem.js @@ -3,24 +3,7 @@ * SPDX-License-Identifier: BUSL-1.1 */ -// @ts-check - 'use strict'; -const JsonReporter = require('./test-reporter'); - -/** - * Get the path for the test results file based on the command line arguments - * @returns {string} The path to the test results file - */ -const getReportPath = () => { - const jsonReportArg = process.argv.find((arg) => - arg.startsWith('--json-report=') - ); - if (jsonReportArg) { - return jsonReportArg.split('=')[1]; - } - return null; -}; const config = { test_page: 'tests/index.html?hidepassed', @@ -30,12 +13,6 @@ const config = { browser_start_timeout: 120, parallel: -1, framework: 'qunit', - reporter: JsonReporter, - custom_report_file: getReportPath(), - // NOTE: we output this property as custom_report_file instead of report_file. - // See https://github.com/testem/testem/issues/1073, report_file + custom reporter results in double output. - debug: true, - browser_args: { // New format in testem/master, but not in a release yet // Chrome: { diff --git a/ui/yarn.lock b/ui/yarn.lock index 4be11f4af9a..8930c2a8c51 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -11248,9 +11248,9 @@ mute-stream@0.0.8: integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nanoid@^3.3.7: - version "3.3.8" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" - integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== nanomatch@^1.2.9: version "1.2.13" diff --git a/website/content/docs/concepts/security.mdx b/website/content/docs/concepts/security.mdx index fdb707f1b1b..ac5f52ade99 100644 --- a/website/content/docs/concepts/security.mdx +++ b/website/content/docs/concepts/security.mdx @@ -9,7 +9,7 @@ description: >- authentication. --- -# Security Model +## Overview Nomad is a flexible workload orchestrator to deploy and manage any containerized or legacy application using a single, unified workflow. It can run diverse diff --git a/website/content/docs/enterprise/sentinel.mdx b/website/content/docs/enterprise/sentinel.mdx index 2ae87fe0cd9..30fcf3c5e18 100644 --- a/website/content/docs/enterprise/sentinel.mdx +++ b/website/content/docs/enterprise/sentinel.mdx @@ -5,7 +5,7 @@ description: >- Learn about Nomad Sentinel Policy Objects --- -# Sentinel +## Sentinel In Nomad Enterprise, operators can create Sentinel policies for fine-grained policy enforcement. Sentinel policies build on top of the ACL system and allow diff --git a/website/content/docs/job-specification/ui.mdx b/website/content/docs/job-specification/ui.mdx index b2fe276b662..c9792168b2d 100644 --- a/website/content/docs/job-specification/ui.mdx +++ b/website/content/docs/job-specification/ui.mdx @@ -46,5 +46,3 @@ job "docs" { # ... } ``` - -![Job UI links and description rendered in the Web UI](/img/nomad-ui-block.png) diff --git a/website/package-lock.json b/website/package-lock.json index 009d9319695..d15f7e408aa 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -13,7 +13,7 @@ "dart-linkcheck": "^2.0.15", "husky": "^9.0.7", "next": "14.0.4", - "prettier": "^3.4.1" + "prettier": "^3.2.4" }, "engines": { "node": ">=18.x <=20.x", @@ -9806,9 +9806,9 @@ } }, "node_modules/prettier": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.1.tgz", - "integrity": "sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz", + "integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" diff --git a/website/package.json b/website/package.json index dd77a403e0e..099e57a32f4 100644 --- a/website/package.json +++ b/website/package.json @@ -13,7 +13,7 @@ "dart-linkcheck": "^2.0.15", "husky": "^9.0.7", "next": "14.0.4", - "prettier": "^3.4.1" + "prettier": "^3.2.4" }, "scripts": { "build": "./scripts/website-build.sh", diff --git a/website/public/img/nomad-ui-block.png b/website/public/img/nomad-ui-block.png deleted file mode 100644 index f69a21693995cbcc5927b47e58bc0208251575ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18682 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