diff --git a/.github/.linkspector.yml b/.github/.linkspector.yml index 18aeeae416f..50e9359f515 100644 --- a/.github/.linkspector.yml +++ b/.github/.linkspector.yml @@ -27,7 +27,7 @@ ignorePatterns: - pattern: "splunk.com" - pattern: "stackoverflow.com/questions" - pattern: "developer.hashicorp.com/terraform/language" - - pattern: "platform.openai.com/docs/api-reference" + - pattern: "platform.openai.com" - pattern: "api.openai.com" aliveStatusCodes: - 200 diff --git a/.github/actions/setup-go-tools/action.yaml b/.github/actions/setup-go-tools/action.yaml index 9c08a7d417b..d259a38420b 100644 --- a/.github/actions/setup-go-tools/action.yaml +++ b/.github/actions/setup-go-tools/action.yaml @@ -11,4 +11,4 @@ runs: go install storj.io/drpc/cmd/protoc-gen-go-drpc@v0.0.34 go install golang.org/x/tools/cmd/goimports@v0.31.0 go install github.com/mikefarah/yq/v4@v4.44.3 - go install go.uber.org/mock/mockgen@v0.5.0 + go install go.uber.org/mock/mockgen@v0.6.0 diff --git a/.github/actions/setup-go/action.yaml b/.github/actions/setup-go/action.yaml index 02b54830cdf..80f9f878750 100644 --- a/.github/actions/setup-go/action.yaml +++ b/.github/actions/setup-go/action.yaml @@ -4,7 +4,7 @@ description: | inputs: version: description: "The Go version to use." - default: "1.24.10" + default: "1.25.10" use-preinstalled-go: description: "Whether to use preinstalled Go." default: "false" diff --git a/.github/actions/setup-tf/action.yaml b/.github/actions/setup-tf/action.yaml index f79618834d9..2ab8ea10b17 100644 --- a/.github/actions/setup-tf/action.yaml +++ b/.github/actions/setup-tf/action.yaml @@ -7,5 +7,5 @@ runs: - name: Install Terraform uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2 with: - terraform_version: 1.13.4 + terraform_version: 1.16.6 terraform_wrapper: false diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5fd91050f30..fc4a2b7492a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -35,7 +35,7 @@ jobs: tailnet-integration: ${{ steps.filter.outputs.tailnet-integration }} steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -157,7 +157,7 @@ jobs: runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -235,7 +235,7 @@ jobs: if: ${{ !cancelled() }} steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -292,7 +292,7 @@ jobs: timeout-minutes: 20 steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -343,7 +343,7 @@ jobs: - windows-2022 steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -532,7 +532,7 @@ jobs: timeout-minutes: 25 steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -581,7 +581,7 @@ jobs: timeout-minutes: 25 steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -641,7 +641,7 @@ jobs: timeout-minutes: 20 steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -668,7 +668,7 @@ jobs: timeout-minutes: 20 steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -701,7 +701,7 @@ jobs: name: ${{ matrix.variant.name }} steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -781,7 +781,7 @@ jobs: if: needs.changes.outputs.site == 'true' || needs.changes.outputs.ci == 'true' steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -862,7 +862,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -933,7 +933,7 @@ jobs: if: always() steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -967,7 +967,7 @@ jobs: # We always build the dylibs on Go changes to verify we're not merging unbuildable code, # but they need only be signed and uploaded on coder/coder main. if: needs.changes.outputs.go == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/') - runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest' }} + runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-15' || 'macos-15' }} steps: # Harden Runner doesn't work on macOS - name: Checkout @@ -1053,7 +1053,7 @@ jobs: runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -1108,7 +1108,7 @@ jobs: IMAGE: ghcr.io/coder/coder-preview:${{ steps.build-docker.outputs.tag }} steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -1505,7 +1505,7 @@ jobs: if: needs.changes.outputs.db == 'true' || needs.changes.outputs.ci == 'true' || github.ref == 'refs/heads/main' steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/contrib.yaml b/.github/workflows/contrib.yaml index 54f23310cc2..53850f2023a 100644 --- a/.github/workflows/contrib.yaml +++ b/.github/workflows/contrib.yaml @@ -43,7 +43,7 @@ jobs: # branch should not be protected branch: "main" # Some users have signed a corporate CLA with Coder so are exempt from signing our community one. - allowlist: "coryb,aaronlehmann,dependabot*,blink-so*" + allowlist: "coryb,aaronlehmann,dependabot*,blink-so*,coder-backporter-app[bot]" release-labels: runs-on: ubuntu-latest diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index e4211fc8973..349bea46be2 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -36,7 +36,7 @@ jobs: verdict: ${{ steps.check.outputs.verdict }} # DEPLOY or NOOP steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -65,7 +65,7 @@ jobs: packages: write # to retag image as dogfood steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -146,7 +146,7 @@ jobs: needs: deploy steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/docker-base.yaml b/.github/workflows/docker-base.yaml index 46a6998fce1..50b14bc8dcb 100644 --- a/.github/workflows/docker-base.yaml +++ b/.github/workflows/docker-base.yaml @@ -38,7 +38,7 @@ jobs: if: github.repository_owner == 'coder' steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/dogfood.yaml b/.github/workflows/dogfood.yaml index 752641dbf06..c11c9d60a3c 100644 --- a/.github/workflows/dogfood.yaml +++ b/.github/workflows/dogfood.yaml @@ -7,14 +7,10 @@ on: paths: - "dogfood/**" - ".github/workflows/dogfood.yaml" - - "flake.lock" - - "flake.nix" pull_request: paths: - "dogfood/**" - ".github/workflows/dogfood.yaml" - - "flake.lock" - - "flake.nix" workflow_dispatch: permissions: @@ -26,7 +22,7 @@ jobs: runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-4' || 'ubuntu-latest' }} steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -35,32 +31,6 @@ jobs: with: persist-credentials: false - - name: Setup Nix - uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34 - with: - # Pinning to 2.28 here, as Nix gets a "error: [json.exception.type_error.302] type must be array, but is string" - # on version 2.29 and above. - nix_version: "2.28.5" - - - uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3 - with: - # restore and save a cache using this key - primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }} - # if there's no cache hit, restore a cache by this prefix - restore-prefixes-first-match: nix-${{ runner.os }}- - # collect garbage until Nix store size (in bytes) is at most this number - # before trying to save a new cache - # 1G = 1073741824 - gc-max-store-size-linux: 5G - # do purge caches - purge: true - # purge all versions of the cache - purge-prefixes: nix-${{ runner.os }}- - # created more than this number of seconds ago relative to the start of the `Post Restore` phase - purge-created: 0 - # except the version with the `primary-key`, if it exists - purge-primary-key: never - - name: Get branch name id: branch-name uses: tj-actions/branch-names@5250492686b253f06fa55861556d1027b067aeb5 # v9.0.2 @@ -99,24 +69,6 @@ jobs: push: ${{ github.ref == 'refs/heads/main' }} tags: "codercom/oss-dogfood:${{ steps.docker-tag-name.outputs.tag }},codercom/oss-dogfood:latest" - - name: Build Nix image - run: nix build .#dev_image - - - name: Push Nix image - if: github.ref == 'refs/heads/main' - run: | - docker load -i result - - CURRENT_SYSTEM=$(nix eval --impure --raw --expr 'builtins.currentSystem') - - docker image tag "codercom/oss-dogfood-nix:latest-$CURRENT_SYSTEM" "codercom/oss-dogfood-nix:${DOCKER_TAG}" - docker image push "codercom/oss-dogfood-nix:${DOCKER_TAG}" - - docker image tag "codercom/oss-dogfood-nix:latest-$CURRENT_SYSTEM" "codercom/oss-dogfood-nix:latest" - docker image push "codercom/oss-dogfood-nix:latest" - env: - DOCKER_TAG: ${{ steps.docker-tag-name.outputs.tag }} - deploy_template: needs: build_image runs-on: ubuntu-latest @@ -125,7 +77,7 @@ jobs: id-token: write steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/nightly-gauntlet.yaml b/.github/workflows/nightly-gauntlet.yaml index 1cd13d79d84..b1d3b3595b2 100644 --- a/.github/workflows/nightly-gauntlet.yaml +++ b/.github/workflows/nightly-gauntlet.yaml @@ -27,7 +27,7 @@ jobs: - windows-2022 steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/pr-auto-assign.yaml b/.github/workflows/pr-auto-assign.yaml index 6da81f35e12..29a5039c7f2 100644 --- a/.github/workflows/pr-auto-assign.yaml +++ b/.github/workflows/pr-auto-assign.yaml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/pr-cleanup.yaml b/.github/workflows/pr-cleanup.yaml index cfcd997377b..01008a73df2 100644 --- a/.github/workflows/pr-cleanup.yaml +++ b/.github/workflows/pr-cleanup.yaml @@ -19,7 +19,7 @@ jobs: packages: write steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/pr-deploy.yaml b/.github/workflows/pr-deploy.yaml index d45e97cc9c4..b42b2bf66b8 100644 --- a/.github/workflows/pr-deploy.yaml +++ b/.github/workflows/pr-deploy.yaml @@ -39,7 +39,7 @@ jobs: PR_OPEN: ${{ steps.check_pr.outputs.pr_open }} steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -76,7 +76,7 @@ jobs: runs-on: "ubuntu-latest" steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -184,7 +184,7 @@ jobs: pull-requests: write # needed for commenting on PRs steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -228,7 +228,7 @@ jobs: CODER_IMAGE_TAG: ${{ needs.get_info.outputs.CODER_IMAGE_TAG }} steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -288,7 +288,7 @@ jobs: PR_HOSTNAME: "pr${{ needs.get_info.outputs.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/release-validation.yaml b/.github/workflows/release-validation.yaml index ada3297f816..98ae9d9c036 100644 --- a/.github/workflows/release-validation.yaml +++ b/.github/workflows/release-validation.yaml @@ -14,7 +14,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index be1b56f0725..6dade09a057 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -60,7 +60,7 @@ jobs: # build-dylib is a separate job to build the dylib on macOS. build-dylib: - runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-latest' || 'macos-latest' }} + runs-on: ${{ github.repository_owner == 'coder' && 'depot-macos-15' || 'macos-15' }} needs: check-perms steps: # Harden Runner doesn't work on macOS. @@ -164,7 +164,7 @@ jobs: version: ${{ steps.version.outputs.version }} steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -693,6 +693,18 @@ jobs: gcloud storage cp "./build/${detached_signature}" "gs://releases.coder.com/coder-cli/${version}/${cli_name}.asc" done + # Mint a short-lived installation token from the dedicated release + # GitHub App. The default GITHUB_TOKEN (github-actions[bot]) cannot be + # added to the "Auto-imported tag create protections" ruleset bypass + # list, so `gh release create` fails to create the tag with a 403. The + # App is added to that ruleset's bypass list as an Integration actor. + - name: Generate release App token + id: release_app_token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.RELEASE_APP_ID }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + - name: Publish release run: | set -euo pipefail @@ -728,7 +740,9 @@ jobs: --release-notes-file "$CODER_RELEASE_NOTES_FILE" \ "${files[@]}" env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Use the dedicated release App token (github-actions[bot] is blocked + # from creating tags by the tag-create protection ruleset). + GITHUB_TOKEN: ${{ steps.release_app_token.outputs.token }} CODER_GPG_RELEASE_KEY_BASE64: ${{ secrets.GPG_RELEASE_KEY_BASE64 }} VERSION: ${{ steps.version.outputs.version }} CREATED_LATEST_TAG: ${{ steps.build_docker.outputs.created_latest_tag }} @@ -802,7 +816,7 @@ jobs: # TODO: skip this if it's not a new release (i.e. a backport). This is # fine right now because it just makes a PR that we can close. - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -878,7 +892,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -971,7 +985,7 @@ jobs: if: ${{ !inputs.dry_run }} steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 392311ad7f7..47ae7f57691 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/security.yaml b/.github/workflows/security.yaml index e40d828c51d..31a73c45cbb 100644 --- a/.github/workflows/security.yaml +++ b/.github/workflows/security.yaml @@ -27,7 +27,7 @@ jobs: runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -63,116 +63,3 @@ jobs: --data "{\"content\": \"$msg\"}" \ "${{ secrets.SLACK_SECURITY_FAILURE_WEBHOOK_URL }}" - trivy: - permissions: - security-events: write - runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }} - steps: - - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 - with: - egress-policy: audit - - - name: Checkout - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Setup Go - uses: ./.github/actions/setup-go - - - name: Setup Node - uses: ./.github/actions/setup-node - - - name: Setup sqlc - uses: ./.github/actions/setup-sqlc - - - name: Install cosign - uses: ./.github/actions/install-cosign - - - name: Install syft - uses: ./.github/actions/install-syft - - - name: Install yq - run: go run github.com/mikefarah/yq/v4@v4.44.3 - - name: Install mockgen - run: go install go.uber.org/mock/mockgen@v0.5.0 - - name: Install protoc-gen-go - run: go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.30 - - name: Install protoc-gen-go-drpc - run: go install storj.io/drpc/cmd/protoc-gen-go-drpc@v0.0.34 - - name: Install Protoc - run: | - # protoc must be in lockstep with our dogfood Dockerfile or the - # version in the comments will differ. This is also defined in - # ci.yaml. - set -euxo pipefail - cd dogfood/coder - mkdir -p /usr/local/bin - mkdir -p /usr/local/include - - DOCKER_BUILDKIT=1 docker build . --target proto -t protoc - protoc_path=/usr/local/bin/protoc - docker run --rm --entrypoint cat protoc /tmp/bin/protoc > $protoc_path - chmod +x $protoc_path - protoc --version - # Copy the generated files to the include directory. - docker run --rm -v /usr/local/include:/target protoc cp -r /tmp/include/google /target/ - ls -la /usr/local/include/google/protobuf/ - stat /usr/local/include/google/protobuf/timestamp.proto - - - name: Build Coder linux amd64 Docker image - id: build - run: | - set -euo pipefail - - version="$(./scripts/version.sh)" - image_job="build/coder_${version}_linux_amd64.tag" - - # This environment variable force make to not build packages and - # archives (which the Docker image depends on due to technical reasons - # related to concurrent FS writes). - export DOCKER_IMAGE_NO_PREREQUISITES=true - # This environment variables forces scripts/build_docker.sh to build - # the base image tag locally instead of using the cached version from - # the registry. - CODER_IMAGE_BUILD_BASE_TAG="$(CODER_IMAGE_BASE=coder-base ./scripts/image_tag.sh --version "$version")" - export CODER_IMAGE_BUILD_BASE_TAG - - # We would like to use make -j here, but it doesn't work with the some recent additions - # to our code generation. - make "$image_job" - echo "image=$(cat "$image_job")" >> "$GITHUB_OUTPUT" - - - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 - with: - image-ref: ${{ steps.build.outputs.image }} - format: sarif - output: trivy-results.sarif - severity: "CRITICAL,HIGH" - - - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@014f16e7ab1402f30e7c3329d33797e7948572db # v3.29.5 - with: - sarif_file: trivy-results.sarif - category: "Trivy" - - - name: Upload Trivy scan results as an artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 - with: - name: trivy - path: trivy-results.sarif - retention-days: 7 - - - name: Send Slack notification on failure - if: ${{ failure() }} - run: | - msg="❌ Trivy Failed\n\nhttps://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" - curl \ - -qfsSL \ - -X POST \ - -H "Content-Type: application/json" \ - --data "{\"content\": \"$msg\"}" \ - "${{ secrets.SLACK_SECURITY_FAILURE_WEBHOOK_URL }}" diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml index 957152634d1..50b404948cf 100644 --- a/.github/workflows/stale.yaml +++ b/.github/workflows/stale.yaml @@ -18,7 +18,7 @@ jobs: pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -96,7 +96,7 @@ jobs: contents: write steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit @@ -120,7 +120,7 @@ jobs: actions: write steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit diff --git a/.github/workflows/weekly-docs.yaml b/.github/workflows/weekly-docs.yaml index 511915814d6..6e672557663 100644 --- a/.github/workflows/weekly-docs.yaml +++ b/.github/workflows/weekly-docs.yaml @@ -21,7 +21,7 @@ jobs: pull-requests: write # required to post PR review comments by the action steps: - name: Harden Runner - uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2 + uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.16.0 with: egress-policy: audit diff --git a/Makefile b/Makefile index 4997430f9dd..5c310cbea5a 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,9 @@ MOST_GO_SRC_FILES := $(shell \ # All the shell files in the repo, excluding ignored files. SHELL_SRC_FILES := $(shell find . $(FIND_EXCLUSIONS) -type f -name '*.sh') +MIGRATION_FILES := $(shell find ./coderd/database/migrations/ -maxdepth 1 $(FIND_EXCLUSIONS) -type f -name '*.sql') +FIXTURE_FILES := $(shell find ./coderd/database/migrations/testdata/fixtures/ $(FIND_EXCLUSIONS) -type f -name '*.sql') + # Ensure we don't use the user's git configs which might cause side-effects GIT_FLAGS = GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null @@ -561,7 +564,7 @@ endif # Note: we don't run zizmor in the lint target because it takes a while. CI # runs it explicitly. -lint: lint/shellcheck lint/go lint/ts lint/examples lint/helm lint/site-icons lint/markdown lint/actions/actionlint lint/check-scopes +lint: lint/shellcheck lint/go lint/ts lint/examples lint/helm lint/site-icons lint/markdown lint/actions/actionlint lint/check-scopes lint/migrations .PHONY: lint lint/site-icons: @@ -608,7 +611,9 @@ lint/actions/actionlint: .PHONY: lint/actions/actionlint lint/actions/zizmor: - ./scripts/zizmor.sh \ + # Using a token will use trivy, which is no longer supported. + # So disable any use of a token for this target. + GH_TOKEN="" ./scripts/zizmor.sh \ --strict-collection \ --persona=regular \ . @@ -619,6 +624,12 @@ lint/check-scopes: coderd/database/dump.sql go run ./scripts/check-scopes .PHONY: lint/check-scopes +# Verify migrations do not hardcode the public schema. +lint/migrations: + ./scripts/check_pg_schema.sh "Migrations" $(MIGRATION_FILES) + ./scripts/check_pg_schema.sh "Fixtures" $(FIXTURE_FILES) +.PHONY: lint/migrations + # All files generated by the database should be added here, and this can be used # as a target for jobs that need to run after the database is generated. DB_GEN_FILES := \ diff --git a/agent/agent.go b/agent/agent.go index 238aee9ad1d..0a5459ddc0e 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -41,6 +41,7 @@ import ( "github.com/coder/coder/v2/agent/agentcontainers" "github.com/coder/coder/v2/agent/agentexec" "github.com/coder/coder/v2/agent/agentscripts" + "github.com/coder/coder/v2/agent/agentsocket" "github.com/coder/coder/v2/agent/agentssh" "github.com/coder/coder/v2/agent/proto" "github.com/coder/coder/v2/agent/proto/resourcesmonitor" @@ -97,6 +98,8 @@ type Options struct { Devcontainers bool DevcontainerAPIOptions []agentcontainers.Option // Enable Devcontainers for these to be effective. Clock quartz.Clock + SocketServerEnabled bool + SocketPath string // Path for the agent socket server socket } type Client interface { @@ -202,6 +205,8 @@ func New(options Options) Agent { devcontainers: options.Devcontainers, containerAPIOptions: options.DevcontainerAPIOptions, + socketPath: options.SocketPath, + socketServerEnabled: options.SocketServerEnabled, } // Initially, we have a closed channel, reflecting the fact that we are not initially connected. // Each time we connect we replace the channel (while holding the closeMutex) with a new one @@ -279,6 +284,10 @@ type agent struct { devcontainers bool containerAPIOptions []agentcontainers.Option containerAPI *agentcontainers.API + + socketServerEnabled bool + socketPath string + socketServer *agentsocket.Server } func (a *agent) TailnetConn() *tailnet.Conn { @@ -358,9 +367,32 @@ func (a *agent) init() { s.ExperimentalContainers = a.devcontainers }, ) + + a.initSocketServer() + go a.runLoop() } +// initSocketServer initializes server that allows direct communication with a workspace agent using IPC. +func (a *agent) initSocketServer() { + if !a.socketServerEnabled { + a.logger.Info(a.hardCtx, "socket server is disabled") + return + } + + server, err := agentsocket.NewServer( + a.logger.Named("socket"), + agentsocket.WithPath(a.socketPath), + ) + if err != nil { + a.logger.Warn(a.hardCtx, "failed to create socket server", slog.Error(err), slog.F("path", a.socketPath)) + return + } + + a.socketServer = server + a.logger.Debug(a.hardCtx, "socket server started", slog.F("path", a.socketPath)) +} + // runLoop attempts to start the agent in a retry loop. // Coder may be offline temporarily, a connection issue // may be happening, but regardless after the intermittent @@ -1095,7 +1127,7 @@ func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context, if err != nil { return xerrors.Errorf("fetch metadata: %w", err) } - a.logger.Info(ctx, "fetched manifest", slog.F("manifest", mp)) + a.logger.Info(ctx, "fetched manifest") manifest, err := agentsdk.ManifestFromProto(mp) if err != nil { a.logger.Critical(ctx, "failed to convert manifest", slog.F("manifest", mp), slog.Error(err)) @@ -1928,6 +1960,7 @@ func (a *agent) Close() error { lifecycleState = codersdk.WorkspaceAgentLifecycleShutdownError } } + a.setLifecycle(lifecycleState) err = a.scriptRunner.Close() @@ -1935,6 +1968,12 @@ func (a *agent) Close() error { a.logger.Error(a.hardCtx, "script runner close", slog.Error(err)) } + if a.socketServer != nil { + if err := a.socketServer.Close(); err != nil { + a.logger.Error(a.hardCtx, "socket server close", slog.Error(err)) + } + } + if err := a.containerAPI.Close(); err != nil { a.logger.Error(a.hardCtx, "container API close", slog.Error(err)) } diff --git a/agent/agentcontainers/api_test.go b/agent/agentcontainers/api_test.go index 263f1698a71..2a381a895b8 100644 --- a/agent/agentcontainers/api_test.go +++ b/agent/agentcontainers/api_test.go @@ -2768,16 +2768,14 @@ func TestAPI(t *testing.T) { // Verify commands were executed through the custom shell and environment. require.NotEmpty(t, fakeExec.commands, "commands should be executed") - // Want: /bin/custom-shell -c '"docker" "ps" "--all" "--quiet" "--no-trunc"' + // Want: /bin/custom-shell -c "$@" "" docker ps --all --quiet --no-trunc + // The command is passed as positional parameters and run via "$@" so + // the shell forwards argv without re-parsing it. require.Equal(t, testShell, fakeExec.commands[0][0], "custom shell should be used") - if runtime.GOOS == "windows" { - require.Equal(t, "/c", fakeExec.commands[0][1], "shell should be called with /c on Windows") - } else { - require.Equal(t, "-c", fakeExec.commands[0][1], "shell should be called with -c") - } - require.Len(t, fakeExec.commands[0], 3, "command should have 3 arguments") - require.GreaterOrEqual(t, strings.Count(fakeExec.commands[0][2], " "), 2, "command/script should have multiple arguments") - require.True(t, strings.HasPrefix(fakeExec.commands[0][2], `"docker" "ps"`), "command should start with \"docker\" \"ps\"") + require.Equal(t, "-c", fakeExec.commands[0][1], "shell should be called with -c") + require.Equal(t, `"$@"`, fakeExec.commands[0][2], "script should run argv via \"$@\"") + require.Equal(t, "", fakeExec.commands[0][3], "$0 slot should be an empty placeholder") + require.Equal(t, []string{"docker", "ps", "--all", "--quiet", "--no-trunc"}, fakeExec.commands[0][4:], "argv should be passed through unquoted") // Verify the environment was set on the command. lastCmd := fakeExec.getLastCommand() diff --git a/agent/agentcontainers/execer.go b/agent/agentcontainers/execer.go index 323401f34ca..463adf395aa 100644 --- a/agent/agentcontainers/execer.go +++ b/agent/agentcontainers/execer.go @@ -2,10 +2,7 @@ package agentcontainers import ( "context" - "fmt" "os/exec" - "runtime" - "strings" "cdr.dev/slog" "github.com/coder/coder/v2/agent/agentexec" @@ -51,15 +48,15 @@ func (e *commandEnvExecer) prepare(ctx context.Context, inName string, inArgs .. return inName, inArgs, "", nil } - caller := "-c" - if runtime.GOOS == "windows" { - caller = "/c" - } name = shell - for _, arg := range append([]string{inName}, inArgs...) { - args = append(args, fmt.Sprintf("%q", arg)) - } - args = []string{caller, strings.Join(args, " ")} + // Pass the command through the shell as positional parameters and run + // "$@" so the shell re-emits argv verbatim without re-parsing it. This + // prevents arguments containing shell metacharacters such as $, `, and + // quotes from being interpreted (e.g. command substitution). The token + // before them fills $0, which "$@" never references, so it is discarded. + // This assumes a POSIX shell; Windows is not supported here. + cmdArgs := append([]string{inName}, inArgs...) + args = append([]string{"-c", `"$@"`, ""}, cmdArgs...) return name, args, dir, env } diff --git a/agent/agentcontainers/execer_internal_test.go b/agent/agentcontainers/execer_internal_test.go new file mode 100644 index 00000000000..855b07a174f --- /dev/null +++ b/agent/agentcontainers/execer_internal_test.go @@ -0,0 +1,84 @@ +package agentcontainers + +import ( + "bytes" + "context" + "path/filepath" + "runtime" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "cdr.dev/slog" + "cdr.dev/slog/sloggers/slogtest" + "github.com/coder/coder/v2/agent/agentexec" + "github.com/coder/coder/v2/agent/usershell" + "github.com/coder/coder/v2/testutil" +) + +func TestCommandEnvExecer_Prepare(t *testing.T) { + t.Parallel() + + if runtime.GOOS == "windows" { + t.Skip("the POSIX shell quoting under test does not apply on Windows") + } + + const shell = "/bin/sh" + commandEnv := func(usershell.EnvInfoer, []string) (string, string, []string, error) { + return shell, "/tmp", []string{"FOO=bar"}, nil + } + e := newCommandEnvExecer(slogtest.Make(t, nil).Leveled(slog.LevelDebug), commandEnv, agentexec.DefaultExecer) + + t.Run("ArgvPassthrough", func(t *testing.T) { + t.Parallel() + + name, args, dir, env := e.prepare(context.Background(), "echo", "hello", "world") + // The command is run as: shell -c "$@" "" so that the + // shell re-emits argv without re-parsing it. The empty $0 slot is + // discarded. + require.Equal(t, shell, name) + require.Equal(t, []string{"-c", `"$@"`, "", "echo", "hello", "world"}, args) + require.Equal(t, "/tmp", dir) + require.Equal(t, []string{"FOO=bar"}, env) + }) + + t.Run("MetacharactersNotInterpreted", func(t *testing.T) { + t.Parallel() + + payloads := []string{ + "$(echo INJECTED)", + "`echo INJECTED`", + "$HOME", + "a; echo INJECTED", + "a && echo INJECTED", + "a | echo INJECTED", + "a\necho INJECTED", + "it's a \"test\" \\ end", + "", + } + for _, payload := range payloads { + ctx := testutil.Context(t, testutil.WaitShort) + cmd := e.CommandContext(ctx, "printf", "%s", payload) + var out bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &out + require.NoError(t, cmd.Run(), "payload %q", payload) + assert.Equal(t, payload, out.String(), "payload %q was altered by the shell", payload) + } + }) + + t.Run("CommandSubstitutionHasNoSideEffect", func(t *testing.T) { + t.Parallel() + + marker := filepath.Join(t.TempDir(), "pwned") + ctx := testutil.Context(t, testutil.WaitShort) + cmd := e.CommandContext(ctx, "echo", "$(touch "+marker+")") + var out bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &out + require.NoError(t, cmd.Run()) + require.Equal(t, "$(touch "+marker+")\n", out.String()) + require.NoFileExists(t, marker, "command substitution executed; injection is possible") + }) +} diff --git a/agent/agentsocket/client.go b/agent/agentsocket/client.go new file mode 100644 index 00000000000..3ed629e5557 --- /dev/null +++ b/agent/agentsocket/client.go @@ -0,0 +1,149 @@ +package agentsocket + +import ( + "context" + + "golang.org/x/xerrors" + "storj.io/drpc" + "storj.io/drpc/drpcconn" + + "github.com/coder/coder/v2/agent/agentsocket/proto" + "github.com/coder/coder/v2/agent/unit" +) + +// Option represents a configuration option for NewClient. +type Option func(*options) + +type options struct { + path string +} + +// WithPath sets the socket path. If not provided or empty, the client will +// auto-discover the default socket path. +func WithPath(path string) Option { + return func(opts *options) { + if path == "" { + return + } + opts.path = path + } +} + +// Client provides a client for communicating with the workspace agentsocket API. +type Client struct { + client proto.DRPCAgentSocketClient + conn drpc.Conn +} + +// NewClient creates a new socket client and opens a connection to the socket. +// If path is not provided via WithPath or is empty, it will auto-discover the +// default socket path. +func NewClient(ctx context.Context, opts ...Option) (*Client, error) { + options := &options{} + for _, opt := range opts { + opt(options) + } + + conn, err := dialSocket(ctx, options.path) + if err != nil { + return nil, xerrors.Errorf("connect to socket: %w", err) + } + + drpcConn := drpcconn.New(conn) + client := proto.NewDRPCAgentSocketClient(drpcConn) + + return &Client{ + client: client, + conn: drpcConn, + }, nil +} + +// Close closes the socket connection. +func (c *Client) Close() error { + return c.conn.Close() +} + +// Ping sends a ping request to the agent. +func (c *Client) Ping(ctx context.Context) error { + _, err := c.client.Ping(ctx, &proto.PingRequest{}) + return err +} + +// SyncStart starts a unit in the dependency graph. +func (c *Client) SyncStart(ctx context.Context, unitName unit.ID) error { + _, err := c.client.SyncStart(ctx, &proto.SyncStartRequest{ + Unit: string(unitName), + }) + return err +} + +// SyncWant declares a dependency between units. +func (c *Client) SyncWant(ctx context.Context, unitName, dependsOn unit.ID) error { + _, err := c.client.SyncWant(ctx, &proto.SyncWantRequest{ + Unit: string(unitName), + DependsOn: string(dependsOn), + }) + return err +} + +// SyncComplete marks a unit as complete in the dependency graph. +func (c *Client) SyncComplete(ctx context.Context, unitName unit.ID) error { + _, err := c.client.SyncComplete(ctx, &proto.SyncCompleteRequest{ + Unit: string(unitName), + }) + return err +} + +// SyncReady requests whether a unit is ready to be started. That is, all dependencies are satisfied. +func (c *Client) SyncReady(ctx context.Context, unitName unit.ID) (bool, error) { + resp, err := c.client.SyncReady(ctx, &proto.SyncReadyRequest{ + Unit: string(unitName), + }) + if err != nil { + return false, xerrors.Errorf("sync ready: %w", err) + } + return resp.Ready, nil +} + +// SyncStatus gets the status of a unit and its dependencies. +func (c *Client) SyncStatus(ctx context.Context, unitName unit.ID) (SyncStatusResponse, error) { + resp, err := c.client.SyncStatus(ctx, &proto.SyncStatusRequest{ + Unit: string(unitName), + }) + if err != nil { + return SyncStatusResponse{}, err + } + + var dependencies []DependencyInfo + for _, dep := range resp.Dependencies { + dependencies = append(dependencies, DependencyInfo{ + DependsOn: unit.ID(dep.DependsOn), + RequiredStatus: unit.Status(dep.RequiredStatus), + CurrentStatus: unit.Status(dep.CurrentStatus), + IsSatisfied: dep.IsSatisfied, + }) + } + + return SyncStatusResponse{ + UnitName: unitName, + Status: unit.Status(resp.Status), + IsReady: resp.IsReady, + Dependencies: dependencies, + }, nil +} + +// SyncStatusResponse contains the status information for a unit. +type SyncStatusResponse struct { + UnitName unit.ID `table:"unit,default_sort" json:"unit_name"` + Status unit.Status `table:"status" json:"status"` + IsReady bool `table:"ready" json:"is_ready"` + Dependencies []DependencyInfo `table:"dependencies" json:"dependencies"` +} + +// DependencyInfo contains information about a unit dependency. +type DependencyInfo struct { + DependsOn unit.ID `table:"depends on,default_sort" json:"depends_on"` + RequiredStatus unit.Status `table:"required status" json:"required_status"` + CurrentStatus unit.Status `table:"current status" json:"current_status"` + IsSatisfied bool `table:"satisfied" json:"is_satisfied"` +} diff --git a/agent/agentsocket/server.go b/agent/agentsocket/server.go index c9f9a4ca427..eb82553527f 100644 --- a/agent/agentsocket/server.go +++ b/agent/agentsocket/server.go @@ -7,8 +7,6 @@ import ( "sync" "golang.org/x/xerrors" - - "github.com/hashicorp/yamux" "storj.io/drpc/drpcmux" "storj.io/drpc/drpcserver" @@ -33,11 +31,17 @@ type Server struct { wg sync.WaitGroup } -func NewServer(path string, logger slog.Logger) (*Server, error) { +// NewServer creates a new agent socket server. +func NewServer(logger slog.Logger, opts ...Option) (*Server, error) { + options := &options{} + for _, opt := range opts { + opt(options) + } + logger = logger.Named("agentsocket-server") server := &Server{ logger: logger, - path: path, + path: options.path, service: &DRPCAgentSocketService{ logger: logger, unitManager: unit.NewManager(), @@ -50,7 +54,7 @@ func NewServer(path string, logger slog.Logger) (*Server, error) { return nil, xerrors.Errorf("failed to register drpc service: %w", err) } - server.drpcServer = drpcserver.NewWithOptions(mux, drpcserver.Options{ + server.drpcServer = drpcsdk.NewServer(logger, mux, drpcserver.Options{ Manager: drpcsdk.DefaultDRPCOptions(nil), Log: func(err error) { if errors.Is(err, context.Canceled) || @@ -61,14 +65,6 @@ func NewServer(path string, logger slog.Logger) (*Server, error) { }, }) - if server.path == "" { - var err error - server.path, err = getDefaultSocketPath() - if err != nil { - return nil, xerrors.Errorf("get default socket path: %w", err) - } - } - listener, err := createSocket(server.path) if err != nil { return nil, xerrors.Errorf("create socket: %w", err) @@ -91,6 +87,7 @@ func NewServer(path string, logger slog.Logger) (*Server, error) { return server, nil } +// Close stops the server and cleans up resources. func (s *Server) Close() error { s.mu.Lock() @@ -134,52 +131,8 @@ func (s *Server) acceptConnections() { return } - for { - select { - case <-s.ctx.Done(): - return - default: - } - - conn, err := listener.Accept() - if err != nil { - s.logger.Warn(s.ctx, "error accepting connection", slog.Error(err)) - continue - } - - s.mu.Lock() - if s.listener == nil { - s.mu.Unlock() - _ = conn.Close() - return - } - s.wg.Add(1) - s.mu.Unlock() - - go func() { - defer s.wg.Done() - s.handleConnection(conn) - }() - } -} - -func (s *Server) handleConnection(conn net.Conn) { - defer conn.Close() - - s.logger.Debug(s.ctx, "new connection accepted", slog.F("remote_addr", conn.RemoteAddr())) - - config := yamux.DefaultConfig() - config.LogOutput = nil - config.Logger = slog.Stdlib(s.ctx, s.logger.Named("agentsocket-yamux"), slog.LevelInfo) - session, err := yamux.Server(conn, config) - if err != nil { - s.logger.Warn(s.ctx, "failed to create yamux session", slog.Error(err)) - return - } - defer session.Close() - - err = s.drpcServer.Serve(s.ctx, session) + err := s.drpcServer.Serve(s.ctx, listener) if err != nil { - s.logger.Debug(s.ctx, "drpc server finished", slog.Error(err)) + s.logger.Warn(s.ctx, "error serving drpc server", slog.Error(err)) } } diff --git a/agent/agentsocket/server_test.go b/agent/agentsocket/server_test.go index cf06aff170b..da74039c401 100644 --- a/agent/agentsocket/server_test.go +++ b/agent/agentsocket/server_test.go @@ -1,14 +1,24 @@ package agentsocket_test import ( + "context" "path/filepath" "runtime" "testing" + "github.com/google/uuid" + "github.com/spf13/afero" "github.com/stretchr/testify/require" "cdr.dev/slog" + "github.com/coder/coder/v2/agent" "github.com/coder/coder/v2/agent/agentsocket" + "github.com/coder/coder/v2/agent/agenttest" + agentproto "github.com/coder/coder/v2/agent/proto" + "github.com/coder/coder/v2/codersdk/agentsdk" + "github.com/coder/coder/v2/tailnet" + "github.com/coder/coder/v2/tailnet/tailnettest" + "github.com/coder/coder/v2/testutil" ) func TestServer(t *testing.T) { @@ -23,7 +33,7 @@ func TestServer(t *testing.T) { socketPath := filepath.Join(t.TempDir(), "test.sock") logger := slog.Make().Leveled(slog.LevelDebug) - server, err := agentsocket.NewServer(socketPath, logger) + server, err := agentsocket.NewServer(logger, agentsocket.WithPath(socketPath)) require.NoError(t, err) require.NoError(t, server.Close()) }) @@ -33,10 +43,10 @@ func TestServer(t *testing.T) { socketPath := filepath.Join(t.TempDir(), "test.sock") logger := slog.Make().Leveled(slog.LevelDebug) - server1, err := agentsocket.NewServer(socketPath, logger) + server1, err := agentsocket.NewServer(logger, agentsocket.WithPath(socketPath)) require.NoError(t, err) defer server1.Close() - _, err = agentsocket.NewServer(socketPath, logger) + _, err = agentsocket.NewServer(logger, agentsocket.WithPath(socketPath)) require.ErrorContains(t, err, "create socket") }) @@ -45,8 +55,84 @@ func TestServer(t *testing.T) { socketPath := filepath.Join(t.TempDir(), "test.sock") logger := slog.Make().Leveled(slog.LevelDebug) - server, err := agentsocket.NewServer(socketPath, logger) + server, err := agentsocket.NewServer(logger, agentsocket.WithPath(socketPath)) require.NoError(t, err) require.NoError(t, server.Close()) }) } + +func TestServerWindowsNotSupported(t *testing.T) { + t.Parallel() + + if runtime.GOOS != "windows" { + t.Skip("this test only runs on Windows") + } + + t.Run("NewServer", func(t *testing.T) { + t.Parallel() + + socketPath := filepath.Join(t.TempDir(), "test.sock") + logger := slog.Make().Leveled(slog.LevelDebug) + _, err := agentsocket.NewServer(logger, agentsocket.WithPath(socketPath)) + require.ErrorContains(t, err, "agentsocket is not supported on Windows") + }) + + t.Run("NewClient", func(t *testing.T) { + t.Parallel() + + _, err := agentsocket.NewClient(context.Background(), agentsocket.WithPath("test.sock")) + require.ErrorContains(t, err, "agentsocket is not supported on Windows") + }) +} + +func TestAgentInitializesOnWindowsWithoutSocketServer(t *testing.T) { + t.Parallel() + + if runtime.GOOS != "windows" { + t.Skip("this test only runs on Windows") + } + + ctx := testutil.Context(t, testutil.WaitShort) + logger := testutil.Logger(t).Named("agent") + + derpMap, _ := tailnettest.RunDERPAndSTUN(t) + + coordinator := tailnet.NewCoordinator(logger) + t.Cleanup(func() { + _ = coordinator.Close() + }) + + statsCh := make(chan *agentproto.Stats, 50) + agentID := uuid.New() + manifest := agentsdk.Manifest{ + AgentID: agentID, + AgentName: "test-agent", + WorkspaceName: "test-workspace", + OwnerName: "test-user", + WorkspaceID: uuid.New(), + DERPMap: derpMap, + } + + client := agenttest.NewClient(t, logger.Named("agenttest"), agentID, manifest, statsCh, coordinator) + t.Cleanup(client.Close) + + options := agent.Options{ + Client: client, + Filesystem: afero.NewMemMapFs(), + Logger: logger.Named("agent"), + ReconnectingPTYTimeout: testutil.WaitShort, + EnvironmentVariables: map[string]string{}, + SocketPath: "", + } + + agnt := agent.New(options) + t.Cleanup(func() { + _ = agnt.Close() + }) + + startup := testutil.TryReceive(ctx, t, client.GetStartup()) + require.NotNil(t, startup, "agent should send startup message") + + err := agnt.Close() + require.NoError(t, err, "agent should close cleanly") +} diff --git a/agent/agentsocket/service.go b/agent/agentsocket/service.go index f3384dfd439..60248a8fe68 100644 --- a/agent/agentsocket/service.go +++ b/agent/agentsocket/service.go @@ -15,15 +15,18 @@ var _ proto.DRPCAgentSocketServer = (*DRPCAgentSocketService)(nil) var ErrUnitManagerNotAvailable = xerrors.New("unit manager not available") +// DRPCAgentSocketService implements the DRPC agent socket service. type DRPCAgentSocketService struct { unitManager *unit.Manager logger slog.Logger } +// Ping responds to a ping request to check if the service is alive. func (*DRPCAgentSocketService) Ping(_ context.Context, _ *proto.PingRequest) (*proto.PingResponse, error) { return &proto.PingResponse{}, nil } +// SyncStart starts a unit in the dependency graph. func (s *DRPCAgentSocketService) SyncStart(_ context.Context, req *proto.SyncStartRequest) (*proto.SyncStartResponse, error) { if s.unitManager == nil { return nil, xerrors.Errorf("SyncStart: %w", ErrUnitManagerNotAvailable) @@ -53,6 +56,7 @@ func (s *DRPCAgentSocketService) SyncStart(_ context.Context, req *proto.SyncSta return &proto.SyncStartResponse{}, nil } +// SyncWant declares a dependency between units. func (s *DRPCAgentSocketService) SyncWant(_ context.Context, req *proto.SyncWantRequest) (*proto.SyncWantResponse, error) { if s.unitManager == nil { return nil, xerrors.Errorf("cannot add dependency: %w", ErrUnitManagerNotAvailable) @@ -72,6 +76,7 @@ func (s *DRPCAgentSocketService) SyncWant(_ context.Context, req *proto.SyncWant return &proto.SyncWantResponse{}, nil } +// SyncComplete marks a unit as complete in the dependency graph. func (s *DRPCAgentSocketService) SyncComplete(_ context.Context, req *proto.SyncCompleteRequest) (*proto.SyncCompleteResponse, error) { if s.unitManager == nil { return nil, xerrors.Errorf("cannot complete unit: %w", ErrUnitManagerNotAvailable) @@ -86,6 +91,7 @@ func (s *DRPCAgentSocketService) SyncComplete(_ context.Context, req *proto.Sync return &proto.SyncCompleteResponse{}, nil } +// SyncReady checks whether a unit is ready to be started. That is, all dependencies are satisfied. func (s *DRPCAgentSocketService) SyncReady(_ context.Context, req *proto.SyncReadyRequest) (*proto.SyncReadyResponse, error) { if s.unitManager == nil { return nil, xerrors.Errorf("cannot check readiness: %w", ErrUnitManagerNotAvailable) @@ -102,6 +108,7 @@ func (s *DRPCAgentSocketService) SyncReady(_ context.Context, req *proto.SyncRea }, nil } +// SyncStatus gets the status of a unit and lists its dependencies. func (s *DRPCAgentSocketService) SyncStatus(_ context.Context, req *proto.SyncStatusRequest) (*proto.SyncStatusResponse, error) { if s.unitManager == nil { return nil, xerrors.Errorf("cannot get status for unit %q: %w", req.Unit, ErrUnitManagerNotAvailable) @@ -115,8 +122,11 @@ func (s *DRPCAgentSocketService) SyncStatus(_ context.Context, req *proto.SyncSt } dependencies, err := s.unitManager.GetAllDependencies(unitID) - if err != nil { - return nil, xerrors.Errorf("failed to get dependencies: %w", err) + switch { + case errors.Is(err, unit.ErrUnitNotFound): + dependencies = []unit.Dependency{} + case err != nil: + return nil, xerrors.Errorf("cannot get dependencies: %w", err) } var depInfos []*proto.DependencyInfo diff --git a/agent/agentsocket/service_test.go b/agent/agentsocket/service_test.go index 0d6be345b92..925703b63f7 100644 --- a/agent/agentsocket/service_test.go +++ b/agent/agentsocket/service_test.go @@ -5,21 +5,18 @@ import ( "crypto/sha256" "encoding/hex" "fmt" - "net" "os" "path/filepath" "runtime" "testing" - "github.com/hashicorp/yamux" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "cdr.dev/slog" "github.com/coder/coder/v2/agent/agentsocket" - "github.com/coder/coder/v2/agent/agentsocket/proto" "github.com/coder/coder/v2/agent/unit" - "github.com/coder/coder/v2/codersdk/drpcsdk" + "github.com/coder/coder/v2/testutil" ) // tempDirUnixSocket returns a temporary directory that can safely hold unix @@ -47,23 +44,15 @@ func tempDirUnixSocket(t *testing.T) string { } // newSocketClient creates a DRPC client connected to the Unix socket at the given path. -func newSocketClient(t *testing.T, socketPath string) proto.DRPCAgentSocketClient { +func newSocketClient(ctx context.Context, t *testing.T, socketPath string) *agentsocket.Client { t.Helper() - conn, err := net.Dial("unix", socketPath) - require.NoError(t, err) - - config := yamux.DefaultConfig() - config.Logger = nil - session, err := yamux.Client(conn, config) - require.NoError(t, err) - - client := proto.NewDRPCAgentSocketClient(drpcsdk.MultiplexedConn(session)) - + client, err := agentsocket.NewClient(ctx, agentsocket.WithPath(socketPath)) t.Cleanup(func() { - _ = session.Close() - _ = conn.Close() + _ = client.Close() }) + require.NoError(t, err) + return client } @@ -78,17 +67,17 @@ func TestDRPCAgentSocketService(t *testing.T) { t.Parallel() socketPath := filepath.Join(tempDirUnixSocket(t), "test.sock") - + ctx := testutil.Context(t, testutil.WaitShort) server, err := agentsocket.NewServer( - socketPath, slog.Make().Leveled(slog.LevelDebug), + agentsocket.WithPath(socketPath), ) require.NoError(t, err) defer server.Close() - client := newSocketClient(t, socketPath) + client := newSocketClient(ctx, t, socketPath) - _, err = client.Ping(context.Background(), &proto.PingRequest{}) + err = client.Ping(ctx) require.NoError(t, err) }) @@ -98,147 +87,116 @@ func TestDRPCAgentSocketService(t *testing.T) { t.Run("NewUnit", func(t *testing.T) { t.Parallel() socketPath := filepath.Join(tempDirUnixSocket(t), "test.sock") - + ctx := testutil.Context(t, testutil.WaitShort) server, err := agentsocket.NewServer( - socketPath, slog.Make().Leveled(slog.LevelDebug), + agentsocket.WithPath(socketPath), ) require.NoError(t, err) defer server.Close() - client := newSocketClient(t, socketPath) + client := newSocketClient(ctx, t, socketPath) - _, err = client.SyncStart(context.Background(), &proto.SyncStartRequest{ - Unit: "test-unit", - }) + err = client.SyncStart(ctx, "test-unit") require.NoError(t, err) - status, err := client.SyncStatus(context.Background(), &proto.SyncStatusRequest{ - Unit: "test-unit", - }) + status, err := client.SyncStatus(ctx, "test-unit") require.NoError(t, err) - require.Equal(t, "started", status.Status) + require.Equal(t, unit.StatusStarted, status.Status) }) t.Run("UnitAlreadyStarted", func(t *testing.T) { t.Parallel() socketPath := filepath.Join(tempDirUnixSocket(t), "test.sock") - + ctx := testutil.Context(t, testutil.WaitShort) server, err := agentsocket.NewServer( - socketPath, slog.Make().Leveled(slog.LevelDebug), + agentsocket.WithPath(socketPath), ) require.NoError(t, err) defer server.Close() - client := newSocketClient(t, socketPath) + client := newSocketClient(ctx, t, socketPath) // First Start - _, err = client.SyncStart(context.Background(), &proto.SyncStartRequest{ - Unit: "test-unit", - }) + err = client.SyncStart(ctx, "test-unit") require.NoError(t, err) - status, err := client.SyncStatus(context.Background(), &proto.SyncStatusRequest{ - Unit: "test-unit", - }) + status, err := client.SyncStatus(ctx, "test-unit") require.NoError(t, err) - require.Equal(t, "started", status.Status) + require.Equal(t, unit.StatusStarted, status.Status) // Second Start - _, err = client.SyncStart(context.Background(), &proto.SyncStartRequest{ - Unit: "test-unit", - }) + err = client.SyncStart(ctx, "test-unit") require.ErrorContains(t, err, unit.ErrSameStatusAlreadySet.Error()) - status, err = client.SyncStatus(context.Background(), &proto.SyncStatusRequest{ - Unit: "test-unit", - }) + status, err = client.SyncStatus(ctx, "test-unit") require.NoError(t, err) - require.Equal(t, "started", status.Status) + require.Equal(t, unit.StatusStarted, status.Status) }) t.Run("UnitAlreadyCompleted", func(t *testing.T) { t.Parallel() socketPath := filepath.Join(tempDirUnixSocket(t), "test.sock") - + ctx := testutil.Context(t, testutil.WaitShort) server, err := agentsocket.NewServer( - socketPath, slog.Make().Leveled(slog.LevelDebug), + agentsocket.WithPath(socketPath), ) require.NoError(t, err) defer server.Close() - client := newSocketClient(t, socketPath) + client := newSocketClient(ctx, t, socketPath) // First start - _, err = client.SyncStart(context.Background(), &proto.SyncStartRequest{ - Unit: "test-unit", - }) + err = client.SyncStart(ctx, "test-unit") require.NoError(t, err) - status, err := client.SyncStatus(context.Background(), &proto.SyncStatusRequest{ - Unit: "test-unit", - }) + status, err := client.SyncStatus(ctx, "test-unit") require.NoError(t, err) - require.Equal(t, "started", status.Status) + require.Equal(t, unit.StatusStarted, status.Status) // Complete the unit - _, err = client.SyncComplete(context.Background(), &proto.SyncCompleteRequest{ - Unit: "test-unit", - }) + err = client.SyncComplete(ctx, "test-unit") require.NoError(t, err) - status, err = client.SyncStatus(context.Background(), &proto.SyncStatusRequest{ - Unit: "test-unit", - }) + status, err = client.SyncStatus(ctx, "test-unit") require.NoError(t, err) - require.Equal(t, "completed", status.Status) + require.Equal(t, unit.StatusComplete, status.Status) // Second start - _, err = client.SyncStart(context.Background(), &proto.SyncStartRequest{ - Unit: "test-unit", - }) + err = client.SyncStart(ctx, "test-unit") require.NoError(t, err) - status, err = client.SyncStatus(context.Background(), &proto.SyncStatusRequest{ - Unit: "test-unit", - }) + status, err = client.SyncStatus(ctx, "test-unit") require.NoError(t, err) - require.Equal(t, "started", status.Status) + require.Equal(t, unit.StatusStarted, status.Status) }) t.Run("UnitNotReady", func(t *testing.T) { t.Parallel() socketPath := filepath.Join(tempDirUnixSocket(t), "test.sock") - + ctx := testutil.Context(t, testutil.WaitShort) server, err := agentsocket.NewServer( - socketPath, slog.Make().Leveled(slog.LevelDebug), + agentsocket.WithPath(socketPath), ) require.NoError(t, err) defer server.Close() - client := newSocketClient(t, socketPath) + client := newSocketClient(ctx, t, socketPath) - _, err = client.SyncWant(context.Background(), &proto.SyncWantRequest{ - Unit: "test-unit", - DependsOn: "dependency-unit", - }) + err = client.SyncWant(ctx, "test-unit", "dependency-unit") require.NoError(t, err) - _, err = client.SyncStart(context.Background(), &proto.SyncStartRequest{ - Unit: "test-unit", - }) + err = client.SyncStart(ctx, "test-unit") require.ErrorContains(t, err, "unit not ready") - status, err := client.SyncStatus(context.Background(), &proto.SyncStatusRequest{ - Unit: "test-unit", - }) + status, err := client.SyncStatus(ctx, "test-unit") require.NoError(t, err) - require.Equal(t, string(unit.StatusPending), status.Status) + require.Equal(t, unit.StatusPending, status.Status) require.False(t, status.IsReady) }) }) @@ -250,107 +208,86 @@ func TestDRPCAgentSocketService(t *testing.T) { t.Parallel() socketPath := filepath.Join(tempDirUnixSocket(t), "test.sock") - + ctx := testutil.Context(t, testutil.WaitShort) server, err := agentsocket.NewServer( - socketPath, slog.Make().Leveled(slog.LevelDebug), + agentsocket.WithPath(socketPath), ) require.NoError(t, err) defer server.Close() - client := newSocketClient(t, socketPath) + client := newSocketClient(ctx, t, socketPath) // If dependency units are not registered, they are registered automatically - _, err = client.SyncWant(context.Background(), &proto.SyncWantRequest{ - Unit: "test-unit", - DependsOn: "dependency-unit", - }) + err = client.SyncWant(ctx, "test-unit", "dependency-unit") require.NoError(t, err) - status, err := client.SyncStatus(context.Background(), &proto.SyncStatusRequest{ - Unit: "test-unit", - }) + status, err := client.SyncStatus(ctx, "test-unit") require.NoError(t, err) require.Len(t, status.Dependencies, 1) - require.Equal(t, "dependency-unit", status.Dependencies[0].DependsOn) - require.Equal(t, "completed", status.Dependencies[0].RequiredStatus) + require.Equal(t, unit.ID("dependency-unit"), status.Dependencies[0].DependsOn) + require.Equal(t, unit.StatusComplete, status.Dependencies[0].RequiredStatus) }) t.Run("DependencyAlreadyRegistered", func(t *testing.T) { t.Parallel() socketPath := filepath.Join(tempDirUnixSocket(t), "test.sock") - + ctx := testutil.Context(t, testutil.WaitShort) server, err := agentsocket.NewServer( - socketPath, slog.Make().Leveled(slog.LevelDebug), + agentsocket.WithPath(socketPath), ) require.NoError(t, err) defer server.Close() - client := newSocketClient(t, socketPath) + client := newSocketClient(ctx, t, socketPath) // Start the dependency unit - _, err = client.SyncStart(context.Background(), &proto.SyncStartRequest{ - Unit: "dependency-unit", - }) + err = client.SyncStart(ctx, "dependency-unit") require.NoError(t, err) - status, err := client.SyncStatus(context.Background(), &proto.SyncStatusRequest{ - Unit: "dependency-unit", - }) + status, err := client.SyncStatus(ctx, "dependency-unit") require.NoError(t, err) - require.Equal(t, "started", status.Status) + require.Equal(t, unit.StatusStarted, status.Status) // Add the dependency after the dependency unit has already started - _, err = client.SyncWant(context.Background(), &proto.SyncWantRequest{ - Unit: "test-unit", - DependsOn: "dependency-unit", - }) + err = client.SyncWant(ctx, "test-unit", "dependency-unit") // Dependencies can be added even if the dependency unit has already started require.NoError(t, err) // The dependency is now reflected in the test unit's status - status, err = client.SyncStatus(context.Background(), &proto.SyncStatusRequest{ - Unit: "test-unit", - }) + status, err = client.SyncStatus(ctx, "test-unit") require.NoError(t, err) - require.Equal(t, "dependency-unit", status.Dependencies[0].DependsOn) - require.Equal(t, "completed", status.Dependencies[0].RequiredStatus) + require.Equal(t, unit.ID("dependency-unit"), status.Dependencies[0].DependsOn) + require.Equal(t, unit.StatusComplete, status.Dependencies[0].RequiredStatus) }) t.Run("DependencyAddedAfterDependentStarted", func(t *testing.T) { t.Parallel() socketPath := filepath.Join(tempDirUnixSocket(t), "test.sock") - + ctx := testutil.Context(t, testutil.WaitShort) server, err := agentsocket.NewServer( - socketPath, slog.Make().Leveled(slog.LevelDebug), + agentsocket.WithPath(socketPath), ) require.NoError(t, err) defer server.Close() - client := newSocketClient(t, socketPath) + client := newSocketClient(ctx, t, socketPath) // Start the dependent unit - _, err = client.SyncStart(context.Background(), &proto.SyncStartRequest{ - Unit: "test-unit", - }) + err = client.SyncStart(ctx, "test-unit") require.NoError(t, err) - status, err := client.SyncStatus(context.Background(), &proto.SyncStatusRequest{ - Unit: "test-unit", - }) + status, err := client.SyncStatus(ctx, "test-unit") require.NoError(t, err) - require.Equal(t, "started", status.Status) + require.Equal(t, unit.StatusStarted, status.Status) // Add the dependency after the dependency unit has already started - _, err = client.SyncWant(context.Background(), &proto.SyncWantRequest{ - Unit: "test-unit", - DependsOn: "dependency-unit", - }) + err = client.SyncWant(ctx, "test-unit", "dependency-unit") // Dependencies can be added even if the dependent unit has already started. // The dependency applies the next time a unit is started. The current status is not updated. @@ -359,12 +296,10 @@ func TestDRPCAgentSocketService(t *testing.T) { require.NoError(t, err) // The dependency is now reflected in the test unit's status - status, err = client.SyncStatus(context.Background(), &proto.SyncStatusRequest{ - Unit: "test-unit", - }) + status, err = client.SyncStatus(ctx, "test-unit") require.NoError(t, err) - require.Equal(t, "dependency-unit", status.Dependencies[0].DependsOn) - require.Equal(t, "completed", status.Dependencies[0].RequiredStatus) + require.Equal(t, unit.ID("dependency-unit"), status.Dependencies[0].DependsOn) + require.Equal(t, unit.StatusComplete, status.Dependencies[0].RequiredStatus) }) }) @@ -375,96 +310,80 @@ func TestDRPCAgentSocketService(t *testing.T) { t.Parallel() socketPath := filepath.Join(tempDirUnixSocket(t), "test.sock") - + ctx := testutil.Context(t, testutil.WaitShort) server, err := agentsocket.NewServer( - socketPath, slog.Make().Leveled(slog.LevelDebug), + agentsocket.WithPath(socketPath), ) require.NoError(t, err) defer server.Close() - client := newSocketClient(t, socketPath) + client := newSocketClient(ctx, t, socketPath) - response, err := client.SyncReady(context.Background(), &proto.SyncReadyRequest{ - Unit: "unregistered-unit", - }) + ready, err := client.SyncReady(ctx, "unregistered-unit") require.NoError(t, err) - require.False(t, response.Ready) + require.True(t, ready) }) t.Run("UnitNotReady", func(t *testing.T) { t.Parallel() socketPath := filepath.Join(tempDirUnixSocket(t), "test.sock") - + ctx := testutil.Context(t, testutil.WaitShort) server, err := agentsocket.NewServer( - socketPath, slog.Make().Leveled(slog.LevelDebug), + agentsocket.WithPath(socketPath), ) require.NoError(t, err) defer server.Close() - client := newSocketClient(t, socketPath) + client := newSocketClient(ctx, t, socketPath) // Register a unit with an unsatisfied dependency - _, err = client.SyncWant(context.Background(), &proto.SyncWantRequest{ - Unit: "test-unit", - DependsOn: "dependency-unit", - }) + err = client.SyncWant(ctx, "test-unit", "dependency-unit") require.NoError(t, err) // Check readiness - should be false because dependency is not satisfied - response, err := client.SyncReady(context.Background(), &proto.SyncReadyRequest{ - Unit: "test-unit", - }) + ready, err := client.SyncReady(ctx, "test-unit") require.NoError(t, err) - require.False(t, response.Ready) + require.False(t, ready) }) t.Run("UnitReady", func(t *testing.T) { t.Parallel() socketPath := filepath.Join(tempDirUnixSocket(t), "test.sock") - + ctx := testutil.Context(t, testutil.WaitShort) server, err := agentsocket.NewServer( - socketPath, slog.Make().Leveled(slog.LevelDebug), + agentsocket.WithPath(socketPath), ) require.NoError(t, err) defer server.Close() - client := newSocketClient(t, socketPath) + client := newSocketClient(ctx, t, socketPath) // Register a unit with no dependencies - should be ready immediately - _, err = client.SyncStart(context.Background(), &proto.SyncStartRequest{ - Unit: "test-unit", - }) + err = client.SyncStart(ctx, "test-unit") require.NoError(t, err) // Check readiness - should be true - _, err = client.SyncReady(context.Background(), &proto.SyncReadyRequest{ - Unit: "test-unit", - }) + ready, err := client.SyncReady(ctx, "test-unit") require.NoError(t, err) + require.True(t, ready) // Also test a unit with satisfied dependencies - _, err = client.SyncWant(context.Background(), &proto.SyncWantRequest{ - Unit: "dependent-unit", - DependsOn: "test-unit", - }) + err = client.SyncWant(ctx, "dependent-unit", "test-unit") require.NoError(t, err) // Complete the dependency - _, err = client.SyncComplete(context.Background(), &proto.SyncCompleteRequest{ - Unit: "test-unit", - }) + err = client.SyncComplete(ctx, "test-unit") require.NoError(t, err) // Now dependent-unit should be ready - _, err = client.SyncReady(context.Background(), &proto.SyncReadyRequest{ - Unit: "dependent-unit", - }) + ready, err = client.SyncReady(ctx, "dependent-unit") require.NoError(t, err) + require.True(t, ready) }) }) } diff --git a/agent/agentsocket/socket_unix.go b/agent/agentsocket/socket_unix.go index 0fa062656ae..7492fb1d033 100644 --- a/agent/agentsocket/socket_unix.go +++ b/agent/agentsocket/socket_unix.go @@ -3,8 +3,7 @@ package agentsocket import ( - "crypto/rand" - "encoding/hex" + "context" "net" "os" "path/filepath" @@ -13,8 +12,13 @@ import ( "golang.org/x/xerrors" ) -// createSocket creates a Unix domain socket listener +const defaultSocketPath = "/tmp/coder-agent.sock" + func createSocket(path string) (net.Listener, error) { + if path == "" { + path = defaultSocketPath + } + if !isSocketAvailable(path) { return nil, xerrors.Errorf("socket path %s is not available", path) } @@ -23,7 +27,6 @@ func createSocket(path string) (net.Listener, error) { return nil, xerrors.Errorf("remove existing socket: %w", err) } - // Create parent directory if it doesn't exist parentDir := filepath.Dir(path) if err := os.MkdirAll(parentDir, 0o700); err != nil { return nil, xerrors.Errorf("create socket directory: %w", err) @@ -41,43 +44,30 @@ func createSocket(path string) (net.Listener, error) { return listener, nil } -// getDefaultSocketPath returns the default socket path for Unix-like systems -func getDefaultSocketPath() (string, error) { - randomBytes := make([]byte, 4) - if _, err := rand.Read(randomBytes); err != nil { - return "", xerrors.Errorf("generate random socket name: %w", err) - } - randomSuffix := hex.EncodeToString(randomBytes) - - // Try XDG_RUNTIME_DIR first - if runtimeDir := os.Getenv("XDG_RUNTIME_DIR"); runtimeDir != "" { - return filepath.Join(runtimeDir, "coder-agent-"+randomSuffix+".sock"), nil - } - - return filepath.Join("/tmp", "coder-agent-"+randomSuffix+".sock"), nil -} - -// CleanupSocket removes the socket file func cleanupSocket(path string) error { return os.Remove(path) } -// isSocketAvailable checks if a socket path is available for use func isSocketAvailable(path string) bool { - // Check if file exists if _, err := os.Stat(path); os.IsNotExist(err) { return true } - // Try to connect to see if it's actually listening + // Try to connect to see if it's actually listening. dialer := net.Dialer{Timeout: 10 * time.Second} conn, err := dialer.Dial("unix", path) if err != nil { - // If we can't connect, the socket is not in use - // Socket is available for use return true } _ = conn.Close() - // Socket is in use return false } + +func dialSocket(ctx context.Context, path string) (net.Conn, error) { + if path == "" { + path = defaultSocketPath + } + + dialer := net.Dialer{} + return dialer.DialContext(ctx, "unix", path) +} diff --git a/agent/agentsocket/socket_windows.go b/agent/agentsocket/socket_windows.go index 69785de43e9..e39c8ae3d92 100644 --- a/agent/agentsocket/socket_windows.go +++ b/agent/agentsocket/socket_windows.go @@ -3,25 +3,20 @@ package agentsocket import ( + "context" "net" "golang.org/x/xerrors" ) -// createSocket returns an error indicating that agentsocket is not supported on Windows. -// This feature is unix-only in its current experimental state. func createSocket(_ string) (net.Listener, error) { return nil, xerrors.New("agentsocket is not supported on Windows") } -// getDefaultSocketPath returns an error indicating that agentsocket is not supported on Windows. -// This feature is unix-only in its current experimental state. -func getDefaultSocketPath() (string, error) { - return "", xerrors.New("agentsocket is not supported on Windows") -} - -// cleanupSocket is a no-op on Windows since agentsocket is not supported. func cleanupSocket(_ string) error { - // No-op since agentsocket is not supported on Windows return nil } + +func dialSocket(_ context.Context, _ string) (net.Conn, error) { + return nil, xerrors.New("agentsocket is not supported on Windows") +} diff --git a/agent/agenttest/client.go b/agent/agenttest/client.go index ff601a7d083..295d9dae0e4 100644 --- a/agent/agenttest/client.go +++ b/agent/agenttest/client.go @@ -61,6 +61,7 @@ func NewClient(t testing.TB, fakeAAPI := NewFakeAgentAPI(t, logger, mp, statsChan) err = agentproto.DRPCRegisterAgent(mux, fakeAAPI) require.NoError(t, err) + // Keep panics unrecovered in this test server so they fail tests loudly. server := drpcserver.NewWithOptions(mux, drpcserver.Options{ Manager: drpcsdk.DefaultDRPCOptions(nil), Log: func(err error) { diff --git a/agent/api.go b/agent/api.go index a631286c40a..bf67f6384d3 100644 --- a/agent/api.go +++ b/agent/api.go @@ -19,7 +19,7 @@ func (a *agent) apiHandler() http.Handler { r.Use( httpmw.Recover(a.logger), tracing.StatusWriterMiddleware, - loggermw.Logger(a.logger), + loggermw.Logger(a.logger, nil), ) r.Get("/", func(rw http.ResponseWriter, r *http.Request) { httpapi.Write(r.Context(), rw, http.StatusOK, codersdk.Response{ diff --git a/agent/reaper/reaper.go b/agent/reaper/reaper.go index 94f5190d118..eeed192dca3 100644 --- a/agent/reaper/reaper.go +++ b/agent/reaper/reaper.go @@ -4,6 +4,8 @@ import ( "os" "github.com/hashicorp/go-reap" + + "cdr.dev/slog" ) type Option func(o *options) @@ -34,8 +36,15 @@ func WithCatchSignals(sigs ...os.Signal) Option { } } +func WithLogger(logger slog.Logger) Option { + return func(o *options) { + o.Logger = logger + } +} + type options struct { ExecArgs []string PIDs reap.PidCh CatchSignals []os.Signal + Logger slog.Logger } diff --git a/agent/reaper/reaper_stub.go b/agent/reaper/reaper_stub.go index 8cd87ab0bf3..da4d871fc59 100644 --- a/agent/reaper/reaper_stub.go +++ b/agent/reaper/reaper_stub.go @@ -7,6 +7,6 @@ func IsInitProcess() bool { return false } -func ForkReap(_ ...Option) error { - return nil +func ForkReap(_ ...Option) (int, error) { + return 0, nil } diff --git a/agent/reaper/reaper_test.go b/agent/reaper/reaper_test.go index 84246fba061..7ef3f0a50b9 100644 --- a/agent/reaper/reaper_test.go +++ b/agent/reaper/reaper_test.go @@ -32,12 +32,13 @@ func TestReap(t *testing.T) { } pids := make(reap.PidCh, 1) - err := reaper.ForkReap( + exitCode, err := reaper.ForkReap( reaper.WithPIDCallback(pids), // Provide some argument that immediately exits. reaper.WithExecArgs("/bin/sh", "-c", "exit 0"), ) require.NoError(t, err) + require.Equal(t, 0, exitCode) cmd := exec.Command("tail", "-f", "/dev/null") err = cmd.Start() @@ -65,6 +66,36 @@ func TestReap(t *testing.T) { } } +//nolint:paralleltest +func TestForkReapExitCodes(t *testing.T) { + if testutil.InCI() { + t.Skip("Detected CI, skipping reaper tests") + } + + tests := []struct { + name string + command string + expectedCode int + }{ + {"exit 0", "exit 0", 0}, + {"exit 1", "exit 1", 1}, + {"exit 42", "exit 42", 42}, + {"exit 255", "exit 255", 255}, + {"SIGKILL", "kill -9 $$", 128 + 9}, + {"SIGTERM", "kill -15 $$", 128 + 15}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + exitCode, err := reaper.ForkReap( + reaper.WithExecArgs("/bin/sh", "-c", tt.command), + ) + require.NoError(t, err) + require.Equal(t, tt.expectedCode, exitCode, "exit code mismatch for %q", tt.command) + }) + } +} + //nolint:paralleltest // Signal handling. func TestReapInterrupt(t *testing.T) { // Don't run the reaper test in CI. It does weird @@ -84,13 +115,17 @@ func TestReapInterrupt(t *testing.T) { defer signal.Stop(usrSig) go func() { - errC <- reaper.ForkReap( + exitCode, err := reaper.ForkReap( reaper.WithPIDCallback(pids), reaper.WithCatchSignals(os.Interrupt), // Signal propagation does not extend to children of children, so // we create a little bash script to ensure sleep is interrupted. reaper.WithExecArgs("/bin/sh", "-c", fmt.Sprintf("pid=0; trap 'kill -USR2 %d; kill -TERM $pid' INT; sleep 10 &\npid=$!; kill -USR1 %d; wait", os.Getpid(), os.Getpid())), ) + // The child exits with 128 + SIGTERM (15) = 143, but the trap catches + // SIGINT and sends SIGTERM to the sleep process, so exit code varies. + _ = exitCode + errC <- err }() require.Equal(t, <-usrSig, syscall.SIGUSR1) diff --git a/agent/reaper/reaper_unix.go b/agent/reaper/reaper_unix.go index 35ce9bfaa1c..1e6720fd8c1 100644 --- a/agent/reaper/reaper_unix.go +++ b/agent/reaper/reaper_unix.go @@ -3,12 +3,15 @@ package reaper import ( + "context" "os" "os/signal" "syscall" "github.com/hashicorp/go-reap" "golang.org/x/xerrors" + + "cdr.dev/slog" ) // IsInitProcess returns true if the current process's PID is 1. @@ -16,7 +19,7 @@ func IsInitProcess() bool { return os.Getpid() == 1 } -func catchSignals(pid int, sigs []os.Signal) { +func catchSignals(logger slog.Logger, pid int, sigs []os.Signal) { if len(sigs) == 0 { return } @@ -25,10 +28,19 @@ func catchSignals(pid int, sigs []os.Signal) { signal.Notify(sc, sigs...) defer signal.Stop(sc) + logger.Info(context.Background(), "reaper catching signals", + slog.F("signals", sigs), + slog.F("child_pid", pid), + ) + for { s := <-sc sig, ok := s.(syscall.Signal) if ok { + logger.Info(context.Background(), "reaper caught signal, killing child process", + slog.F("signal", sig.String()), + slog.F("child_pid", pid), + ) _ = syscall.Kill(pid, sig) } } @@ -40,7 +52,10 @@ func catchSignals(pid int, sigs []os.Signal) { // the reaper and an exec.Command waiting for its process to complete. // The provided 'pids' channel may be nil if the caller does not care about the // reaped children PIDs. -func ForkReap(opt ...Option) error { +// +// Returns the child's exit code (using 128+signal for signal termination) +// and any error from Wait4. +func ForkReap(opt ...Option) (int, error) { opts := &options{ ExecArgs: os.Args, } @@ -53,7 +68,7 @@ func ForkReap(opt ...Option) error { pwd, err := os.Getwd() if err != nil { - return xerrors.Errorf("get wd: %w", err) + return 1, xerrors.Errorf("get wd: %w", err) } pattrs := &syscall.ProcAttr{ @@ -72,15 +87,28 @@ func ForkReap(opt ...Option) error { //#nosec G204 pid, err := syscall.ForkExec(opts.ExecArgs[0], opts.ExecArgs, pattrs) if err != nil { - return xerrors.Errorf("fork exec: %w", err) + return 1, xerrors.Errorf("fork exec: %w", err) } - go catchSignals(pid, opts.CatchSignals) + go catchSignals(opts.Logger, pid, opts.CatchSignals) var wstatus syscall.WaitStatus _, err = syscall.Wait4(pid, &wstatus, 0, nil) for xerrors.Is(err, syscall.EINTR) { _, err = syscall.Wait4(pid, &wstatus, 0, nil) } - return err + + // Convert wait status to exit code using standard Unix conventions: + // - Normal exit: use the exit code + // - Signal termination: use 128 + signal number + var exitCode int + switch { + case wstatus.Exited(): + exitCode = wstatus.ExitStatus() + case wstatus.Signaled(): + exitCode = 128 + int(wstatus.Signal()) + default: + exitCode = 1 + } + return exitCode, err } diff --git a/agent/unit/manager.go b/agent/unit/manager.go index 14727d43f93..88185d3f5ee 100644 --- a/agent/unit/manager.go +++ b/agent/unit/manager.go @@ -2,6 +2,7 @@ package unit import ( "errors" + "fmt" "sync" "golang.org/x/xerrors" @@ -23,6 +24,15 @@ var ( // Status represents the status of a unit. type Status string +var _ fmt.Stringer = Status("") + +func (s Status) String() string { + if s == StatusNotRegistered { + return "not registered" + } + return string(s) +} + // Status constants for dependency tracking. const ( StatusNotRegistered Status = "" @@ -137,7 +147,7 @@ func (m *Manager) IsReady(id ID) (bool, error) { defer m.mu.RUnlock() if !m.registered(id) { - return false, nil + return true, nil } return m.units[id].ready, nil diff --git a/agent/unit/manager_test.go b/agent/unit/manager_test.go index 0f1eab93ab1..1729a047a9b 100644 --- a/agent/unit/manager_test.go +++ b/agent/unit/manager_test.go @@ -684,7 +684,7 @@ func TestManager_IsReady(t *testing.T) { // Then: the unit is not ready isReady, err := manager.IsReady(unitA) require.NoError(t, err) - assert.False(t, isReady) + assert.True(t, isReady) }) } diff --git a/archive/archive.go b/archive/archive.go index db78b8c7000..54b6f31b24b 100644 --- a/archive/archive.go +++ b/archive/archive.go @@ -6,43 +6,153 @@ import ( "bytes" "errors" "io" - "log" + "math" "strings" + + "golang.org/x/xerrors" +) + +// Ref: +// https://github.com/golang/go/blob/go1.24.0/src/archive/tar/format.go +// https://github.com/golang/go/blob/go1.24.0/src/archive/tar/writer.go +const ( + tarBlockSize = 512 + tarEndBlockBytes = 2 * tarBlockSize ) +// ErrArchiveTooLarge reports that archive expansion would exceed the +// configured limit. +var ErrArchiveTooLarge = xerrors.New("archive exceeds maximum size") + +// ErrInvalidZipContent reports that a ZIP entry is malformed or its +// contents fail validation during conversion. +var ErrInvalidZipContent = xerrors.New("invalid zip content") + // CreateTarFromZip converts the given zipReader to a tar archive. +// maxSize limits the total tar output, including tar metadata. func CreateTarFromZip(zipReader *zip.Reader, maxSize int64) ([]byte, error) { + err := validateZipArchiveSize(zipReader, maxSize) + if err != nil { + return nil, err + } + var tarBuffer bytes.Buffer - err := writeTarArchive(&tarBuffer, zipReader, maxSize) + err = writeTarArchive(&tarBuffer, zipReader, maxSize) if err != nil { return nil, err } return tarBuffer.Bytes(), nil } -func writeTarArchive(w io.Writer, zipReader *zip.Reader, maxSize int64) error { - tarWriter := tar.NewWriter(w) - defer tarWriter.Close() +// validateZipArchiveSize performs a metadata-based preflight size +// check before conversion. The actual tar output limit will still be +// enforced while streaming. +func validateZipArchiveSize(zipReader *zip.Reader, maxSize int64) error { + if maxSize < 0 { + return ErrArchiveTooLarge + } + + maxBytes := uint64(maxSize) + totalBytes := uint64(tarEndBlockBytes) + if totalBytes > maxBytes { + return ErrArchiveTooLarge + } for _, file := range zipReader.File { - err := processFileInZipArchive(file, tarWriter, maxSize) + entrySize, err := projectedTarEntrySize(file) if err != nil { return err } + if entrySize > maxBytes-totalBytes { + return ErrArchiveTooLarge + } + totalBytes += entrySize } + return nil } -func processFileInZipArchive(file *zip.File, tarWriter *tar.Writer, maxSize int64) error { +func projectedTarEntrySize(file *zip.File) (uint64, error) { + // Each tar entry contributes one header block plus its data + // rounded up to the next tar block boundary. + size := file.UncompressedSize64 + if remainder := size % tarBlockSize; remainder != 0 { + padding := tarBlockSize - remainder + if size > math.MaxUint64-padding { + return 0, ErrArchiveTooLarge + } + size += padding + } + + if size > math.MaxUint64-tarBlockSize { + return 0, ErrArchiveTooLarge + } + + return tarBlockSize + size, nil +} + +type limitedWriter struct { + w io.Writer + remaining int64 +} + +func (w *limitedWriter) Write(p []byte) (int, error) { + if len(p) == 0 { + return 0, nil + } + if w.remaining <= 0 { + return 0, ErrArchiveTooLarge + } + + origLen := len(p) + if int64(origLen) > w.remaining { + p = p[:int(w.remaining)] + } + + n, err := w.w.Write(p) + // io.Writer may report both written bytes and an error, so + // account for any accepted bytes before returning the error. + w.remaining -= int64(n) + if err != nil { + return n, err + } + if n < origLen { + return n, ErrArchiveTooLarge + } + return n, nil +} + +func writeTarArchive(w io.Writer, zipReader *zip.Reader, maxSize int64) error { + tarWriter := tar.NewWriter(&limitedWriter{ + w: w, + remaining: maxSize, + }) + + for _, file := range zipReader.File { + err := processFileInZipArchive(file, tarWriter) + if err != nil { + return err + } + } + + return tarWriter.Close() +} + +func processFileInZipArchive(file *zip.File, tarWriter *tar.Writer) error { fileReader, err := file.Open() if err != nil { return err } defer fileReader.Close() + size := file.FileInfo().Size() + if size < 0 { + return ErrArchiveTooLarge + } + err = tarWriter.WriteHeader(&tar.Header{ Name: file.Name, - Size: file.FileInfo().Size(), + Size: size, Mode: int64(file.Mode()), ModTime: file.Modified, // Note: Zip archives do not store ownership information. @@ -53,12 +163,17 @@ func processFileInZipArchive(file *zip.File, tarWriter *tar.Writer, maxSize int6 return err } - n, err := io.CopyN(tarWriter, fileReader, maxSize) - log.Println(file.Name, n, err) - if errors.Is(err, io.EOF) { - err = nil + _, err = io.CopyN(tarWriter, fileReader, size) + switch { + case errors.Is(err, io.EOF), errors.Is(err, io.ErrUnexpectedEOF): + return ErrInvalidZipContent + case errors.Is(err, zip.ErrChecksum), errors.Is(err, zip.ErrFormat): + return ErrInvalidZipContent + case err != nil: + return err + default: + return nil } - return err } // CreateZipFromTar converts the given tarReader to a zip archive. diff --git a/archive/archive_test.go b/archive/archive_test.go index c10d103622f..79f3d894e32 100644 --- a/archive/archive_test.go +++ b/archive/archive_test.go @@ -4,6 +4,7 @@ import ( "archive/tar" "archive/zip" "bytes" + "encoding/binary" "io/fs" "os" "os/exec" @@ -35,14 +36,15 @@ func TestCreateTarFromZip(t *testing.T) { zr, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes))) require.NoError(t, err, "failed to parse sample zip file") - tarBytes, err := archive.CreateTarFromZip(zr, int64(len(zipBytes))) + wantTar := archivetest.TestTarFileBytes() + gotTar, err := archive.CreateTarFromZip(zr, int64(len(wantTar))) require.NoError(t, err, "failed to convert zip to tar") - archivetest.AssertSampleTarFile(t, tarBytes) + archivetest.AssertSampleTarFile(t, gotTar) tempDir := t.TempDir() tempFilePath := filepath.Join(tempDir, "test.tar") - err = os.WriteFile(tempFilePath, tarBytes, 0o600) + err = os.WriteFile(tempFilePath, gotTar, 0o600) require.NoError(t, err, "failed to write converted tar file") cmd := exec.CommandContext(ctx, "tar", "--extract", "--verbose", "--file", tempFilePath, "--directory", tempDir) @@ -50,6 +52,97 @@ func TestCreateTarFromZip(t *testing.T) { assertExtractedFiles(t, tempDir, true) } +func buildTestZip(t *testing.T, files map[string]string) []byte { + t.Helper() + + var zipBytes bytes.Buffer + zw := zip.NewWriter(&zipBytes) + for name, contents := range files { + w, err := zw.Create(name) + require.NoError(t, err) + + _, err = w.Write([]byte(contents)) + require.NoError(t, err) + } + require.NoError(t, zw.Close()) + + return zipBytes.Bytes() +} + +func TestCreateTarFromZip_RejectsOversizedAggregateExpansion(t *testing.T) { + t.Parallel() + + zipBytes := buildTestZip(t, map[string]string{ + "a.txt": strings.Repeat("a", 600), + "b.txt": strings.Repeat("b", 600), + "c.txt": strings.Repeat("c", 600), + }) + + zr, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes))) + require.NoError(t, err) + + tarBytes, err := archive.CreateTarFromZip(zr, 1024) + require.Error(t, err) + require.Nil(t, tarBytes) +} + +func TestCreateTarFromZip_RejectsInvalidZipMetadata(t *testing.T) { + t.Parallel() + + // Ref: https://github.com/golang/go/blob/go1.24.0/src/archive/zip/struct.go + corruptZipUncompressedSize := func(t *testing.T, zipBytes []byte, size uint32) []byte { + t.Helper() + + const ( + directoryHeaderSignature = "PK\x01\x02" + uncompressedSizeOffset = 24 + ) + hdrOffset := bytes.Index(zipBytes, []byte(directoryHeaderSignature)) + require.NotEqual(t, -1, hdrOffset, "missing ZIP central directory header") + corrupted := bytes.Clone(zipBytes) + sizeBytes := corrupted[hdrOffset+uncompressedSizeOffset : hdrOffset+uncompressedSizeOffset+4] + binary.LittleEndian.PutUint32(sizeBytes, size) + + return corrupted + } + + zipBytes := buildTestZip(t, map[string]string{ + "hello.txt": "hello", + }) + zipBytes = corruptZipUncompressedSize(t, zipBytes, 6) + + zr, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes))) + require.NoError(t, err) + + // Keep the size limit large so this test exercises the invalid + // ZIP metadata path rather than the tar output limit. + maxSize := int64(4096) + tarBytes, err := archive.CreateTarFromZip(zr, maxSize) + require.ErrorIs(t, err, archive.ErrInvalidZipContent) + require.Nil(t, tarBytes) +} + +func TestCreateTarFromZip_RejectsOversizedTarOverhead(t *testing.T) { + t.Parallel() + + // Empty files keep the ZIP payload tiny while still forcing tar + // headers and end-of-archive blocks to consume output budget. + zipBytes := buildTestZip(t, map[string]string{ + "empty-a.txt": "", + "empty-b.txt": "", + }) + + zr, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes))) + require.NoError(t, err) + + // Two empty tar entries still need 2 header blocks plus the 2 + // end-of-archive blocks, so the output is 2048 bytes and must + // exceed this limit. + tarBytes, err := archive.CreateTarFromZip(zr, 2047) + require.Error(t, err) + require.Nil(t, tarBytes) +} + func TestCreateZipFromTar(t *testing.T) { t.Parallel() if runtime.GOOS != "linux" { diff --git a/cli/agent.go b/cli/agent.go index 5d7738d3f4b..2e396206c9f 100644 --- a/cli/agent.go +++ b/cli/agent.go @@ -9,6 +9,7 @@ import ( "net/http/pprof" "net/url" "os" + "os/signal" "path/filepath" "runtime" "slices" @@ -57,6 +58,8 @@ func workspaceAgent() *serpent.Command { devcontainers bool devcontainerProjectDiscovery bool devcontainerDiscoveryAutostart bool + socketServerEnabled bool + socketPath string ) agentAuth := &AgentAuth{} cmd := &serpent.Command{ @@ -128,40 +131,29 @@ func workspaceAgent() *serpent.Command { sinks = append(sinks, sloghuman.Sink(logWriter)) logger := inv.Logger.AppendSinks(sinks...).Leveled(slog.LevelDebug) + logger = logger.Named("reaper") logger.Info(ctx, "spawning reaper process") // Do not start a reaper on the child process. It's important // to do this else we fork bomb ourselves. //nolint:gocritic args := append(os.Args, "--no-reap") - err := reaper.ForkReap( + exitCode, err := reaper.ForkReap( reaper.WithExecArgs(args...), reaper.WithCatchSignals(StopSignals...), + reaper.WithLogger(logger), ) if err != nil { logger.Error(ctx, "agent process reaper unable to fork", slog.Error(err)) return xerrors.Errorf("fork reap: %w", err) } - logger.Info(ctx, "reaper process exiting") - return nil + logger.Info(ctx, "child process exited, propagating exit code", + slog.F("exit_code", exitCode), + ) + return ExitError(exitCode, nil) } - // Handle interrupt signals to allow for graceful shutdown, - // note that calling stopNotify disables the signal handler - // and the next interrupt will terminate the program (you - // probably want cancel instead). - // - // Note that we don't want to handle these signals in the - // process that runs as PID 1, that's why we do this after - // the reaper forked. - ctx, stopNotify := inv.SignalNotifyContext(ctx, StopSignals...) - defer stopNotify() - - // DumpHandler does signal handling, so we call it after the - // reaper. - go DumpHandler(ctx, "agent") - logWriter := &clilog.LumberjackWriteCloseFixer{Writer: &lumberjack.Logger{ Filename: filepath.Join(logDir, "coder-agent.log"), MaxSize: 5, // MB @@ -174,6 +166,21 @@ func workspaceAgent() *serpent.Command { sinks = append(sinks, sloghuman.Sink(logWriter)) logger := inv.Logger.AppendSinks(sinks...).Leveled(slog.LevelDebug) + // Handle interrupt signals to allow for graceful shutdown, + // note that calling stopNotify disables the signal handler + // and the next interrupt will terminate the program (you + // probably want cancel instead). + // + // Note that we also handle these signals in the + // process that runs as PID 1, mainly to forward it to the agent child + // so that it can shutdown gracefully. + ctx, stopNotify := logSignalNotifyContext(ctx, logger, StopSignals...) + defer stopNotify() + + // DumpHandler does signal handling, so we call it after the + // reaper. + go DumpHandler(ctx, "agent") + version := buildinfo.Version() logger.Info(ctx, "agent is starting now", slog.F("url", agentAuth.agentURL), @@ -317,6 +324,8 @@ func workspaceAgent() *serpent.Command { agentcontainers.WithProjectDiscovery(devcontainerProjectDiscovery), agentcontainers.WithDiscoveryAutostart(devcontainerDiscoveryAutostart), }, + SocketPath: socketPath, + SocketServerEnabled: socketServerEnabled, }) if debugAddress != "" { @@ -477,6 +486,19 @@ func workspaceAgent() *serpent.Command { Description: "Allow the agent to autostart devcontainer projects it discovers based on their configuration.", Value: serpent.BoolOf(&devcontainerDiscoveryAutostart), }, + { + Flag: "socket-server-enabled", + Default: "false", + Env: "CODER_AGENT_SOCKET_SERVER_ENABLED", + Description: "Enable the agent socket server.", + Value: serpent.BoolOf(&socketServerEnabled), + }, + { + Flag: "socket-path", + Env: "CODER_AGENT_SOCKET_PATH", + Description: "Specify the path for the agent socket.", + Value: serpent.StringOf(&socketPath), + }, } agentAuth.AttachOptions(cmd, false) return cmd @@ -540,3 +562,26 @@ func urlPort(u string) (int, error) { } return -1, xerrors.Errorf("invalid port: %s", u) } + +// logSignalNotifyContext is like signal.NotifyContext but logs the received +// signal before canceling the context. +func logSignalNotifyContext(parent context.Context, logger slog.Logger, signals ...os.Signal) (context.Context, context.CancelFunc) { + ctx, cancel := context.WithCancelCause(parent) + c := make(chan os.Signal, 1) + signal.Notify(c, signals...) + + go func() { + select { + case sig := <-c: + logger.Info(ctx, "agent received signal", slog.F("signal", sig.String())) + cancel(xerrors.Errorf("signal: %s", sig.String())) + case <-ctx.Done(): + logger.Info(ctx, "ctx canceled, stopping signal handler") + } + }() + + return ctx, func() { + cancel(context.Canceled) + signal.Stop(c) + } +} diff --git a/cli/clitest/clitest.go b/cli/clitest/clitest.go index 20db3121018..3e506a26b6d 100644 --- a/cli/clitest/clitest.go +++ b/cli/clitest/clitest.go @@ -61,10 +61,10 @@ func NewWithCommand( t testing.TB, cmd *serpent.Command, args ...string, ) (*serpent.Invocation, config.Root) { configDir := config.Root(t.TempDir()) - // Keyring usage is disabled here because many existing tests expect the session token - // to be stored on disk and is not properly instrumented for parallel testing against - // the actual operating system keyring. - invArgs := append([]string{"--global-config", string(configDir), "--use-keyring=false"}, args...) + // Keyring usage is disabled here when --global-config is set because many existing + // tests expect the session token to be stored on disk and is not properly instrumented + // for parallel testing against the actual operating system keyring. + invArgs := append([]string{"--global-config", string(configDir)}, args...) return setupInvocation(t, cmd, invArgs...), configDir } diff --git a/cli/configssh.go b/cli/configssh.go index 7676e82c4a7..72320a7ba5c 100644 --- a/cli/configssh.go +++ b/cli/configssh.go @@ -584,6 +584,10 @@ func mergeSSHOptions( ) ( sshConfigOptions, error, ) { + if err := coderd.Validate(); err != nil { + return sshConfigOptions{}, xerrors.Errorf("invalid ssh config from coderd: %w", err) + } + // Write agent configuration. defaultOptions := []string{ "ConnectTimeout=0", diff --git a/cli/configssh_internal_test.go b/cli/configssh_internal_test.go index df97527d645..fa62e454e74 100644 --- a/cli/configssh_internal_test.go +++ b/cli/configssh_internal_test.go @@ -10,6 +10,8 @@ import ( "testing" "github.com/stretchr/testify/require" + + "github.com/coder/coder/v2/codersdk" ) func init() { @@ -307,6 +309,140 @@ func Test_sshConfigExecEscapeSeparatorForce(t *testing.T) { } } +func Test_mergeSSHOptions_RejectsUnsafeServerConfig(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + coderd codersdk.SSHConfigResponse + wantErr string + }{ + { + name: "HostnameSuffix", + coderd: codersdk.SSHConfigResponse{ + HostnameSuffix: "coder\nHost *", + }, + wantErr: "workspace hostname suffix", + }, + { + name: "HostnamePrefix", + coderd: codersdk.SSHConfigResponse{ + HostnamePrefix: "coder.\nHost *", + }, + wantErr: "workspace hostname prefix", + }, + { + name: "ProxyCommand", + coderd: codersdk.SSHConfigResponse{ + SSHConfigOptions: map[string]string{"ProxyCommand": "ssh -W %h:%p bastion"}, + }, + wantErr: `ssh config option "ProxyCommand" is not allowed`, + }, + { + name: "PermitLocalCommand", + coderd: codersdk.SSHConfigResponse{ + SSHConfigOptions: map[string]string{"PermitLocalCommand": "yes"}, + }, + wantErr: `ssh config option "PermitLocalCommand" is not allowed`, + }, + { + name: "KnownHostsCommand", + coderd: codersdk.SSHConfigResponse{ + SSHConfigOptions: map[string]string{"KnownHostsCommand": "echo key"}, + }, + wantErr: `ssh config option "KnownHostsCommand" is not allowed`, + }, + { + name: "PKCS11Provider", + coderd: codersdk.SSHConfigResponse{ + SSHConfigOptions: map[string]string{"PKCS11Provider": "/tmp/evil.so"}, + }, + wantErr: `ssh config option "PKCS11Provider" is not allowed`, + }, + { + name: "NewlineInValue", + coderd: codersdk.SSHConfigResponse{ + SSHConfigOptions: map[string]string{"UserKnownHostsFile": "/tmp/known_hosts\nHost *"}, + }, + wantErr: `ssh config option "UserKnownHostsFile" must not contain carriage return, newline, or NUL characters`, + }, + { + name: "SmartcardDevice", + coderd: codersdk.SSHConfigResponse{ + SSHConfigOptions: map[string]string{"SmartcardDevice": "/path/to/lib"}, + }, + wantErr: `not allowed`, + }, + { + name: "XAuthLocation", + coderd: codersdk.SSHConfigResponse{ + SSHConfigOptions: map[string]string{"XAuthLocation": "/usr/bin/xauth"}, + }, + wantErr: `not allowed`, + }, + { + name: "ProxyJump", + coderd: codersdk.SSHConfigResponse{ + SSHConfigOptions: map[string]string{"ProxyJump": "bastion.example.com"}, + }, + wantErr: `conflicts with`, + }, + { + name: "HostnameSuffixGlob", + coderd: codersdk.SSHConfigResponse{ + HostnameSuffix: "*", + }, + wantErr: `glob`, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + _, err := mergeSSHOptions(sshConfigOptions{}, tt.coderd, t.TempDir(), "/tmp/coder") + require.ErrorContains(t, err, tt.wantErr) + }) + } +} + +func Test_mergeSSHOptions_UserOptionsOverrideServerConfig(t *testing.T) { + t.Parallel() + + user := sshConfigOptions{ + userHostPrefix: "dev.", + hostnameSuffix: "local", + } + got, err := mergeSSHOptions(user, codersdk.SSHConfigResponse{ + HostnamePrefix: "coder.", + HostnameSuffix: "coder", + }, t.TempDir(), "/tmp/coder") + require.NoError(t, err) + require.Equal(t, "dev.", got.userHostPrefix) + require.Equal(t, "local", got.hostnameSuffix) +} + +func Test_mergeSSHOptions_AllowsSafeServerConfig(t *testing.T) { + t.Parallel() + + got, err := mergeSSHOptions(sshConfigOptions{}, codersdk.SSHConfigResponse{ + HostnamePrefix: "coder.", + HostnameSuffix: "coder", + SSHConfigOptions: map[string]string{ + "HostName": "example.com", + "User": "coder", + "Port": "22", + "SetEnv": "FOO=bar BAZ=qux", + "UserKnownHostsFile": "/tmp/coder_known_hosts", + }, + }, t.TempDir(), "/tmp/coder") + require.NoError(t, err) + require.Equal(t, "coder.", got.userHostPrefix) + require.Equal(t, "coder", got.hostnameSuffix) + require.Contains(t, got.sshOptions, "HostName example.com") + require.Contains(t, got.sshOptions, "SetEnv FOO=bar BAZ=qux") +} + func Test_sshConfigOptions_addOption(t *testing.T) { t.Parallel() testCases := []struct { diff --git a/cli/configssh_test.go b/cli/configssh_test.go index 7e42bfe81a7..e0831491dfd 100644 --- a/cli/configssh_test.go +++ b/cli/configssh_test.go @@ -169,6 +169,63 @@ func TestConfigSSH(t *testing.T) { <-copyDone } +func TestConfigSSH_RejectsUnsafeServerConfig(t *testing.T) { + t.Parallel() + + if runtime.GOOS == "windows" { + t.Skip("See coder/internal#117") + } + + testCases := []struct { + name string + configSSH codersdk.SSHConfigResponse + wantErr string + }{ + { + name: "HostnameSuffix", + configSSH: codersdk.SSHConfigResponse{HostnameSuffix: "coder\nHost *"}, + wantErr: "workspace hostname suffix", + }, + { + name: "HostnamePrefix", + configSSH: codersdk.SSHConfigResponse{HostnamePrefix: "coder.\nHost *"}, + wantErr: "workspace hostname prefix", + }, + { + name: "HostnameSuffixGlob", + configSSH: codersdk.SSHConfigResponse{HostnameSuffix: "*"}, + wantErr: "glob", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + const existingConfig = "Host safe\n\tHostName safe.example.com\n" + client := coderdtest.New(t, &coderdtest.Options{ + ConfigSSH: tc.configSSH, + }) + _ = coderdtest.CreateFirstUser(t, client) + + sshConfigPath := sshConfigFileName(t) + sshConfigFileCreate(t, sshConfigPath, strings.NewReader(existingConfig)) + + inv, root := clitest.New(t, + "config-ssh", + "--ssh-config-file", sshConfigPath, + "--yes", + ) + clitest.SetupConfig(t, client, root) + + err := inv.Run() + require.Error(t, err) + require.ErrorContains(t, err, tc.wantErr) + require.Equal(t, existingConfig, sshConfigFileRead(t, sshConfigPath)) + }) + } +} + func TestConfigSSH_MissingDirectory(t *testing.T) { t.Parallel() diff --git a/cli/keyring_test.go b/cli/keyring_test.go index 27b7c12d53c..7cb190845a3 100644 --- a/cli/keyring_test.go +++ b/cli/keyring_test.go @@ -54,6 +54,7 @@ func setupKeyringTestEnv(t *testing.T, clientURL string, args ...string) keyring serviceName := keyringTestServiceName(t) root.WithKeyringServiceName(serviceName) + root.UseKeyringWithGlobalConfig() inv, cfg := clitest.NewWithDefaultKeyringCommand(t, cmd, args...) @@ -169,6 +170,7 @@ func TestUseKeyring(t *testing.T) { logoutCmd, err := logoutRoot.Command(logoutRoot.AGPL()) require.NoError(t, err) logoutRoot.WithKeyringServiceName(env.serviceName) + logoutRoot.UseKeyringWithGlobalConfig() logoutInv, _ := clitest.NewWithDefaultKeyringCommand(t, logoutCmd, "logout", diff --git a/cli/open.go b/cli/open.go index 89e30e4c6de..ef16083fef4 100644 --- a/cli/open.go +++ b/cli/open.go @@ -39,6 +39,11 @@ func (r *RootCmd) open() *serpent.Command { const vscodeDesktopName = "VS Code Desktop" +// externalSessionTokenPlaceholder is the literal substring in an external +// workspace-app URL that the CLI replaces with the user's session token +// when the app belongs to a trusted (top-level) agent. +const externalSessionTokenPlaceholder = "$SESSION_TOKEN" + func (r *RootCmd) openVSCode() *serpent.Command { var ( generateToken bool @@ -381,8 +386,13 @@ func (r *RootCmd) openApp() *serpent.Command { pathAppURL := strings.TrimPrefix(region.PathAppURL, baseURL.String()) appURL := buildAppLinkURL(baseURL, ws, agt, foundApp, region.WildcardHostname, pathAppURL) - if foundApp.External { - appURL = replacePlaceholderExternalSessionTokenString(client, appURL) + externalSubAgentApp := foundApp.External && agt.ParentID.Valid + if foundApp.External && !agt.ParentID.Valid { + // Template-defined apps run on a top-level agent and are + // admin-authored, so their URLs are trusted. Substitute the + // session token placeholder so the OS open handler receives + // a usable URL. + appURL = strings.ReplaceAll(appURL, externalSessionTokenPlaceholder, client.SessionToken()) } // Check if we're inside a workspace. Generally, we know @@ -393,6 +403,18 @@ func (r *RootCmd) openApp() *serpent.Command { _, _ = fmt.Fprintf(inv.Stdout, "%s\n", appURL) return nil } + + // Sub-agent external app URLs are set at runtime. Only open + // sub-agent URLs that don't contain the placeholder to prevent + // token exfiltration. + if externalSubAgentApp && strings.Contains(appURL, externalSessionTokenPlaceholder) { + cliui.Warnf(inv.Stderr, + "This app was registered from inside the workspace rather than from the workspace template. "+ + "Inspect the URL below carefully and, if you trust the source, substitute the $SESSION_TOKEN placeholder "+ + "with your session token and manually open it:") + _, _ = fmt.Fprintf(inv.Stdout, "%s\n", appURL) + return nil + } _, _ = fmt.Fprintf(inv.Stderr, "Opening %s\n", appURL) if !testOpenError { @@ -662,15 +684,3 @@ func buildAppLinkURL(baseURL *url.URL, workspace codersdk.Workspace, agent coder } return u.String() } - -// replacePlaceholderExternalSessionTokenString replaces any $SESSION_TOKEN -// strings in the URL with the actual session token. -// This is consistent behavior with the frontend. See: site/src/modules/resources/AppLink/AppLink.tsx -func replacePlaceholderExternalSessionTokenString(client *codersdk.Client, appURL string) string { - if !strings.Contains(appURL, "$SESSION_TOKEN") { - return appURL - } - - // We will just re-use the existing session token we're already using. - return strings.ReplaceAll(appURL, "$SESSION_TOKEN", client.SessionToken()) -} diff --git a/cli/open_test.go b/cli/open_test.go index 688fc24b5e8..8fa6dd5719f 100644 --- a/cli/open_test.go +++ b/cli/open_test.go @@ -1,7 +1,9 @@ package cli_test import ( + "bytes" "context" + "database/sql" "net/url" "os" "path" @@ -21,6 +23,10 @@ import ( "github.com/coder/coder/v2/agent/agenttest" "github.com/coder/coder/v2/cli/clitest" "github.com/coder/coder/v2/coderd/coderdtest" + "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/dbauthz" + "github.com/coder/coder/v2/coderd/database/dbfake" + "github.com/coder/coder/v2/coderd/database/dbgen" "github.com/coder/coder/v2/coderd/database/dbtime" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/provisionersdk/proto" @@ -646,14 +652,16 @@ func TestOpenApp(t *testing.T) { w.RequireContains("region not found") }) - t.Run("ExternalAppSessionToken", func(t *testing.T) { + t.Run("ExternalAppOnTopLevelAgentSubstitutes", func(t *testing.T) { t.Parallel() + // Apps on the top-level (template-defined) agent are trusted, so the + // CLI substitutes $SESSION_TOKEN regardless of scheme. client, ws, _ := setupWorkspaceForAgent(t, func(agents []*proto.Agent) []*proto.Agent { agents[0].Apps = []*proto.App{ { Slug: "app1", - Url: "https://example.com/app1?token=$SESSION_TOKEN", + Url: "vscode://coder.coder-remote/open?token=$SESSION_TOKEN", External: true, }, } @@ -670,4 +678,104 @@ func TestOpenApp(t *testing.T) { w.RequireContains("test.open-error") w.RequireContains(client.SessionToken()) }) + + t.Run("ExternalAppOnSubAgentWithPlaceholderPrintsURLAndDoesNotOpen", func(t *testing.T) { + t.Parallel() + + // Sub-agent app URLs are attacker-influenceable through workspace + // configuration and runtime registration. The CLI must not + // substitute the session token, and must not hand the URL to the + // OS open handler. The URL is printed to stdout so a user who + // trusts the source can substitute and open it manually. + ownerClient, store := coderdtest.NewWithDatabase(t, nil) + ownerClient.SetLogger(testutil.Logger(t).Named("client")) + first := coderdtest.CreateFirstUser(t, ownerClient) + userClient, user := coderdtest.CreateAnotherUserMutators(t, ownerClient, first.OrganizationID, nil, func(r *codersdk.CreateUserRequestWithOrgs) { + r.Username = "subagentowner" + }) + r := dbfake.WorkspaceBuild(t, store, database.WorkspaceTable{ + Name: "subagentws", + OrganizationID: first.OrganizationID, + OwnerID: user.ID, + }).WithAgent().Do() + + ctx := testutil.Context(t, testutil.WaitShort) + agents, err := store.GetWorkspaceAgentsByWorkspaceAndBuildNumber(dbauthz.AsSystemRestricted(ctx), database.GetWorkspaceAgentsByWorkspaceAndBuildNumberParams{ + WorkspaceID: r.Workspace.ID, + BuildNumber: r.Build.BuildNumber, + }) + require.NoError(t, err) + require.NotEmpty(t, agents, "expected at least one workspace agent") + mainAgent := agents[0] + + subAgent := dbgen.WorkspaceSubAgent(t, store, mainAgent, database.WorkspaceAgent{ + Name: "devcontainer", + }) + _ = dbgen.WorkspaceApp(t, store, database.WorkspaceApp{ + AgentID: subAgent.ID, + Slug: "subapp", + External: true, + Url: sql.NullString{Valid: true, String: "vscode://coder.coder-remote/open?token=$SESSION_TOKEN"}, + }) + + inv, root := clitest.New(t, "open", "app", r.Workspace.Name+".devcontainer", "subapp", "--test.open-error") + clitest.SetupConfig(t, userClient, root) + var stdout, stderr bytes.Buffer + inv.Stdout = &stdout + inv.Stderr = &stderr + + w := clitest.StartWithWaiter(t, inv) + w.RequireSuccess() + require.NotContains(t, stderr.String(), "test.open-error") + require.NotContains(t, stdout.String(), "test.open-error") + require.Contains(t, stdout.String(), "vscode://coder.coder-remote/open?token=$SESSION_TOKEN") + require.NotContains(t, stdout.String(), userClient.SessionToken()) + require.Contains(t, stderr.String(), "substitute") + }) + + t.Run("ExternalAppOnSubAgentWithoutPlaceholderOpensAsIs", func(t *testing.T) { + t.Parallel() + + // Sub-agent app URLs that don't reference $SESSION_TOKEN carry no + // token to leak. The CLI auto-opens them like any other external + // app; only placeholder-bearing URLs are gated. + ownerClient, store := coderdtest.NewWithDatabase(t, nil) + ownerClient.SetLogger(testutil.Logger(t).Named("client")) + first := coderdtest.CreateFirstUser(t, ownerClient) + userClient, user := coderdtest.CreateAnotherUserMutators(t, ownerClient, first.OrganizationID, nil, func(r *codersdk.CreateUserRequestWithOrgs) { + r.Username = "subagentowner2" + }) + r := dbfake.WorkspaceBuild(t, store, database.WorkspaceTable{ + Name: "subagentws2", + OrganizationID: first.OrganizationID, + OwnerID: user.ID, + }).WithAgent().Do() + + ctx := testutil.Context(t, testutil.WaitShort) + agents, err := store.GetWorkspaceAgentsByWorkspaceAndBuildNumber(dbauthz.AsSystemRestricted(ctx), database.GetWorkspaceAgentsByWorkspaceAndBuildNumberParams{ + WorkspaceID: r.Workspace.ID, + BuildNumber: r.Build.BuildNumber, + }) + require.NoError(t, err) + require.NotEmpty(t, agents, "expected at least one workspace agent") + mainAgent := agents[0] + + subAgent := dbgen.WorkspaceSubAgent(t, store, mainAgent, database.WorkspaceAgent{ + Name: "devcontainer", + }) + _ = dbgen.WorkspaceApp(t, store, database.WorkspaceApp{ + AgentID: subAgent.ID, + Slug: "subapp", + External: true, + Url: sql.NullString{Valid: true, String: "https://example.com/some/path"}, + }) + + inv, root := clitest.New(t, "open", "app", r.Workspace.Name+".devcontainer", "subapp", "--test.open-error") + clitest.SetupConfig(t, userClient, root) + + w := clitest.StartWithWaiter(t, inv) + w.RequireError() + w.RequireContains("test.open-error") + w.RequireContains("https://example.com/some/path") + }) } diff --git a/cli/root.go b/cli/root.go index 2ec035d64ab..1aa45ae42d7 100644 --- a/cli/root.go +++ b/cli/root.go @@ -150,6 +150,7 @@ func (r *RootCmd) AGPLExperimental() []*serpent.Command { r.mcpCommand(), r.promptExample(), r.rptyCommand(), + r.syncCommand(), r.boundary(), } } @@ -483,9 +484,9 @@ func (r *RootCmd) Command(subcommands []*serpent.Command) (*serpent.Command, err Flag: varUseKeyring, Env: envUseKeyring, Description: "Store and retrieve session tokens using the operating system " + - "keyring. Enabled by default. If the keyring is not supported on the " + - "current platform, file-based storage is used automatically. Set to " + - "false to force file-based storage.", + "keyring. This flag is ignored and file-based storage is used when " + + "--global-config is set or keyring usage is not supported on the current " + + "platform. Set to false to force file-based storage on supported platforms.", Default: "true", Value: serpent.BoolOf(&r.useKeyring), Group: globalGroup, @@ -536,11 +537,12 @@ type RootCmd struct { disableDirect bool debugHTTP bool - disableNetworkTelemetry bool - noVersionCheck bool - noFeatureWarning bool - useKeyring bool - keyringServiceName string + disableNetworkTelemetry bool + noVersionCheck bool + noFeatureWarning bool + useKeyring bool + keyringServiceName string + useKeyringWithGlobalConfig bool } // InitClient creates and configures a new client with authentication, telemetry, @@ -721,8 +723,14 @@ func (r *RootCmd) createUnauthenticatedClient(ctx context.Context, serverURL *ur // flag. func (r *RootCmd) ensureTokenBackend() sessionstore.Backend { if r.tokenBackend == nil { + // Checking for the --global-config directory being set is a bit wonky but necessary + // to allow extensions that invoke the CLI with this flag (e.g. VS code) to continue + // working without modification. In the future we should modify these extensions to + // either access the credential in the keyring (like Coder Desktop) or some other + // approach that doesn't rely on the session token being stored on disk. + assumeExtensionInUse := r.globalConfig != config.DefaultDir() && !r.useKeyringWithGlobalConfig keyringSupported := runtime.GOOS == "windows" || runtime.GOOS == "darwin" - if r.useKeyring && keyringSupported { + if r.useKeyring && !assumeExtensionInUse && keyringSupported { serviceName := sessionstore.DefaultServiceName if r.keyringServiceName != "" { serviceName = r.keyringServiceName @@ -742,6 +750,13 @@ func (r *RootCmd) WithKeyringServiceName(serviceName string) { r.keyringServiceName = serviceName } +// UseKeyringWithGlobalConfig enables the use of the keyring storage backend +// when the --global-config directory is set. This is only intended as an override +// for tests, which require specifying the global config directory for test isolation. +func (r *RootCmd) UseKeyringWithGlobalConfig() { + r.useKeyringWithGlobalConfig = true +} + type AgentAuth struct { // Agent Client config agentToken string diff --git a/cli/root_test.go b/cli/root_test.go index b9b23041385..4e4c9c23996 100644 --- a/cli/root_test.go +++ b/cli/root_test.go @@ -72,6 +72,31 @@ func TestCommandHelp(t *testing.T) { Name: "coder provisioner jobs list --output json", Cmd: []string{"provisioner", "jobs", "list", "--output", "json"}, }, + // TODO (SasSwart): Remove these once the sync commands are promoted out of experimental. + clitest.CommandHelpCase{ + Name: "coder exp sync --help", + Cmd: []string{"exp", "sync", "--help"}, + }, + clitest.CommandHelpCase{ + Name: "coder exp sync ping --help", + Cmd: []string{"exp", "sync", "ping", "--help"}, + }, + clitest.CommandHelpCase{ + Name: "coder exp sync start --help", + Cmd: []string{"exp", "sync", "start", "--help"}, + }, + clitest.CommandHelpCase{ + Name: "coder exp sync want --help", + Cmd: []string{"exp", "sync", "want", "--help"}, + }, + clitest.CommandHelpCase{ + Name: "coder exp sync complete --help", + Cmd: []string{"exp", "sync", "complete", "--help"}, + }, + clitest.CommandHelpCase{ + Name: "coder exp sync status --help", + Cmd: []string{"exp", "sync", "status", "--help"}, + }, )) } diff --git a/cli/server.go b/cli/server.go index e8e2d24de18..93a12521401 100644 --- a/cli/server.go +++ b/cli/server.go @@ -69,6 +69,7 @@ import ( "github.com/coder/coder/v2/cli/cliutil" "github.com/coder/coder/v2/cli/config" "github.com/coder/coder/v2/coderd" + "github.com/coder/coder/v2/coderd/authlink" "github.com/coder/coder/v2/coderd/autobuild" "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/awsiamrds" @@ -113,6 +114,58 @@ import ( "github.com/coder/coder/v2/tailnet" ) +// oidcAuthLinks validates and can repair any broken OIDC auth links from changes in +// OIDC providers. This function should avoid returning a fatal error as much as possible. +// If this function fails, it should just log the error and exit. +func oidcAuthLinks(ctx context.Context, logger slog.Logger, cli *http.Client, vals *codersdk.DeploymentValues, db database.Store) error { + // nolint:gocritic // Requires system privileges + ctx = dbauthz.AsSystemRestricted(ctx) + expectedIssuer, err := authlink.ResolveIssuer(ctx, cli, vals.OIDC.IssuerURL.String()) + if err != nil { + // Always log if there is a failure here + logger.Error(ctx, "unable to resolve OIDC 'issuer'", + slog.F("error", err.Error()), + slog.F("url", vals.OIDC.IssuerURL.String()), + ) + return nil + } + + analysis, err := authlink.AnalyzeOIDCLinks(ctx, db, expectedIssuer) + if err != nil { + // Do not make this error fatal + logger.Error(ctx, "unable to analyze OIDC links, OIDC user links cannot be verified as linked to this issuer", + slog.F("error", err.Error()), + slog.F("url", vals.OIDC.IssuerURL.String()), + slog.F("issuer", expectedIssuer), + ) + return nil + } + + if !vals.OIDC.AutoRepairLinks.Value() { + return nil + } + + // Repair any broken OIDC links + if analysis.MismatchedTotal() > 0 { + count, err := authlink.ResetMismatchedOIDCLinks(ctx, db, expectedIssuer) + if err != nil { + logger.Error(ctx, "unable to reset mismatched OIDC links", + slog.F("error", err.Error()), + slog.F("url", vals.OIDC.IssuerURL.String()), + slog.F("issuer", expectedIssuer), + ) + return nil + } + + logger.Info(ctx, "oidc users OIDC links reset", + slog.F("url", vals.OIDC.IssuerURL.String()), + slog.F("issuer", expectedIssuer), + slog.F("count", count), + ) + } + return nil +} + func createOIDCConfig(ctx context.Context, logger slog.Logger, vals *codersdk.DeploymentValues) (*coderd.OIDCConfig, error) { if vals.OIDC.ClientID == "" { return nil, xerrors.Errorf("OIDC client ID must be set!") @@ -410,6 +463,19 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. logger.Debug(ctx, "tracing closed", slog.Error(traceCloseErr)) }() + configSSHOptions, err := vals.SSHConfig.ParseOptions() + if err != nil { + return xerrors.Errorf("parse ssh config options %q: %w", vals.SSHConfig.SSHConfigOptions.String(), err) + } + sshConfigResponse := codersdk.SSHConfigResponse{ + HostnamePrefix: vals.SSHConfig.DeploymentName.String(), + HostnameSuffix: vals.WorkspaceHostnameSuffix.String(), + SSHConfigOptions: configSSHOptions, + } + if err := sshConfigResponse.Validate(); err != nil { + return xerrors.Errorf("invalid ssh config: %w", err) + } + httpServers, err := ConfigureHTTPServers(logger, inv, vals) if err != nil { return xerrors.Errorf("configure http(s): %w", err) @@ -627,20 +693,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. return xerrors.Errorf("parse real ip config: %w", err) } - configSSHOptions, err := vals.SSHConfig.ParseOptions() - if err != nil { - return xerrors.Errorf("parse ssh config options %q: %w", vals.SSHConfig.SSHConfigOptions.String(), err) - } - - // The workspace hostname suffix is always interpreted as implicitly beginning with a single dot, so it is - // a config error to explicitly include the dot. This ensures that we always interpret the suffix as a - // separate DNS label, and not just an ordinary string suffix. E.g. a suffix of 'coder' will match - // 'en.coder' but not 'encoder'. - if strings.HasPrefix(vals.WorkspaceHostnameSuffix.String(), ".") { - return xerrors.Errorf("you must omit any leading . in workspace hostname suffix: %s", - vals.WorkspaceHostnameSuffix.String()) - } - options := &coderd.Options{ AccessURL: vals.AccessURL.Value(), AppHostname: appHostname, @@ -670,14 +722,10 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. HTTPClient: httpClient, TemplateScheduleStore: &atomic.Pointer[schedule.TemplateScheduleStore]{}, UserQuietHoursScheduleStore: &atomic.Pointer[schedule.UserQuietHoursScheduleStore]{}, - SSHConfig: codersdk.SSHConfigResponse{ - HostnamePrefix: vals.SSHConfig.DeploymentName.String(), - SSHConfigOptions: configSSHOptions, - HostnameSuffix: vals.WorkspaceHostnameSuffix.String(), - }, - AllowWorkspaceRenames: vals.AllowWorkspaceRenames.Value(), - Entitlements: entitlements.New(), - NotificationsEnqueuer: notifications.NewNoopEnqueuer(), // Changed further down if notifications enabled. + SSHConfig: sshConfigResponse, + AllowWorkspaceRenames: vals.AllowWorkspaceRenames.Value(), + Entitlements: entitlements.New(), + NotificationsEnqueuer: notifications.NewNoopEnqueuer(), // Changed further down if notifications enabled. } if httpServers.TLSConfig != nil { options.TLSCertificates = httpServers.TLSConfig.Certificates @@ -711,29 +759,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. } } - // As OIDC clients can be confidential or public, - // we should only check for a client id being set. - // The underlying library handles the case of no - // client secrets correctly. For more details on - // client types: https://oauth.net/2/client-types/ - if vals.OIDC.ClientID != "" { - if vals.OIDC.IgnoreEmailVerified { - logger.Warn(ctx, "coder will not check email_verified for OIDC logins") - } - - // This OIDC config is **not** being instrumented with the - // oauth2 instrument wrapper. If we implement the missing - // oidc methods, then we can instrument it. - // Missing: - // - Userinfo - // - Verify - oc, err := createOIDCConfig(ctx, options.Logger, vals) - if err != nil { - return xerrors.Errorf("create oidc config: %w", err) - } - options.OIDCConfig = oc - } - // We'll read from this channel in the select below that tracks shutdown. If it remains // nil, that case of the select will just never fire, but it's important not to have a // "bare" read on this channel. @@ -802,6 +827,35 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. return xerrors.Errorf("set deployment id: %w", err) } + // As OIDC clients can be confidential or public, + // we should only check for a client id being set. + // The underlying library handles the case of no + // client secrets correctly. For more details on + // client types: https://oauth.net/2/client-types/ + if vals.OIDC.ClientID != "" { + if vals.OIDC.IgnoreEmailVerified { + logger.Warn(ctx, "coder will not check email_verified for OIDC logins") + } + + // This OIDC config is **not** being instrumented with the + // oauth2 instrument wrapper. If we implement the missing + // oidc methods, then we can instrument it. + // Missing: + // - Userinfo + // - Verify + oc, err := createOIDCConfig(ctx, options.Logger, vals) + if err != nil { + return xerrors.Errorf("create oidc config: %w", err) + } + options.OIDCConfig = oc + + // Repair any existing broken OIDC + err = oidcAuthLinks(ctx, logger, httpClient, vals, options.Database) + if err != nil { + return xerrors.Errorf("oidc auth links: %w", err) + } + } + // Manage push notifications. experiments := coderd.ReadExperiments(options.Logger, options.DeploymentValues.Experiments.Value()) if experiments.Enabled(codersdk.ExperimentWebPush) { @@ -1293,6 +1347,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. createAdminUserCmd := r.newCreateAdminUserCommand() regenerateVapidKeypairCmd := r.newRegenerateVapidKeypairCommand() + fixOIDCLinksCmd := r.newFixOIDCLinksCommand() rawURLOpt := serpent.Option{ Flag: "raw-url", @@ -1306,7 +1361,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. serverCmd.Children = append( serverCmd.Children, - createAdminUserCmd, postgresBuiltinURLCmd, postgresBuiltinServeCmd, regenerateVapidKeypairCmd, + createAdminUserCmd, postgresBuiltinURLCmd, postgresBuiltinServeCmd, regenerateVapidKeypairCmd, fixOIDCLinksCmd, ) return serverCmd diff --git a/cli/server_fix_oidc_links.go b/cli/server_fix_oidc_links.go new file mode 100644 index 00000000000..28863455c0a --- /dev/null +++ b/cli/server_fix_oidc_links.go @@ -0,0 +1,156 @@ +//go:build !slim + +package cli + +import ( + "fmt" + "net/http" + "strings" + + "golang.org/x/xerrors" + + "cdr.dev/slog" + "cdr.dev/slog/sloggers/sloghuman" + "github.com/coder/coder/v2/cli/cliui" + "github.com/coder/coder/v2/coderd/authlink" + "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/awsiamrds" + "github.com/coder/coder/v2/codersdk" + "github.com/coder/serpent" +) + +func (r *RootCmd) newFixOIDCLinksCommand() *serpent.Command { + var ( + pgURL string + pgAuth string + issuerURL string + dryRun bool + ) + fixOIDCLinksCmd := &serpent.Command{ + Use: "fix-oidc-links", + Short: "Reset OIDC linked IDs that do not match the expected issuer, allowing users to re-authenticate.", + Handler: func(inv *serpent.Invocation) error { + var ( + ctx, cancel = inv.SignalNotifyContext(inv.Context(), StopSignals...) + logger = inv.Logger.AppendSinks(sloghuman.Sink(inv.Stderr)) + ) + if r.verbose { + logger = logger.Leveled(slog.LevelDebug) + } + defer cancel() + + issuerURL = strings.TrimSpace(issuerURL) + if issuerURL == "" { + return xerrors.Errorf("the --%s flag is required, set it to the OIDC issuer URL (e.g. https://accounts.google.com)", "issuer-url") + } + // Resolve the canonical issuer from OIDC discovery. + cliui.Infof(inv.Stdout, "Resolving OIDC issuer from %q...", issuerURL) + // TODO: The default client might not be configured with the right certs to make this request. + issuer, err := authlink.ResolveIssuer(ctx, http.DefaultClient, issuerURL) + if err != nil { + return xerrors.Errorf("resolve issuer: %w", err) + } + _, _ = fmt.Fprintf(inv.Stdout, "Resolved OIDC issuer: %q\n\n", issuer) + + // Connect to the database. + if pgURL == "" { + return xerrors.New("the --postgres-url flag is required") + } + + sqlDriver := "postgres" + if codersdk.PostgresAuth(pgAuth) == codersdk.PostgresAuthAWSIAMRDS { + sqlDriver, err = awsiamrds.Register(inv.Context(), sqlDriver) + if err != nil { + return xerrors.Errorf("register aws rds iam auth: %w", err) + } + } + + sqlDB, err := ConnectToPostgres(ctx, logger, sqlDriver, pgURL, nil) + if err != nil { + return xerrors.Errorf("connect to postgres: %w", err) + } + defer func() { + _ = sqlDB.Close() + }() + db := database.New(sqlDB) + + // Run analysis. + analysis, err := authlink.AnalyzeOIDCLinks(ctx, db, issuer) + if err != nil { + return xerrors.Errorf("analyze OIDC links: %w", err) + } + authlink.PrintAnalysis(inv.Stdout, analysis, issuer) + _, _ = fmt.Fprintln(inv.Stdout) + + if dryRun { + return nil + } + + mismatchedTotal := analysis.MismatchedTotal() + if mismatchedTotal == 0 { + _, _ = fmt.Fprintln(inv.Stdout, "Nothing to do. All OIDC links match the expected issuer.") + return nil + } + + // Molly guard. + _, _ = fmt.Fprintf(inv.Stdout, "This will reset %d linked IDs to allow affected users to re-authenticate.\n", mismatchedTotal) + if _, err := cliui.Prompt(inv, cliui.PromptOptions{ + Text: "Are you sure you want to continue?", + IsConfirm: true, + Default: cliui.ConfirmNo, + }); err != nil { + return err + } + _, _ = fmt.Fprintln(inv.Stdout) + + // Execute the reset. + count, err := authlink.ResetMismatchedOIDCLinks(ctx, db, issuer) + if err != nil { + return xerrors.Errorf("reset mismatched OIDC links: %w", err) + } + cliui.Infof(inv.Stdout, "Reset %d linked IDs.", count) + _, _ = fmt.Fprintln(inv.Stdout) + + // Print updated analysis. + analysis, err = authlink.AnalyzeOIDCLinks(ctx, db, issuer) + if err != nil { + return xerrors.Errorf("re-analyze OIDC links: %w", err) + } + authlink.PrintAnalysis(inv.Stdout, analysis, issuer) + return nil + }, + } + + fixOIDCLinksCmd.Options.Add( + cliui.SkipPromptOption(), + serpent.Option{ + Env: "CODER_PG_CONNECTION_URL", + Flag: "postgres-url", + Description: "URL of a PostgreSQL database. If empty, the built-in PostgreSQL deployment will be used (Coder must not be already running in this case).", + Value: serpent.StringOf(&pgURL), + }, + serpent.Option{ + Name: "Postgres Connection Auth", + Description: "Type of auth to use when connecting to postgres.", + Flag: "postgres-connection-auth", + Env: "CODER_PG_CONNECTION_AUTH", + Default: "password", + Value: serpent.EnumOf(&pgAuth, codersdk.PostgresAuthDrivers...), + }, + serpent.Option{ + Env: "CODER_OIDC_ISSUER_URL", + Flag: "issuer-url", + Description: "The OIDC issuer URL. The canonical issuer is resolved via OIDC discovery.", + Value: serpent.StringOf(&issuerURL), + }, + serpent.Option{ + Flag: "dry-run", + FlagShorthand: "n", + Env: "CODER_FIX_OIDC_LINKS_DRY_RUN", + Description: "Print analysis only, do not modify the database.", + Value: serpent.BoolOf(&dryRun), + }, + ) + + return fixOIDCLinksCmd +} diff --git a/cli/server_fix_oidc_links_internal_test.go b/cli/server_fix_oidc_links_internal_test.go new file mode 100644 index 00000000000..8c1d2dd4d7e --- /dev/null +++ b/cli/server_fix_oidc_links_internal_test.go @@ -0,0 +1,167 @@ +package cli + +import ( + "database/sql" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" + + "cdr.dev/slog" + "cdr.dev/slog/sloggers/slogtest" + "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/dbgen" + "github.com/coder/coder/v2/coderd/database/dbtestutil" + "github.com/coder/coder/v2/codersdk" + "github.com/coder/coder/v2/testutil" + "github.com/coder/serpent" +) + +func fakeOIDCIssuerDiscovery(t *testing.T, issuer string) *httptest.Server { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/.well-known/openid-configuration" { + http.NotFound(w, r) + return + } + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(map[string]string{ + "issuer": issuer, + }) + })) + t.Cleanup(srv.Close) + return srv +} + +func newDeploymentValues(t *testing.T, issuerURL string, autoRepair bool) *codersdk.DeploymentValues { + t.Helper() + vals := &codersdk.DeploymentValues{} + require.NoError(t, vals.OIDC.IssuerURL.Set(issuerURL)) + vals.OIDC.AutoRepairLinks = serpent.Bool(autoRepair) + return vals +} + +func TestOIDCAuthLinks(t *testing.T) { + t.Parallel() + + t.Run("RepairsMismatchedLinks", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + logger := testutil.Logger(t) + + const expectedIssuer = "https://accounts.google.com" + oidcSrv := fakeOIDCIssuerDiscovery(t, expectedIssuer) + + db, _ := dbtestutil.NewDB(t) + + // Correctly linked user. + correctUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: correctUser.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: expectedIssuer + "||sub-correct", + }) + + // Mismatched user. + mismatchedUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: mismatchedUser.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: "https://old-issuer.example.com||sub-mismatched", + }) + + vals := newDeploymentValues(t, oidcSrv.URL, true) + err := oidcAuthLinks(ctx, logger, oidcSrv.Client(), vals, db) + require.NoError(t, err) + + // Mismatched link should be reset. + link, err := db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{ + UserID: mismatchedUser.ID, + LoginType: database.LoginTypeOIDC, + }) + require.NoError(t, err) + require.Equal(t, "", link.LinkedID) + + // Correct link should be untouched. + link, err = db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{ + UserID: correctUser.ID, + LoginType: database.LoginTypeOIDC, + }) + require.NoError(t, err) + require.Equal(t, expectedIssuer+"||sub-correct", link.LinkedID) + }) + + t.Run("AutoRepairDisabledSkipsReset", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + logger := testutil.Logger(t) + + const expectedIssuer = "https://accounts.google.com" + oidcSrv := fakeOIDCIssuerDiscovery(t, expectedIssuer) + + db, _ := dbtestutil.NewDB(t) + + mismatchedUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: mismatchedUser.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: "https://old-issuer.example.com||sub-mismatched", + }) + + vals := newDeploymentValues(t, oidcSrv.URL, false) + err := oidcAuthLinks(ctx, logger, oidcSrv.Client(), vals, db) + require.NoError(t, err) + + // Link must not be modified when auto-repair is off. + link, err := db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{ + UserID: mismatchedUser.ID, + LoginType: database.LoginTypeOIDC, + }) + require.NoError(t, err) + require.Equal(t, "https://old-issuer.example.com||sub-mismatched", link.LinkedID) + }) + + t.Run("DiscoveryFailureNonFatal", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + // oidcAuthLinks logs errors but does not return them. + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) + + // Serve 500 so discovery fails. + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + t.Cleanup(srv.Close) + + db, _ := dbtestutil.NewDB(t) + + vals := newDeploymentValues(t, srv.URL, true) + err := oidcAuthLinks(ctx, logger, srv.Client(), vals, db) + require.NoError(t, err, "discovery failure must not be fatal") + }) + + t.Run("AnalyzeFailureNonFatal", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + // oidcAuthLinks logs errors but does not return them. + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) + + const expectedIssuer = "https://accounts.google.com" + oidcSrv := fakeOIDCIssuerDiscovery(t, expectedIssuer) + + // Use a closed DB so the analysis query fails. + connectionURL, err := dbtestutil.Open(t) + require.NoError(t, err) + sqlDB, err := sql.Open("postgres", connectionURL) + require.NoError(t, err) + db := database.New(sqlDB) + // Close the underlying connection so the query fails. + sqlDB.Close() + + vals := newDeploymentValues(t, oidcSrv.URL, true) + err = oidcAuthLinks(ctx, logger, oidcSrv.Client(), vals, db) + require.NoError(t, err, "analysis failure must not be fatal") + }) +} diff --git a/cli/server_fix_oidc_links_test.go b/cli/server_fix_oidc_links_test.go new file mode 100644 index 00000000000..095a54c91e4 --- /dev/null +++ b/cli/server_fix_oidc_links_test.go @@ -0,0 +1,203 @@ +package cli_test + +import ( + "context" + "database/sql" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/coder/coder/v2/cli/clitest" + "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/dbgen" + "github.com/coder/coder/v2/coderd/database/dbtestutil" + "github.com/coder/coder/v2/pty/ptytest" + "github.com/coder/coder/v2/testutil" +) + +// fakeOIDCDiscovery returns a test server serving an OIDC discovery document. +func fakeOIDCDiscovery(t *testing.T, issuer string) *httptest.Server { + t.Helper() + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/.well-known/openid-configuration" { + http.NotFound(w, r) + return + } + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(map[string]string{ + "issuer": issuer, + }) + })) + t.Cleanup(srv.Close) + return srv +} + +func TestFixOIDCLinks(t *testing.T) { + t.Parallel() + + t.Run("DryRun", func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium) + t.Cleanup(cancel) + + const expectedIssuer = "https://accounts.google.com" + oidcSrv := fakeOIDCDiscovery(t, expectedIssuer) + + connectionURL, err := dbtestutil.Open(t) + require.NoError(t, err) + + sqlDB, err := sql.Open("postgres", connectionURL) + require.NoError(t, err) + defer sqlDB.Close() + + db := database.New(sqlDB) + + // Seed a correctly linked user. + correctUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: correctUser.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: expectedIssuer + "||sub-correct", + }) + + // Seed a mismatched user. + mismatchedUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: mismatchedUser.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: "https://old-issuer.example.com||sub-mismatched", + }) + + inv, _ := clitest.New(t, + "server", "fix-oidc-links", + "--postgres-url", connectionURL, + "--issuer-url", oidcSrv.URL, + "--dry-run", + ) + + pty := ptytest.New(t).Attach(inv) + w := clitest.StartWithWaiter(t, inv) + + pty.ExpectMatchContext(ctx, "Resolved OIDC issuer: \""+expectedIssuer+"\"") + pty.ExpectMatchContext(ctx, "Total OIDC users:") + pty.ExpectMatchContext(ctx, "Correctly linked:") + pty.ExpectMatchContext(ctx, "Linked to other issuers:") + w.RequireSuccess() + + // Verify no changes were made. + link, err := db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{ + UserID: mismatchedUser.ID, + LoginType: database.LoginTypeOIDC, + }) + require.NoError(t, err) + require.Equal(t, "https://old-issuer.example.com||sub-mismatched", link.LinkedID, "dry-run must not modify the database") + }) + + t.Run("Confirm", func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium) + t.Cleanup(cancel) + + const expectedIssuer = "https://accounts.google.com" + oidcSrv := fakeOIDCDiscovery(t, expectedIssuer) + + connectionURL, err := dbtestutil.Open(t) + require.NoError(t, err) + + sqlDB, err := sql.Open("postgres", connectionURL) + require.NoError(t, err) + defer sqlDB.Close() + + db := database.New(sqlDB) + + // Seed a correctly linked user. + correctUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: correctUser.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: expectedIssuer + "||sub-correct", + }) + + // Seed mismatched users. + mismatchedUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: mismatchedUser.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: "https://old-issuer.example.com||sub-mismatched", + }) + + inv, _ := clitest.New(t, + "server", "fix-oidc-links", + "--postgres-url", connectionURL, + "--issuer-url", oidcSrv.URL, + "--yes", + ) + + pty := ptytest.New(t).Attach(inv) + w := clitest.StartWithWaiter(t, inv) + + pty.ExpectMatchContext(ctx, "Reset 1 linked IDs.") + w.RequireSuccess() + + // Verify the mismatched link was reset. + link, err := db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{ + UserID: mismatchedUser.ID, + LoginType: database.LoginTypeOIDC, + }) + require.NoError(t, err) + require.Equal(t, "", link.LinkedID) + + // Verify the correct link is unchanged. + link, err = db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{ + UserID: correctUser.ID, + LoginType: database.LoginTypeOIDC, + }) + require.NoError(t, err) + require.Equal(t, expectedIssuer+"||sub-correct", link.LinkedID) + }) + + t.Run("NothingToDo", func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium) + t.Cleanup(cancel) + + const expectedIssuer = "https://accounts.google.com" + oidcSrv := fakeOIDCDiscovery(t, expectedIssuer) + + connectionURL, err := dbtestutil.Open(t) + require.NoError(t, err) + + sqlDB, err := sql.Open("postgres", connectionURL) + require.NoError(t, err) + defer sqlDB.Close() + + db := database.New(sqlDB) + + // All users correctly linked. + user := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: user.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: expectedIssuer + "||sub-correct", + }) + + inv, _ := clitest.New(t, + "server", "fix-oidc-links", + "--postgres-url", connectionURL, + "--issuer-url", oidcSrv.URL, + "--yes", + ) + + pty := ptytest.New(t).Attach(inv) + w := clitest.StartWithWaiter(t, inv) + + pty.ExpectMatchContext(ctx, "Nothing to do") + w.RequireSuccess() + }) +} diff --git a/cli/server_test.go b/cli/server_test.go index d6278fc7669..ba7e27ceed6 100644 --- a/cli/server_test.go +++ b/cli/server_test.go @@ -1793,6 +1793,56 @@ func TestServer(t *testing.T) { }) } +// TestServer_InvalidSSHDeploymentConfig checks that unsafe SSH config flags are +// rejected at startup, before any database connection, so these invocations +// fail fast. +func TestServer_InvalidSSHDeploymentConfig(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + flag string + wantErr string + }{ + { + name: "HostnameSuffixLeadingDot", + flag: "--workspace-hostname-suffix=.coder", + wantErr: "workspace hostname suffix", + }, + { + name: "HostnameSuffixNewline", + flag: "--workspace-hostname-suffix=coder\nHost *", + wantErr: "workspace hostname suffix", + }, + { + name: "HostnamePrefixNewline", + flag: "--ssh-hostname-prefix=coder.\nHost *", + wantErr: "workspace hostname prefix", + }, + { + name: "SSHOptionUnparseable", + flag: "--ssh-config-options=NoSeparatorOption", + wantErr: "parse ssh config options", + }, + { + name: "SSHOptionDisallowedKey", + flag: "--ssh-config-options=ProxyCommand=ssh -W %h:%p bastion", + wantErr: `ssh config option "ProxyCommand" is not allowed`, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + inv, _ := clitest.New(t, "server", tc.flag) + err := inv.WithContext(ctx).Run() + require.Error(t, err) + require.ErrorContains(t, err, tc.wantErr) + }) + } +} + //nolint:tparallel,paralleltest // This test sets environment variables. func TestServer_Logging_NoParallel(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/cli/state.go b/cli/state.go index 2b8e7f8cc63..4dac6a3d171 100644 --- a/cli/state.go +++ b/cli/state.go @@ -87,6 +87,7 @@ func buildNumberOption(n *int64) serpent.Option { func (r *RootCmd) statePush() *serpent.Command { var buildNumber int64 + var noBuild bool cmd := &serpent.Command{ Use: "push ", Short: "Push a Terraform state file to a workspace.", @@ -126,6 +127,16 @@ func (r *RootCmd) statePush() *serpent.Command { return err } + if noBuild { + // Update state directly without triggering a build. + err = client.UpdateWorkspaceBuildState(inv.Context(), build.ID, state) + if err != nil { + return err + } + _, _ = fmt.Fprintln(inv.Stdout, "State updated successfully.") + return nil + } + build, err = client.CreateWorkspaceBuild(inv.Context(), workspace.ID, codersdk.CreateWorkspaceBuildRequest{ TemplateVersionID: build.TemplateVersionID, Transition: build.Transition, @@ -139,6 +150,12 @@ func (r *RootCmd) statePush() *serpent.Command { } cmd.Options = serpent.OptionSet{ buildNumberOption(&buildNumber), + { + Flag: "no-build", + FlagShorthand: "n", + Description: "Update the state without triggering a workspace build. Useful for state-only migrations.", + Value: serpent.BoolOf(&noBuild), + }, } return cmd } diff --git a/cli/state_test.go b/cli/state_test.go index 44b92b2c796..88a6b74fd54 100644 --- a/cli/state_test.go +++ b/cli/state_test.go @@ -2,6 +2,7 @@ package cli_test import ( "bytes" + "context" "fmt" "os" "path/filepath" @@ -10,6 +11,7 @@ import ( "testing" "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbfake" "github.com/stretchr/testify/require" @@ -158,4 +160,49 @@ func TestStatePush(t *testing.T) { err := inv.Run() require.NoError(t, err) }) + + t.Run("NoBuild", func(t *testing.T) { + t.Parallel() + client, store := coderdtest.NewWithDatabase(t, nil) + owner := coderdtest.CreateFirstUser(t, client) + templateAdmin, taUser := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin()) + initialState := []byte("initial state") + r := dbfake.WorkspaceBuild(t, store, database.WorkspaceTable{ + OrganizationID: owner.OrganizationID, + OwnerID: taUser.ID, + }). + Seed(database.WorkspaceBuild{ProvisionerState: initialState}). + Do() + wantState := []byte("updated state") + stateFile, err := os.CreateTemp(t.TempDir(), "") + require.NoError(t, err) + _, err = stateFile.Write(wantState) + require.NoError(t, err) + err = stateFile.Close() + require.NoError(t, err) + + inv, root := clitest.New(t, "state", "push", "--no-build", r.Workspace.Name, stateFile.Name()) + clitest.SetupConfig(t, templateAdmin, root) + var stdout bytes.Buffer + inv.Stdout = &stdout + err = inv.Run() + require.NoError(t, err) + require.Contains(t, stdout.String(), "State updated successfully") + + // Verify the state was updated by pulling it. + inv, root = clitest.New(t, "state", "pull", r.Workspace.Name) + var gotState bytes.Buffer + inv.Stdout = &gotState + clitest.SetupConfig(t, templateAdmin, root) + err = inv.Run() + require.NoError(t, err) + require.Equal(t, wantState, bytes.TrimSpace(gotState.Bytes())) + + // Verify no new build was created. + builds, err := store.GetWorkspaceBuildsByWorkspaceID(dbauthz.AsSystemRestricted(context.Background()), database.GetWorkspaceBuildsByWorkspaceIDParams{ + WorkspaceID: r.Workspace.ID, + }) + require.NoError(t, err) + require.Len(t, builds, 1, "expected only the initial build, no new build should be created") + }) } diff --git a/cli/sync.go b/cli/sync.go new file mode 100644 index 00000000000..1d3d344ba6f --- /dev/null +++ b/cli/sync.go @@ -0,0 +1,35 @@ +package cli + +import ( + "github.com/coder/serpent" +) + +func (r *RootCmd) syncCommand() *serpent.Command { + var socketPath string + + cmd := &serpent.Command{ + Use: "sync", + Short: "Manage unit dependencies for coordinated startup", + Long: "Commands for orchestrating unit startup order in workspaces. Units are most commonly coder scripts. Use these commands to declare dependencies between units, coordinate their startup sequence, and ensure units start only after their dependencies are ready. This helps prevent race conditions and startup failures.", + Handler: func(i *serpent.Invocation) error { + return i.Command.HelpHandler(i) + }, + Children: []*serpent.Command{ + r.syncPing(&socketPath), + r.syncStart(&socketPath), + r.syncWant(&socketPath), + r.syncComplete(&socketPath), + r.syncStatus(&socketPath), + }, + Options: serpent.OptionSet{ + { + Flag: "socket-path", + Env: "CODER_AGENT_SOCKET_PATH", + Description: "Specify the path for the agent socket.", + Value: serpent.StringOf(&socketPath), + }, + }, + } + + return cmd +} diff --git a/cli/sync_complete.go b/cli/sync_complete.go new file mode 100644 index 00000000000..88a8117d1aa --- /dev/null +++ b/cli/sync_complete.go @@ -0,0 +1,47 @@ +package cli + +import ( + "golang.org/x/xerrors" + + "github.com/coder/coder/v2/agent/agentsocket" + "github.com/coder/coder/v2/agent/unit" + "github.com/coder/coder/v2/cli/cliui" + "github.com/coder/serpent" +) + +func (*RootCmd) syncComplete(socketPath *string) *serpent.Command { + cmd := &serpent.Command{ + Use: "complete ", + Short: "Mark a unit as complete", + Long: "Mark a unit as complete. Indicating to other units that it has completed its work. This allows units that depend on it to proceed with their startup.", + Handler: func(i *serpent.Invocation) error { + ctx := i.Context() + + if len(i.Args) != 1 { + return xerrors.New("exactly one unit name is required") + } + unit := unit.ID(i.Args[0]) + + opts := []agentsocket.Option{} + if *socketPath != "" { + opts = append(opts, agentsocket.WithPath(*socketPath)) + } + + client, err := agentsocket.NewClient(ctx, opts...) + if err != nil { + return xerrors.Errorf("connect to agent socket: %w", err) + } + defer client.Close() + + if err := client.SyncComplete(ctx, unit); err != nil { + return xerrors.Errorf("complete unit failed: %w", err) + } + + cliui.Info(i.Stdout, "Success") + + return nil + }, + } + + return cmd +} diff --git a/cli/sync_ping.go b/cli/sync_ping.go new file mode 100644 index 00000000000..2e5e517375f --- /dev/null +++ b/cli/sync_ping.go @@ -0,0 +1,42 @@ +package cli + +import ( + "golang.org/x/xerrors" + + "github.com/coder/coder/v2/agent/agentsocket" + "github.com/coder/coder/v2/cli/cliui" + "github.com/coder/serpent" +) + +func (*RootCmd) syncPing(socketPath *string) *serpent.Command { + cmd := &serpent.Command{ + Use: "ping", + Short: "Test agent socket connectivity and health", + Long: "Test connectivity to the local Coder agent socket to verify the agent is running and responsive. Useful for troubleshooting startup issues or verifying the agent is accessible before running other sync commands.", + Handler: func(i *serpent.Invocation) error { + ctx := i.Context() + + opts := []agentsocket.Option{} + if *socketPath != "" { + opts = append(opts, agentsocket.WithPath(*socketPath)) + } + + client, err := agentsocket.NewClient(ctx, opts...) + if err != nil { + return xerrors.Errorf("connect to agent socket: %w", err) + } + defer client.Close() + + err = client.Ping(ctx) + if err != nil { + return xerrors.Errorf("ping failed: %w", err) + } + + cliui.Info(i.Stdout, "Success") + + return nil + }, + } + + return cmd +} diff --git a/cli/sync_start.go b/cli/sync_start.go new file mode 100644 index 00000000000..c114a9b4ade --- /dev/null +++ b/cli/sync_start.go @@ -0,0 +1,101 @@ +package cli + +import ( + "context" + "time" + + "golang.org/x/xerrors" + + "github.com/coder/serpent" + + "github.com/coder/coder/v2/agent/agentsocket" + "github.com/coder/coder/v2/agent/unit" + "github.com/coder/coder/v2/cli/cliui" +) + +const ( + syncPollInterval = 1 * time.Second +) + +func (*RootCmd) syncStart(socketPath *string) *serpent.Command { + var timeout time.Duration + + cmd := &serpent.Command{ + Use: "start ", + Short: "Wait until all unit dependencies are satisfied", + Long: "Wait until all dependencies are satisfied, consider the unit to have started, then allow it to proceed. This command polls until dependencies are ready, then marks the unit as started.", + Handler: func(i *serpent.Invocation) error { + ctx := i.Context() + + if len(i.Args) != 1 { + return xerrors.New("exactly one unit name is required") + } + unitName := unit.ID(i.Args[0]) + + if timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, timeout) + defer cancel() + } + + opts := []agentsocket.Option{} + if *socketPath != "" { + opts = append(opts, agentsocket.WithPath(*socketPath)) + } + + client, err := agentsocket.NewClient(ctx, opts...) + if err != nil { + return xerrors.Errorf("connect to agent socket: %w", err) + } + defer client.Close() + + ready, err := client.SyncReady(ctx, unitName) + if err != nil { + return xerrors.Errorf("error checking dependencies: %w", err) + } + + if !ready { + cliui.Infof(i.Stdout, "Waiting for dependencies of unit '%s' to be satisfied...", unitName) + + ticker := time.NewTicker(syncPollInterval) + defer ticker.Stop() + + pollLoop: + for { + select { + case <-ctx.Done(): + if ctx.Err() == context.DeadlineExceeded { + return xerrors.Errorf("timeout waiting for dependencies of unit '%s'", unitName) + } + return ctx.Err() + case <-ticker.C: + ready, err := client.SyncReady(ctx, unitName) + if err != nil { + return xerrors.Errorf("error checking dependencies: %w", err) + } + if ready { + break pollLoop + } + } + } + } + + if err := client.SyncStart(ctx, unitName); err != nil { + return xerrors.Errorf("start unit failed: %w", err) + } + + cliui.Info(i.Stdout, "Success") + + return nil + }, + } + + cmd.Options = append(cmd.Options, serpent.Option{ + Flag: "timeout", + Description: "Maximum time to wait for dependencies (e.g., 30s, 5m). 5m by default.", + Value: serpent.DurationOf(&timeout), + Default: "5m", + }) + + return cmd +} diff --git a/cli/sync_status.go b/cli/sync_status.go new file mode 100644 index 00000000000..87e3c4ccdf6 --- /dev/null +++ b/cli/sync_status.go @@ -0,0 +1,88 @@ +package cli + +import ( + "fmt" + + "golang.org/x/xerrors" + + "github.com/coder/serpent" + + "github.com/coder/coder/v2/agent/agentsocket" + "github.com/coder/coder/v2/agent/unit" + "github.com/coder/coder/v2/cli/cliui" +) + +func (*RootCmd) syncStatus(socketPath *string) *serpent.Command { + formatter := cliui.NewOutputFormatter( + cliui.ChangeFormatterData( + cliui.TableFormat( + []agentsocket.DependencyInfo{}, + []string{ + "depends on", + "required status", + "current status", + "satisfied", + }, + ), + func(data any) (any, error) { + resp, ok := data.(agentsocket.SyncStatusResponse) + if !ok { + return nil, xerrors.Errorf("expected agentsocket.SyncStatusResponse, got %T", data) + } + return resp.Dependencies, nil + }), + cliui.JSONFormat(), + ) + + cmd := &serpent.Command{ + Use: "status ", + Short: "Show unit status and dependency state", + Long: "Show the current status of a unit, whether it is ready to start, and lists its dependencies. Shows which dependencies are satisfied and which are still pending. Supports multiple output formats.", + Handler: func(i *serpent.Invocation) error { + ctx := i.Context() + + if len(i.Args) != 1 { + return xerrors.New("exactly one unit name is required") + } + unit := unit.ID(i.Args[0]) + + opts := []agentsocket.Option{} + if *socketPath != "" { + opts = append(opts, agentsocket.WithPath(*socketPath)) + } + + client, err := agentsocket.NewClient(ctx, opts...) + if err != nil { + return xerrors.Errorf("connect to agent socket: %w", err) + } + defer client.Close() + + statusResp, err := client.SyncStatus(ctx, unit) + if err != nil { + return xerrors.Errorf("get status failed: %w", err) + } + + var out string + header := fmt.Sprintf("Unit: %s\nStatus: %s\nReady: %t\n\nDependencies:\n", unit, statusResp.Status, statusResp.IsReady) + if formatter.FormatID() == "table" && len(statusResp.Dependencies) == 0 { + out = header + "No dependencies found" + } else { + out, err = formatter.Format(ctx, statusResp) + if err != nil { + return xerrors.Errorf("format status: %w", err) + } + + if formatter.FormatID() == "table" { + out = header + out + } + } + + _, _ = fmt.Fprintln(i.Stdout, out) + + return nil + }, + } + + formatter.AttachOptions(&cmd.Options) + return cmd +} diff --git a/cli/sync_test.go b/cli/sync_test.go new file mode 100644 index 00000000000..42dc38cbe69 --- /dev/null +++ b/cli/sync_test.go @@ -0,0 +1,330 @@ +//go:build !windows + +package cli_test + +import ( + "bytes" + "context" + "os" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "cdr.dev/slog" + "github.com/coder/coder/v2/agent/agentsocket" + "github.com/coder/coder/v2/cli/clitest" + "github.com/coder/coder/v2/testutil" +) + +// setupSocketServer creates an agentsocket server at a temporary path for testing. +// Returns the socket path and a cleanup function. The path should be passed to +// sync commands via the --socket-path flag. +func setupSocketServer(t *testing.T) (path string, cleanup func()) { + t.Helper() + + // Use a temporary socket path for each test + socketPath := filepath.Join(tempDirUnixSocket(t), "test.sock") + + // Create parent directory if needed + parentDir := filepath.Dir(socketPath) + err := os.MkdirAll(parentDir, 0o700) + require.NoError(t, err, "create socket directory") + + server, err := agentsocket.NewServer( + slog.Make().Leveled(slog.LevelDebug), + agentsocket.WithPath(socketPath), + ) + require.NoError(t, err, "create socket server") + + // Return cleanup function + return socketPath, func() { + err := server.Close() + require.NoError(t, err, "close socket server") + _ = os.Remove(socketPath) + } +} + +func TestSyncCommands_Golden(t *testing.T) { + t.Parallel() + + t.Run("ping", func(t *testing.T) { + t.Parallel() + path, cleanup := setupSocketServer(t) + defer cleanup() + + ctx := testutil.Context(t, testutil.WaitShort) + + var outBuf bytes.Buffer + inv, _ := clitest.New(t, "exp", "sync", "ping", "--socket-path", path) + inv.Stdout = &outBuf + inv.Stderr = &outBuf + + err := inv.WithContext(ctx).Run() + require.NoError(t, err) + + clitest.TestGoldenFile(t, "TestSyncCommands_Golden/ping_success", outBuf.Bytes(), nil) + }) + + t.Run("start_no_dependencies", func(t *testing.T) { + t.Parallel() + path, cleanup := setupSocketServer(t) + defer cleanup() + + ctx := testutil.Context(t, testutil.WaitShort) + + var outBuf bytes.Buffer + inv, _ := clitest.New(t, "exp", "sync", "start", "test-unit", "--socket-path", path) + inv.Stdout = &outBuf + inv.Stderr = &outBuf + + err := inv.WithContext(ctx).Run() + require.NoError(t, err) + + clitest.TestGoldenFile(t, "TestSyncCommands_Golden/start_no_dependencies", outBuf.Bytes(), nil) + }) + + t.Run("start_with_dependencies", func(t *testing.T) { + t.Parallel() + path, cleanup := setupSocketServer(t) + defer cleanup() + + ctx := testutil.Context(t, testutil.WaitShort) + + // Set up dependency: test-unit depends on dep-unit + client, err := agentsocket.NewClient(ctx, agentsocket.WithPath(path)) + require.NoError(t, err) + + // Declare dependency + err = client.SyncWant(ctx, "test-unit", "dep-unit") + require.NoError(t, err) + client.Close() + + // Start a goroutine to complete the dependency after a short delay + // This simulates the dependency being satisfied while start is waiting + // The delay ensures the "Waiting..." message appears in the output + done := make(chan error, 1) + go func() { + // Wait a moment to let the start command begin waiting and print the message + time.Sleep(100 * time.Millisecond) + + compCtx := context.Background() + compClient, err := agentsocket.NewClient(compCtx, agentsocket.WithPath(path)) + if err != nil { + done <- err + return + } + defer compClient.Close() + + // Start and complete the dependency unit + err = compClient.SyncStart(compCtx, "dep-unit") + if err != nil { + done <- err + return + } + err = compClient.SyncComplete(compCtx, "dep-unit") + done <- err + }() + + var outBuf bytes.Buffer + inv, _ := clitest.New(t, "exp", "sync", "start", "test-unit", "--socket-path", path) + inv.Stdout = &outBuf + inv.Stderr = &outBuf + + // Run the start command - it should wait for the dependency + err = inv.WithContext(ctx).Run() + require.NoError(t, err) + + // Ensure the completion goroutine finished + select { + case err := <-done: + require.NoError(t, err, "complete dependency") + case <-time.After(time.Second): + // Goroutine should have finished by now + } + + clitest.TestGoldenFile(t, "TestSyncCommands_Golden/start_with_dependencies", outBuf.Bytes(), nil) + }) + + t.Run("want", func(t *testing.T) { + t.Parallel() + path, cleanup := setupSocketServer(t) + defer cleanup() + + ctx := testutil.Context(t, testutil.WaitShort) + + var outBuf bytes.Buffer + inv, _ := clitest.New(t, "exp", "sync", "want", "test-unit", "dep-unit", "--socket-path", path) + inv.Stdout = &outBuf + inv.Stderr = &outBuf + + err := inv.WithContext(ctx).Run() + require.NoError(t, err) + + clitest.TestGoldenFile(t, "TestSyncCommands_Golden/want_success", outBuf.Bytes(), nil) + }) + + t.Run("complete", func(t *testing.T) { + t.Parallel() + path, cleanup := setupSocketServer(t) + defer cleanup() + + ctx := testutil.Context(t, testutil.WaitShort) + + // First start the unit + client, err := agentsocket.NewClient(ctx, agentsocket.WithPath(path)) + require.NoError(t, err) + err = client.SyncStart(ctx, "test-unit") + require.NoError(t, err) + client.Close() + + var outBuf bytes.Buffer + inv, _ := clitest.New(t, "exp", "sync", "complete", "test-unit", "--socket-path", path) + inv.Stdout = &outBuf + inv.Stderr = &outBuf + + err = inv.WithContext(ctx).Run() + require.NoError(t, err) + + clitest.TestGoldenFile(t, "TestSyncCommands_Golden/complete_success", outBuf.Bytes(), nil) + }) + + t.Run("status_pending", func(t *testing.T) { + t.Parallel() + path, cleanup := setupSocketServer(t) + defer cleanup() + + ctx := testutil.Context(t, testutil.WaitShort) + + // Set up a unit with unsatisfied dependency + client, err := agentsocket.NewClient(ctx, agentsocket.WithPath(path)) + require.NoError(t, err) + err = client.SyncWant(ctx, "test-unit", "dep-unit") + require.NoError(t, err) + client.Close() + + var outBuf bytes.Buffer + inv, _ := clitest.New(t, "exp", "sync", "status", "test-unit", "--socket-path", path) + inv.Stdout = &outBuf + inv.Stderr = &outBuf + + err = inv.WithContext(ctx).Run() + require.NoError(t, err) + + clitest.TestGoldenFile(t, "TestSyncCommands_Golden/status_pending", outBuf.Bytes(), nil) + }) + + t.Run("status_started", func(t *testing.T) { + t.Parallel() + path, cleanup := setupSocketServer(t) + defer cleanup() + + ctx := testutil.Context(t, testutil.WaitShort) + + // Start a unit + client, err := agentsocket.NewClient(ctx, agentsocket.WithPath(path)) + require.NoError(t, err) + err = client.SyncStart(ctx, "test-unit") + require.NoError(t, err) + client.Close() + + var outBuf bytes.Buffer + inv, _ := clitest.New(t, "exp", "sync", "status", "test-unit", "--socket-path", path) + inv.Stdout = &outBuf + inv.Stderr = &outBuf + + err = inv.WithContext(ctx).Run() + require.NoError(t, err) + + clitest.TestGoldenFile(t, "TestSyncCommands_Golden/status_started", outBuf.Bytes(), nil) + }) + + t.Run("status_completed", func(t *testing.T) { + t.Parallel() + path, cleanup := setupSocketServer(t) + defer cleanup() + + ctx := testutil.Context(t, testutil.WaitShort) + + // Start and complete a unit + client, err := agentsocket.NewClient(ctx, agentsocket.WithPath(path)) + require.NoError(t, err) + err = client.SyncStart(ctx, "test-unit") + require.NoError(t, err) + err = client.SyncComplete(ctx, "test-unit") + require.NoError(t, err) + client.Close() + + var outBuf bytes.Buffer + inv, _ := clitest.New(t, "exp", "sync", "status", "test-unit", "--socket-path", path) + inv.Stdout = &outBuf + inv.Stderr = &outBuf + + err = inv.WithContext(ctx).Run() + require.NoError(t, err) + + clitest.TestGoldenFile(t, "TestSyncCommands_Golden/status_completed", outBuf.Bytes(), nil) + }) + + t.Run("status_with_dependencies", func(t *testing.T) { + t.Parallel() + path, cleanup := setupSocketServer(t) + defer cleanup() + + ctx := testutil.Context(t, testutil.WaitShort) + + // Set up a unit with dependencies, some satisfied, some not + client, err := agentsocket.NewClient(ctx, agentsocket.WithPath(path)) + require.NoError(t, err) + err = client.SyncWant(ctx, "test-unit", "dep-1") + require.NoError(t, err) + err = client.SyncWant(ctx, "test-unit", "dep-2") + require.NoError(t, err) + // Complete dep-1, leave dep-2 incomplete + err = client.SyncStart(ctx, "dep-1") + require.NoError(t, err) + err = client.SyncComplete(ctx, "dep-1") + require.NoError(t, err) + client.Close() + + var outBuf bytes.Buffer + inv, _ := clitest.New(t, "exp", "sync", "status", "test-unit", "--socket-path", path) + inv.Stdout = &outBuf + inv.Stderr = &outBuf + + err = inv.WithContext(ctx).Run() + require.NoError(t, err) + + clitest.TestGoldenFile(t, "TestSyncCommands_Golden/status_with_dependencies", outBuf.Bytes(), nil) + }) + + t.Run("status_json_format", func(t *testing.T) { + t.Parallel() + path, cleanup := setupSocketServer(t) + defer cleanup() + + ctx := testutil.Context(t, testutil.WaitShort) + + // Set up a unit with dependencies + client, err := agentsocket.NewClient(ctx, agentsocket.WithPath(path)) + require.NoError(t, err) + err = client.SyncWant(ctx, "test-unit", "dep-unit") + require.NoError(t, err) + err = client.SyncStart(ctx, "dep-unit") + require.NoError(t, err) + err = client.SyncComplete(ctx, "dep-unit") + require.NoError(t, err) + client.Close() + + var outBuf bytes.Buffer + inv, _ := clitest.New(t, "exp", "sync", "status", "test-unit", "--output", "json", "--socket-path", path) + inv.Stdout = &outBuf + inv.Stderr = &outBuf + + err = inv.WithContext(ctx).Run() + require.NoError(t, err) + + clitest.TestGoldenFile(t, "TestSyncCommands_Golden/status_json_format", outBuf.Bytes(), nil) + }) +} diff --git a/cli/sync_want.go b/cli/sync_want.go new file mode 100644 index 00000000000..10df9205630 --- /dev/null +++ b/cli/sync_want.go @@ -0,0 +1,49 @@ +package cli + +import ( + "golang.org/x/xerrors" + + "github.com/coder/serpent" + + "github.com/coder/coder/v2/agent/agentsocket" + "github.com/coder/coder/v2/agent/unit" + "github.com/coder/coder/v2/cli/cliui" +) + +func (*RootCmd) syncWant(socketPath *string) *serpent.Command { + cmd := &serpent.Command{ + Use: "want ", + Short: "Declare that a unit depends on another unit completing before it can start", + Long: "Declare that a unit depends on another unit completing before it can start. The unit specified first will not start until the second has signaled that it has completed.", + Handler: func(i *serpent.Invocation) error { + ctx := i.Context() + + if len(i.Args) != 2 { + return xerrors.New("exactly two arguments are required: unit and depends-on") + } + dependentUnit := unit.ID(i.Args[0]) + dependsOn := unit.ID(i.Args[1]) + + opts := []agentsocket.Option{} + if *socketPath != "" { + opts = append(opts, agentsocket.WithPath(*socketPath)) + } + + client, err := agentsocket.NewClient(ctx, opts...) + if err != nil { + return xerrors.Errorf("connect to agent socket: %w", err) + } + defer client.Close() + + if err := client.SyncWant(ctx, dependentUnit, dependsOn); err != nil { + return xerrors.Errorf("declare dependency failed: %w", err) + } + + cliui.Info(i.Stdout, "Success") + + return nil + }, + } + + return cmd +} diff --git a/cli/testdata/TestSyncCommands_Golden/complete_success.golden b/cli/testdata/TestSyncCommands_Golden/complete_success.golden new file mode 100644 index 00000000000..35821117c87 --- /dev/null +++ b/cli/testdata/TestSyncCommands_Golden/complete_success.golden @@ -0,0 +1 @@ +Success diff --git a/cli/testdata/TestSyncCommands_Golden/ping_success.golden b/cli/testdata/TestSyncCommands_Golden/ping_success.golden new file mode 100644 index 00000000000..35821117c87 --- /dev/null +++ b/cli/testdata/TestSyncCommands_Golden/ping_success.golden @@ -0,0 +1 @@ +Success diff --git a/cli/testdata/TestSyncCommands_Golden/start_no_dependencies.golden b/cli/testdata/TestSyncCommands_Golden/start_no_dependencies.golden new file mode 100644 index 00000000000..35821117c87 --- /dev/null +++ b/cli/testdata/TestSyncCommands_Golden/start_no_dependencies.golden @@ -0,0 +1 @@ +Success diff --git a/cli/testdata/TestSyncCommands_Golden/start_with_dependencies.golden b/cli/testdata/TestSyncCommands_Golden/start_with_dependencies.golden new file mode 100644 index 00000000000..23256e9ad12 --- /dev/null +++ b/cli/testdata/TestSyncCommands_Golden/start_with_dependencies.golden @@ -0,0 +1,2 @@ +Waiting for dependencies of unit 'test-unit' to be satisfied... +Success diff --git a/cli/testdata/TestSyncCommands_Golden/status_completed.golden b/cli/testdata/TestSyncCommands_Golden/status_completed.golden new file mode 100644 index 00000000000..3fee6f914a9 --- /dev/null +++ b/cli/testdata/TestSyncCommands_Golden/status_completed.golden @@ -0,0 +1,6 @@ +Unit: test-unit +Status: completed +Ready: true + +Dependencies: +No dependencies found diff --git a/cli/testdata/TestSyncCommands_Golden/status_json_format.golden b/cli/testdata/TestSyncCommands_Golden/status_json_format.golden new file mode 100644 index 00000000000..d84b2c9d715 --- /dev/null +++ b/cli/testdata/TestSyncCommands_Golden/status_json_format.golden @@ -0,0 +1,13 @@ +{ + "unit_name": "test-unit", + "status": "pending", + "is_ready": true, + "dependencies": [ + { + "depends_on": "dep-unit", + "required_status": "completed", + "current_status": "completed", + "is_satisfied": true + } + ] +} diff --git a/cli/testdata/TestSyncCommands_Golden/status_pending.golden b/cli/testdata/TestSyncCommands_Golden/status_pending.golden new file mode 100644 index 00000000000..5c7e3272631 --- /dev/null +++ b/cli/testdata/TestSyncCommands_Golden/status_pending.golden @@ -0,0 +1,7 @@ +Unit: test-unit +Status: pending +Ready: false + +Dependencies: +DEPENDS ON REQUIRED STATUS CURRENT STATUS SATISFIED +dep-unit completed not registered false diff --git a/cli/testdata/TestSyncCommands_Golden/status_started.golden b/cli/testdata/TestSyncCommands_Golden/status_started.golden new file mode 100644 index 00000000000..0f9fc841fbb --- /dev/null +++ b/cli/testdata/TestSyncCommands_Golden/status_started.golden @@ -0,0 +1,6 @@ +Unit: test-unit +Status: started +Ready: true + +Dependencies: +No dependencies found diff --git a/cli/testdata/TestSyncCommands_Golden/status_with_dependencies.golden b/cli/testdata/TestSyncCommands_Golden/status_with_dependencies.golden new file mode 100644 index 00000000000..50d86f50518 --- /dev/null +++ b/cli/testdata/TestSyncCommands_Golden/status_with_dependencies.golden @@ -0,0 +1,8 @@ +Unit: test-unit +Status: pending +Ready: false + +Dependencies: +DEPENDS ON REQUIRED STATUS CURRENT STATUS SATISFIED +dep-1 completed completed true +dep-2 completed not registered false diff --git a/cli/testdata/TestSyncCommands_Golden/want_success.golden b/cli/testdata/TestSyncCommands_Golden/want_success.golden new file mode 100644 index 00000000000..35821117c87 --- /dev/null +++ b/cli/testdata/TestSyncCommands_Golden/want_success.golden @@ -0,0 +1 @@ +Success diff --git a/cli/testdata/coder_--help.golden b/cli/testdata/coder_--help.golden index 0057fdc15d2..ab13e2af71e 100644 --- a/cli/testdata/coder_--help.golden +++ b/cli/testdata/coder_--help.golden @@ -111,9 +111,10 @@ variables or flags. --use-keyring bool, $CODER_USE_KEYRING (default: true) Store and retrieve session tokens using the operating system keyring. - Enabled by default. If the keyring is not supported on the current - platform, file-based storage is used automatically. Set to false to - force file-based storage. + This flag is ignored and file-based storage is used when + --global-config is set or keyring usage is not supported on the + current platform. Set to false to force file-based storage on + supported platforms. -v, --verbose bool, $CODER_VERBOSE Enable verbose output. diff --git a/cli/testdata/coder_agent_--help.golden b/cli/testdata/coder_agent_--help.golden index 1f25fc6941e..d262c0d0c76 100644 --- a/cli/testdata/coder_agent_--help.golden +++ b/cli/testdata/coder_agent_--help.golden @@ -67,6 +67,12 @@ OPTIONS: --script-data-dir string, $CODER_AGENT_SCRIPT_DATA_DIR (default: /tmp) Specify the location for storing script data. + --socket-path string, $CODER_AGENT_SOCKET_PATH + Specify the path for the agent socket. + + --socket-server-enabled bool, $CODER_AGENT_SOCKET_SERVER_ENABLED (default: false) + Enable the agent socket server. + --ssh-max-timeout duration, $CODER_AGENT_SSH_MAX_TIMEOUT (default: 72h) Specify the max timeout for a SSH connection, it is advisable to set it to a minimum of 60s, but no more than 72h. diff --git a/cli/testdata/coder_exp_sync_--help.golden b/cli/testdata/coder_exp_sync_--help.golden new file mode 100644 index 00000000000..b30447351cd --- /dev/null +++ b/cli/testdata/coder_exp_sync_--help.golden @@ -0,0 +1,27 @@ +coder v0.0.0-devel + +USAGE: + coder exp sync [flags] + + Manage unit dependencies for coordinated startup + + Commands for orchestrating unit startup order in workspaces. Units are most + commonly coder scripts. Use these commands to declare dependencies between + units, coordinate their startup sequence, and ensure units start only after + their dependencies are ready. This helps prevent race conditions and startup + failures. + +SUBCOMMANDS: + complete Mark a unit as complete + ping Test agent socket connectivity and health + start Wait until all unit dependencies are satisfied + status Show unit status and dependency state + want Declare that a unit depends on another unit completing before it + can start + +OPTIONS: + --socket-path string, $CODER_AGENT_SOCKET_PATH + Specify the path for the agent socket. + +——— +Run `coder --help` for a list of global options. diff --git a/cli/testdata/coder_exp_sync_complete_--help.golden b/cli/testdata/coder_exp_sync_complete_--help.golden new file mode 100644 index 00000000000..580d5a588b6 --- /dev/null +++ b/cli/testdata/coder_exp_sync_complete_--help.golden @@ -0,0 +1,12 @@ +coder v0.0.0-devel + +USAGE: + coder exp sync complete + + Mark a unit as complete + + Mark a unit as complete. Indicating to other units that it has completed its + work. This allows units that depend on it to proceed with their startup. + +——— +Run `coder --help` for a list of global options. diff --git a/cli/testdata/coder_exp_sync_ping_--help.golden b/cli/testdata/coder_exp_sync_ping_--help.golden new file mode 100644 index 00000000000..58444940b69 --- /dev/null +++ b/cli/testdata/coder_exp_sync_ping_--help.golden @@ -0,0 +1,13 @@ +coder v0.0.0-devel + +USAGE: + coder exp sync ping + + Test agent socket connectivity and health + + Test connectivity to the local Coder agent socket to verify the agent is + running and responsive. Useful for troubleshooting startup issues or verifying + the agent is accessible before running other sync commands. + +——— +Run `coder --help` for a list of global options. diff --git a/cli/testdata/coder_exp_sync_start_--help.golden b/cli/testdata/coder_exp_sync_start_--help.golden new file mode 100644 index 00000000000..d87483130da --- /dev/null +++ b/cli/testdata/coder_exp_sync_start_--help.golden @@ -0,0 +1,17 @@ +coder v0.0.0-devel + +USAGE: + coder exp sync start [flags] + + Wait until all unit dependencies are satisfied + + Wait until all dependencies are satisfied, consider the unit to have started, + then allow it to proceed. This command polls until dependencies are ready, + then marks the unit as started. + +OPTIONS: + --timeout duration (default: 5m) + Maximum time to wait for dependencies (e.g., 30s, 5m). 5m by default. + +——— +Run `coder --help` for a list of global options. diff --git a/cli/testdata/coder_exp_sync_status_--help.golden b/cli/testdata/coder_exp_sync_status_--help.golden new file mode 100644 index 00000000000..ce7d8617be1 --- /dev/null +++ b/cli/testdata/coder_exp_sync_status_--help.golden @@ -0,0 +1,20 @@ +coder v0.0.0-devel + +USAGE: + coder exp sync status [flags] + + Show unit status and dependency state + + Show the current status of a unit, whether it is ready to start, and lists its + dependencies. Shows which dependencies are satisfied and which are still + pending. Supports multiple output formats. + +OPTIONS: + -c, --column [depends on|required status|current status|satisfied] (default: depends on,required status,current status,satisfied) + Columns to display in table output. + + -o, --output table|json (default: table) + Output format. + +——— +Run `coder --help` for a list of global options. diff --git a/cli/testdata/coder_exp_sync_want_--help.golden b/cli/testdata/coder_exp_sync_want_--help.golden new file mode 100644 index 00000000000..0076f94ea90 --- /dev/null +++ b/cli/testdata/coder_exp_sync_want_--help.golden @@ -0,0 +1,13 @@ +coder v0.0.0-devel + +USAGE: + coder exp sync want + + Declare that a unit depends on another unit completing before it can start + + Declare that a unit depends on another unit completing before it can start. + The unit specified first will not start until the second has signaled that it + has completed. + +——— +Run `coder --help` for a list of global options. diff --git a/cli/testdata/coder_server_--help.golden b/cli/testdata/coder_server_--help.golden index 1444f809614..2356e8c7b61 100644 --- a/cli/testdata/coder_server_--help.golden +++ b/cli/testdata/coder_server_--help.golden @@ -9,6 +9,9 @@ SUBCOMMANDS: create-admin-user Create a new admin user with the given username, email and password and adds it to every organization. + fix-oidc-links Reset OIDC linked IDs that do not match the + expected issuer, allowing users to + re-authenticate. postgres-builtin-serve Run the built-in PostgreSQL deployment. postgres-builtin-url Output the connection URL for the built-in PostgreSQL deployment. @@ -139,8 +142,12 @@ Clients include the Coder CLI, Coder Desktop, IDE extensions, and the web UI. --ssh-config-options string-array, $CODER_SSH_CONFIG_OPTIONS These SSH config options will override the default SSH config options. Provide options in "key=value" or "key value" format separated by - commas.Using this incorrectly can break SSH to your deployment, use - cautiously. + commas. Using this incorrectly can break SSH to your deployment, use + cautiously. The following options are not allowed: Host, Match, + Include, ProxyCommand, ProxyJump, LocalCommand, PermitLocalCommand, + RemoteCommand, KnownHostsCommand, PKCS11Provider, SecurityKeyProvider, + SmartcardDevice, XAuthLocation. Option values must not contain + newline, carriage return, or NUL characters. --ssh-hostname-prefix string, $CODER_SSH_HOSTNAME_PREFIX (default: coder.) The SSH deployment prefix is used in the Host of the ssh config. @@ -152,7 +159,8 @@ Clients include the Coder CLI, Coder Desktop, IDE extensions, and the web UI. --workspace-hostname-suffix string, $CODER_WORKSPACE_HOSTNAME_SUFFIX (default: coder) Workspace hostnames use this suffix in SSH config and Coder Connect on Coder Desktop. By default it is coder, resulting in names like - myworkspace.coder. + myworkspace.coder. The suffix must not start with a dot, and must not + contain spaces, newlines, or glob characters (* and ?). CONFIG OPTIONS: Use a YAML configuration file when your server launch become unwieldy. @@ -295,8 +303,8 @@ NETWORKING OPTIONS: True-Client-Ip, X-Forwarded-For. --proxy-trusted-origins string-array, $CODER_PROXY_TRUSTED_ORIGINS - Origin addresses to respect "proxy-trusted-headers". e.g. - 192.168.1.0/24. + Origin addresses to respect "proxy-trusted-headers" and + X-Forwarded-Host for subdomain app routing. e.g. 192.168.1.0/24. --redirect-to-access-url bool, $CODER_REDIRECT_TO_ACCESS_URL Specifies whether to redirect requests that do not match the access diff --git a/cli/testdata/coder_server_fix-oidc-links_--help.golden b/cli/testdata/coder_server_fix-oidc-links_--help.golden new file mode 100644 index 00000000000..a515e404fb7 --- /dev/null +++ b/cli/testdata/coder_server_fix-oidc-links_--help.golden @@ -0,0 +1,29 @@ +coder v0.0.0-devel + +USAGE: + coder server fix-oidc-links [flags] + + Reset OIDC linked IDs that do not match the expected issuer, allowing users to + re-authenticate. + +OPTIONS: + --postgres-connection-auth password|awsiamrds, $CODER_PG_CONNECTION_AUTH (default: password) + Type of auth to use when connecting to postgres. + + -n, --dry-run bool, $CODER_FIX_OIDC_LINKS_DRY_RUN + Print analysis only, do not modify the database. + + --issuer-url string, $CODER_OIDC_ISSUER_URL + The OIDC issuer URL. The canonical issuer is resolved via OIDC + discovery. + + --postgres-url string, $CODER_PG_CONNECTION_URL + URL of a PostgreSQL database. If empty, the built-in PostgreSQL + deployment will be used (Coder must not be already running in this + case). + + -y, --yes bool + Bypass prompts. + +——— +Run `coder --help` for a list of global options. diff --git a/cli/testdata/coder_state_push_--help.golden b/cli/testdata/coder_state_push_--help.golden index 06764846c33..df73146cfe1 100644 --- a/cli/testdata/coder_state_push_--help.golden +++ b/cli/testdata/coder_state_push_--help.golden @@ -9,5 +9,9 @@ OPTIONS: -b, --build int Specify a workspace build to target by name. Defaults to latest. + -n, --no-build bool + Update the state without triggering a workspace build. Useful for + state-only migrations. + ——— Run `coder --help` for a list of global options. diff --git a/cli/testdata/server-config.yaml.golden b/cli/testdata/server-config.yaml.golden index c8f1f94a85f..59546d2b363 100644 --- a/cli/testdata/server-config.yaml.golden +++ b/cli/testdata/server-config.yaml.golden @@ -172,7 +172,8 @@ networking: # True-Client-Ip, X-Forwarded-For. # (default: , type: string-array) proxyTrustedHeaders: [] - # Origin addresses to respect "proxy-trusted-headers". e.g. 192.168.1.0/24. + # Origin addresses to respect "proxy-trusted-headers" and X-Forwarded-Host for + # subdomain app routing. e.g. 192.168.1.0/24. # (default: , type: string-array) proxyTrustedOrigins: [] # Controls if the 'Secure' property is set on browser session cookies. @@ -408,6 +409,12 @@ oidc: # an insecure OIDC configuration. It is not recommended to use this flag. # (default: , type: bool) dangerousSkipIssuerChecks: false + # OIDC based users require the IdP issuer and subject in the claims to be static. + # If a new provider is configured, this option is required to be 'true'. It will + # reset any existing users to the previous provider, and match by email on their + # next login. + # (default: true, type: bool) + oidc-repair-links: true # Telemetry is critical to our ability to improve Coder. We strip all personal # information before sending data to our servers. Please only disable telemetry # when required by your organization's security policy. @@ -504,12 +511,18 @@ client: # (default: coder., type: string) sshHostnamePrefix: coder. # Workspace hostnames use this suffix in SSH config and Coder Connect on Coder - # Desktop. By default it is coder, resulting in names like myworkspace.coder. + # Desktop. By default it is coder, resulting in names like myworkspace.coder. The + # suffix must not start with a dot, and must not contain spaces, newlines, or glob + # characters (* and ?). # (default: coder, type: string) workspaceHostnameSuffix: coder # These SSH config options will override the default SSH config options. Provide - # options in "key=value" or "key value" format separated by commas.Using this - # incorrectly can break SSH to your deployment, use cautiously. + # options in "key=value" or "key value" format separated by commas. Using this + # incorrectly can break SSH to your deployment, use cautiously. The following + # options are not allowed: Host, Match, Include, ProxyCommand, ProxyJump, + # LocalCommand, PermitLocalCommand, RemoteCommand, KnownHostsCommand, + # PKCS11Provider, SecurityKeyProvider, SmartcardDevice, XAuthLocation. Option + # values must not contain newline, carriage return, or NUL characters. # (default: , type: string-array) sshConfigOptions: [] # The upgrade message to display to users when a client/server mismatch is diff --git a/coderd/agentapi/api.go b/coderd/agentapi/api.go index 8c8b1a00ae0..5657c558a48 100644 --- a/coderd/agentapi/api.go +++ b/coderd/agentapi/api.go @@ -24,6 +24,7 @@ import ( "github.com/coder/coder/v2/coderd/database/pubsub" "github.com/coder/coder/v2/coderd/externalauth" "github.com/coder/coder/v2/coderd/notifications" + "github.com/coder/coder/v2/coderd/portsharing" "github.com/coder/coder/v2/coderd/prometheusmetrics" "github.com/coder/coder/v2/coderd/tracing" "github.com/coder/coder/v2/coderd/workspacestats" @@ -54,7 +55,7 @@ type API struct { *ScriptsAPI *ConnLogAPI *SubAgentAPI - *tailnet.DRPCService + tailnetService *tailnet.DRPCService cachedWorkspaceFields *CachedWorkspaceFields @@ -63,13 +64,35 @@ type API struct { var _ agentproto.DRPCAgentServer = &API{} +// agentTailnetService exposes only Tailnet RPCs intended for workspace agents. +// Other current and future RPCs remain unavailable until explicitly forwarded. +type agentTailnetService struct { + tailnetproto.DRPCTailnetUnimplementedServer + + service *tailnet.DRPCService +} + +func (s *agentTailnetService) PostTelemetry(ctx context.Context, req *tailnetproto.TelemetryRequest) (*tailnetproto.TelemetryResponse, error) { + return s.service.PostTelemetry(ctx, req) +} + +func (s *agentTailnetService) StreamDERPMaps(req *tailnetproto.StreamDERPMapsRequest, stream tailnetproto.DRPCTailnet_StreamDERPMapsStream) error { + return s.service.StreamDERPMaps(req, stream) +} + +func (s *agentTailnetService) Coordinate(stream tailnetproto.DRPCTailnet_CoordinateStream) error { + return s.service.Coordinate(stream) +} + +var _ tailnetproto.DRPCTailnetServer = (*agentTailnetService)(nil) + type Options struct { AgentID uuid.UUID OwnerID uuid.UUID WorkspaceID uuid.UUID OrganizationID uuid.UUID - Ctx context.Context + AuthenticatedCtx context.Context Log slog.Logger Clock quartz.Clock Database database.Store @@ -83,6 +106,7 @@ type Options struct { PublishWorkspaceUpdateFn func(ctx context.Context, userID uuid.UUID, event wspubsub.WorkspaceEvent) PublishWorkspaceAgentLogsUpdateFn func(ctx context.Context, workspaceAgentID uuid.UUID, msg agentsdk.LogsNotifyMessage) NetworkTelemetryHandler func(batch []*tailnetproto.TelemetryEvent) + PortSharer *atomic.Pointer[portsharing.PortSharer] AccessURL *url.URL AppHostname string @@ -200,7 +224,7 @@ func New(opts Options, workspace database.Workspace) *API { Log: opts.Log, } - api.DRPCService = &tailnet.DRPCService{ + api.tailnetService = &tailnet.DRPCService{ CoordPtr: opts.TailnetCoordinator, Logger: opts.Log, DerpMapUpdateFrequency: opts.DerpMapUpdateFrequency, @@ -216,11 +240,12 @@ func New(opts Options, workspace database.Workspace) *API { Log: opts.Log, Clock: opts.Clock, Database: opts.Database, + PortSharer: opts.PortSharer, } // Start background cache refresh loop to handle workspace changes // like prebuild claims where owner_id and other fields may be modified in the DB. - go api.startCacheRefreshLoop(opts.Ctx) + go api.startCacheRefreshLoop(opts.AuthenticatedCtx) return api } @@ -232,12 +257,14 @@ func (a *API) Server(ctx context.Context) (*drpcserver.Server, error) { return nil, xerrors.Errorf("register agent API protocol in DRPC mux: %w", err) } - err = tailnetproto.DRPCRegisterTailnet(mux, a) + err = tailnetproto.DRPCRegisterTailnet(mux, &agentTailnetService{ + service: a.tailnetService, + }) if err != nil { return nil, xerrors.Errorf("register tailnet API protocol in DRPC mux: %w", err) } - return drpcserver.NewWithOptions(&tracing.DRPCHandler{Handler: mux}, + return drpcsdk.NewServer(a.opts.Log, &tracing.DRPCHandler{Handler: mux}, drpcserver.Options{ Manager: drpcsdk.DefaultDRPCOptions(nil), Log: func(err error) { diff --git a/coderd/agentapi/metadata_test.go b/coderd/agentapi/metadata_test.go index 23a6c4df4ad..1ba02d037fe 100644 --- a/coderd/agentapi/metadata_test.go +++ b/coderd/agentapi/metadata_test.go @@ -671,15 +671,15 @@ func TestBatchUpdateMetadata(t *testing.T) { // Create full API with cached workspace fields (initial state) api := agentapi.New(agentapi.Options{ - Ctx: ctxWithActor, - AgentID: agentID, - WorkspaceID: workspaceID, - OwnerID: ownerID, - OrganizationID: orgID, - Database: dbauthz.New(dbM, auth, testutil.Logger(t), accessControlStore), - Log: testutil.Logger(t), - Clock: mClock, - Pubsub: pub, + AuthenticatedCtx: ctxWithActor, + AgentID: agentID, + WorkspaceID: workspaceID, + OwnerID: ownerID, + OrganizationID: orgID, + Database: dbauthz.New(dbM, auth, testutil.Logger(t), accessControlStore), + Log: testutil.Logger(t), + Clock: mClock, + Pubsub: pub, }, initialWorkspace) // Cache is initialized with 9am schedule and "my-workspace" name // Wait for ticker to be set up and release it so it can fire diff --git a/coderd/agentapi/subagent.go b/coderd/agentapi/subagent.go index 59728177089..c5ce670dc04 100644 --- a/coderd/agentapi/subagent.go +++ b/coderd/agentapi/subagent.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "strings" + "sync/atomic" "github.com/google/uuid" "github.com/sqlc-dev/pqtype" @@ -19,6 +20,7 @@ import ( agentproto "github.com/coder/coder/v2/agent/proto" "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbauthz" + "github.com/coder/coder/v2/coderd/portsharing" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/provisioner" ) @@ -29,9 +31,10 @@ type SubAgentAPI struct { AgentID uuid.UUID AgentFn func(context.Context) (database.WorkspaceAgent, error) - Log slog.Logger - Clock quartz.Clock - Database database.Store + Log slog.Logger + Clock quartz.Clock + Database database.Store + PortSharer *atomic.Pointer[portsharing.PortSharer] } func (a *SubAgentAPI) CreateSubAgent(ctx context.Context, req *agentproto.CreateSubAgentRequest) (*agentproto.CreateSubAgentResponse, error) { @@ -84,6 +87,21 @@ func (a *SubAgentAPI) CreateSubAgent(ctx context.Context, req *agentproto.Create displayApps = append(displayApps, app) } + var template database.Template + if len(req.Apps) > 0 { + workspace, err := a.Database.GetWorkspaceByAgentID(ctx, parentAgent.ID) + if err != nil { + return nil, xerrors.Errorf("get workspace by agent id: %w", err) + } + + // Intentional: SubAgentAPI auth context enforces template ACL. + // Normal workspace operations depend on this. + template, err = a.Database.GetTemplateByID(ctx, workspace.TemplateID) + if err != nil { + return nil, xerrors.Errorf("get template policy: %w. If template access was recently changed, restart the workspace to refresh agent permissions", err) + } + } + subAgent, err := a.Database.InsertWorkspaceAgent(ctx, database.InsertWorkspaceAgentParams{ ID: uuid.New(), ParentID: uuid.NullUUID{Valid: true, UUID: parentAgent.ID}, @@ -92,7 +110,7 @@ func (a *SubAgentAPI) CreateSubAgent(ctx context.Context, req *agentproto.Create Name: agentName, ResourceID: parentAgent.ResourceID, AuthToken: uuid.New(), - AuthInstanceID: parentAgent.AuthInstanceID, + AuthInstanceID: sql.NullString{}, Architecture: req.Architecture, EnvironmentVariables: pqtype.NullRawMessage{}, OperatingSystem: req.OperatingSystem, @@ -110,6 +128,14 @@ func (a *SubAgentAPI) CreateSubAgent(ctx context.Context, req *agentproto.Create return nil, xerrors.Errorf("insert sub agent: %w", err) } + // A nil PortSharer uses the AGPL default, which permits all share levels. + portSharer := portsharing.DefaultPortSharer + if a.PortSharer != nil { + if loaded := a.PortSharer.Load(); loaded != nil { + portSharer = *loaded + } + } + var appCreationErrors []*agentproto.CreateSubAgentResponse_AppCreationError appSlugs := make(map[string]struct{}) @@ -153,6 +179,18 @@ func (a *SubAgentAPI) CreateSubAgent(ctx context.Context, req *agentproto.Create } } sharingLevel := database.AppSharingLevel(strings.ToLower(protoSharingLevel)) + // Clamp instead of rejecting so a too-permissive app share level does + // not block the sub-agent from starting. + if err := portSharer.AuthorizedLevel(template, codersdk.WorkspaceAgentPortShareLevel(sharingLevel)); err != nil { + a.Log.Warn(ctx, "clamping sub-agent app sharing level to template max port sharing level", + slog.F("sub_agent_name", subAgent.Name), + slog.F("sub_agent_id", subAgent.ID), + slog.F("app_slug", slug), + slog.F("requested_share_level", sharingLevel), + slog.F("max_port_share_level", template.MaxPortSharingLevel), + slog.Error(err)) + sharingLevel = template.MaxPortSharingLevel + } var openIn database.WorkspaceAppOpenIn switch app.GetOpenIn() { @@ -176,8 +214,9 @@ func (a *SubAgentAPI) CreateSubAgent(ctx context.Context, req *agentproto.Create slugHashEnc := base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString(slugHash[:]) computedSlug := strings.ToLower(slugHashEnc[:8]) + "-" + app.Slug + appID := uuid.New() _, err := a.Database.UpsertWorkspaceApp(ctx, database.UpsertWorkspaceAppParams{ - ID: uuid.New(), // NOTE: we may need to maintain the app's ID here for stability, but for now we'll leave this as-is. + ID: appID, // NOTE: we may need to maintain the app's ID here for stability, but for now we'll leave this as-is. CreatedAt: createdAt, AgentID: subAgent.ID, Slug: computedSlug, @@ -208,6 +247,12 @@ func (a *SubAgentAPI) CreateSubAgent(ctx context.Context, req *agentproto.Create Tooltip: "", // tooltips are not currently supported in subagent workspaces, default to empty string }) if err != nil { + if errors.Is(err, sql.ErrNoRows) { + // The upsert's ON CONFLICT guard refused to rebind an + // existing workspace-owned app to an agent outside that + // workspace, including agents that resolve to no workspace. + return xerrors.Errorf("workspace app slug %q with ID %q is already bound to a workspace-owned agent and cannot be rebound to an agent in another workspace or to an agent without a workspace; refusing to rebind to agent ID %q", computedSlug, appID, subAgent.ID) + } return xerrors.Errorf("insert workspace app: %w", err) } diff --git a/coderd/agentapi/subagent_test.go b/coderd/agentapi/subagent_test.go index 1b6eef936f8..2a14c7da07f 100644 --- a/coderd/agentapi/subagent_test.go +++ b/coderd/agentapi/subagent_test.go @@ -12,6 +12,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "cdr.dev/slog" "github.com/coder/coder/v2/agent/proto" @@ -19,6 +20,7 @@ import ( "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbgen" + "github.com/coder/coder/v2/coderd/database/dbmock" "github.com/coder/coder/v2/coderd/database/dbtestutil" "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/util/ptr" @@ -175,6 +177,52 @@ func TestSubAgentAPI(t *testing.T) { } }) + // Context: https://github.com/coder/coder/pull/22196 + t.Run("CreateSubAgentDoesNotInheritAuthInstanceID", func(t *testing.T) { + t.Parallel() + + var ( + log = testutil.Logger(t) + clock = quartz.NewMock(t) + + db, org = newDatabaseWithOrg(t) + user, agent = newUserWithWorkspaceAgent(t, db, org) + ) + + // Given: The parent agent has an AuthInstanceID set + ctx := testutil.Context(t, testutil.WaitShort) + parentAgent, err := db.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), agent.ID) + require.NoError(t, err) + require.True(t, parentAgent.AuthInstanceID.Valid, "parent agent should have an AuthInstanceID") + require.NotEmpty(t, parentAgent.AuthInstanceID.String) + + api := newAgentAPI(t, log, db, clock, user, org, agent) + + // When: We create a sub agent + createResp, err := api.CreateSubAgent(ctx, &proto.CreateSubAgentRequest{ + Name: "sub-agent", + Directory: "/workspaces/test", + Architecture: "amd64", + OperatingSystem: "linux", + }) + require.NoError(t, err) + + subAgentID, err := uuid.FromBytes(createResp.Agent.Id) + require.NoError(t, err) + + // Then: The sub-agent must NOT re-use the parent's AuthInstanceID. + subAgent, err := db.GetWorkspaceAgentByID(dbauthz.AsSystemRestricted(ctx), subAgentID) + require.NoError(t, err) + assert.False(t, subAgent.AuthInstanceID.Valid, "sub-agent should not have an AuthInstanceID") + assert.Empty(t, subAgent.AuthInstanceID.String, "sub-agent AuthInstanceID string should be empty") + + // Double-check: looking up by the parent's instance ID must + // still return the parent, not the sub-agent. + lookedUp, err := db.GetWorkspaceAgentByInstanceID(dbauthz.AsSystemRestricted(ctx), parentAgent.AuthInstanceID.String) + require.NoError(t, err) + assert.Equal(t, parentAgent.ID, lookedUp.ID, "instance ID lookup should still return the parent agent") + }) + type expectedAppError struct { index int32 field string @@ -759,6 +807,81 @@ func TestSubAgentAPI(t *testing.T) { }) }) + t.Run("CreateSubAgentWithAppRebindRejected", func(t *testing.T) { + t.Parallel() + + clock := quartz.NewMock(t) + createdAt := clock.Now() + parentAgent := database.WorkspaceAgent{ + ID: uuid.New(), + ResourceID: uuid.New(), + ConnectionTimeoutSeconds: 30, + TroubleshootingURL: "https://example.com/troubleshoot", + APIKeyScope: database.AgentKeyScopeEnumAll, + } + workspace := database.Workspace{ + ID: uuid.New(), + TemplateID: uuid.New(), + } + template := database.Template{ + ID: workspace.TemplateID, + MaxPortSharingLevel: database.AppSharingLevelPublic, + } + insertedSubAgent := database.WorkspaceAgent{ + ID: uuid.New(), + ParentID: uuid.NullUUID{UUID: parentAgent.ID, Valid: true}, + ResourceID: parentAgent.ResourceID, + Name: "child-agent", + AuthToken: uuid.New(), + } + + dbM := dbmock.NewMockStore(gomock.NewController(t)) + dbM.EXPECT().GetWorkspaceByAgentID(gomock.Any(), parentAgent.ID).Return(workspace, nil) + dbM.EXPECT().GetTemplateByID(gomock.Any(), workspace.TemplateID).Return(template, nil) + dbM.EXPECT().InsertWorkspaceAgent(gomock.Any(), gomock.Cond(func(params database.InsertWorkspaceAgentParams) bool { + return params.ParentID.Valid && params.ParentID.UUID == parentAgent.ID && + params.ResourceID == parentAgent.ResourceID && + params.Name == insertedSubAgent.Name + })).Return(insertedSubAgent, nil) + dbM.EXPECT().UpsertWorkspaceApp(gomock.Any(), gomock.Cond(func(params database.UpsertWorkspaceAppParams) bool { + return params.ID != uuid.Nil && + params.AgentID == insertedSubAgent.ID && + params.CreatedAt.Equal(createdAt) && + params.Slug == "fdqf0lpd-code-server" && + params.DisplayName == "VS Code" + })).Return(database.WorkspaceApp{}, sql.ErrNoRows) + + api := &agentapi.SubAgentAPI{ + OwnerID: uuid.New(), + OrganizationID: uuid.New(), + AgentFn: func(context.Context) (database.WorkspaceAgent, error) { return parentAgent, nil }, + Clock: clock, + Database: dbM, + Log: testutil.Logger(t), + } + + createResp, err := api.CreateSubAgent(context.Background(), &proto.CreateSubAgentRequest{ + Name: insertedSubAgent.Name, + Directory: "/workspaces/coder", + Architecture: "amd64", + OperatingSystem: "linux", + Apps: []*proto.CreateSubAgentRequest_App{ + { + Slug: "code-server", + DisplayName: ptr.Ref("VS Code"), + }, + }, + }) + require.NoError(t, err) + require.Len(t, createResp.AppCreationErrors, 1) + require.Equal(t, int32(0), createResp.AppCreationErrors[0].Index) + require.Nil(t, createResp.AppCreationErrors[0].Field) + require.Contains(t, createResp.AppCreationErrors[0].Error, "workspace app slug \"fdqf0lpd-code-server\"") + require.Contains(t, createResp.AppCreationErrors[0].Error, "already bound to a workspace-owned agent") + require.Contains(t, createResp.AppCreationErrors[0].Error, "cannot be rebound to an agent in another workspace or to an agent without a workspace") + require.NotContains(t, createResp.AppCreationErrors[0].Error, "sql: no rows in result set") + }) + t.Run("DeleteSubAgent", func(t *testing.T) { t.Parallel() diff --git a/coderd/aitasks.go b/coderd/aitasks.go index 2313ee745fa..e2c12accb47 100644 --- a/coderd/aitasks.go +++ b/coderd/aitasks.go @@ -3,7 +3,6 @@ package coderd import ( "context" "fmt" - "net" "net/http" "net/url" "slices" @@ -945,12 +944,6 @@ func (api *API) authAndDoWithTaskAppClient( } defer release() - client := &http.Client{ - Transport: &http.Transport{ - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return agentConn.DialContext(ctx, network, addr) - }, - }, - } + client := agentConn.AppHTTPClient() return do(ctx, client, parsedURL) } diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 186b54716dc..02ac4aef490 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -8387,6 +8387,84 @@ const docTemplate = `{ } } }, + "/users/{user}/preferences": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get user preference settings", + "operationId": "get-user-preference-settings", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserPreferenceSettings" + } + } + } + }, + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Update user preference settings", + "operationId": "update-user-preference-settings", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "New preference settings", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserPreferenceSettingsRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserPreferenceSettings" + } + } + } + } + }, "/users/{user}/profile": { "put": { "security": [ @@ -10104,6 +10182,45 @@ const docTemplate = `{ } } } + }, + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": [ + "application/json" + ], + "tags": [ + "Builds" + ], + "summary": "Update workspace build state", + "operationId": "update-workspace-build-state", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace build ID", + "name": "workspacebuild", + "in": "path", + "required": true + }, + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateWorkspaceBuildStateRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } } }, "/workspacebuilds/{workspacebuild}/timings": { @@ -12785,6 +12902,9 @@ const docTemplate = `{ }, "count": { "type": "integer" + }, + "count_cap": { + "type": "integer" } } }, @@ -13092,6 +13212,9 @@ const docTemplate = `{ }, "count": { "type": "integer" + }, + "count_cap": { + "type": "integer" } } }, @@ -14680,10 +14803,6 @@ const docTemplate = `{ "limit": { "type": "integer" }, - "soft_limit": { - "description": "SoftLimit is the soft limit of the feature, and is only used for showing\nincluded limits in the dashboard. No license validation or warnings are\ngenerated from this value.", - "type": "integer" - }, "usage_period": { "description": "UsagePeriod denotes that the usage is a counter that accumulates over\nthis period (and most likely resets with the issuance of the next\nlicense).\n\nThese dates are determined from the license that this entitlement comes\nfrom, see enterprise/coderd/license/license.go.\n\nOnly certain features set these fields:\n- FeatureManagedAgentLimit", "allOf": [ @@ -15983,6 +16102,9 @@ const docTemplate = `{ "auth_url_params": { "type": "object" }, + "auto_repair_links": { + "type": "boolean" + }, "client_cert_file": { "type": "string" }, @@ -19254,6 +19376,14 @@ const docTemplate = `{ } } }, + "codersdk.UpdateUserPreferenceSettingsRequest": { + "type": "object", + "properties": { + "task_notification_alert_dismissed": { + "type": "boolean" + } + } + }, "codersdk.UpdateUserProfileRequest": { "type": "object", "required": [ @@ -19316,6 +19446,17 @@ const docTemplate = `{ } } }, + "codersdk.UpdateWorkspaceBuildStateRequest": { + "type": "object", + "properties": { + "state": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "codersdk.UpdateWorkspaceDormancy": { "type": "object", "properties": { @@ -19639,6 +19780,14 @@ const docTemplate = `{ } } }, + "codersdk.UserPreferenceSettings": { + "type": "object", + "properties": { + "task_notification_alert_dismissed": { + "type": "boolean" + } + } + }, "codersdk.UserQuietHoursScheduleConfig": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 097aa188f3e..c8a8eba6ff4 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -7418,6 +7418,74 @@ } } }, + "/users/{user}/preferences": { + "get": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Get user preference settings", + "operationId": "get-user-preference-settings", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserPreferenceSettings" + } + } + } + }, + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Update user preference settings", + "operationId": "update-user-preference-settings", + "parameters": [ + { + "type": "string", + "description": "User ID, name, or me", + "name": "user", + "in": "path", + "required": true + }, + { + "description": "New preference settings", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateUserPreferenceSettingsRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.UserPreferenceSettings" + } + } + } + } + }, "/users/{user}/profile": { "put": { "security": [ @@ -8946,6 +9014,41 @@ } } } + }, + "put": { + "security": [ + { + "CoderSessionToken": [] + } + ], + "consumes": ["application/json"], + "tags": ["Builds"], + "summary": "Update workspace build state", + "operationId": "update-workspace-build-state", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Workspace build ID", + "name": "workspacebuild", + "in": "path", + "required": true + }, + { + "description": "Request body", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateWorkspaceBuildStateRequest" + } + } + ], + "responses": { + "204": { + "description": "No Content" + } + } } }, "/workspacebuilds/{workspacebuild}/timings": { @@ -11447,6 +11550,9 @@ }, "count": { "type": "integer" + }, + "count_cap": { + "type": "integer" } } }, @@ -11733,6 +11839,9 @@ }, "count": { "type": "integer" + }, + "count_cap": { + "type": "integer" } } }, @@ -13267,10 +13376,6 @@ "limit": { "type": "integer" }, - "soft_limit": { - "description": "SoftLimit is the soft limit of the feature, and is only used for showing\nincluded limits in the dashboard. No license validation or warnings are\ngenerated from this value.", - "type": "integer" - }, "usage_period": { "description": "UsagePeriod denotes that the usage is a counter that accumulates over\nthis period (and most likely resets with the issuance of the next\nlicense).\n\nThese dates are determined from the license that this entitlement comes\nfrom, see enterprise/coderd/license/license.go.\n\nOnly certain features set these fields:\n- FeatureManagedAgentLimit", "allOf": [ @@ -14517,6 +14622,9 @@ "auth_url_params": { "type": "object" }, + "auto_repair_links": { + "type": "boolean" + }, "client_cert_file": { "type": "string" }, @@ -17660,6 +17768,14 @@ } } }, + "codersdk.UpdateUserPreferenceSettingsRequest": { + "type": "object", + "properties": { + "task_notification_alert_dismissed": { + "type": "boolean" + } + } + }, "codersdk.UpdateUserProfileRequest": { "type": "object", "required": ["username"], @@ -17718,6 +17834,17 @@ } } }, + "codersdk.UpdateWorkspaceBuildStateRequest": { + "type": "object", + "properties": { + "state": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "codersdk.UpdateWorkspaceDormancy": { "type": "object", "properties": { @@ -18020,6 +18147,14 @@ } } }, + "codersdk.UserPreferenceSettings": { + "type": "object", + "properties": { + "task_notification_alert_dismissed": { + "type": "boolean" + } + } + }, "codersdk.UserQuietHoursScheduleConfig": { "type": "object", "properties": { diff --git a/coderd/apikey.go b/coderd/apikey.go index f2aec89e570..07ac1ca2bd9 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -9,7 +9,6 @@ import ( "github.com/go-chi/chi/v5" "github.com/google/uuid" - "github.com/moby/moby/pkg/namesgenerator" "golang.org/x/xerrors" "cdr.dev/slog" @@ -23,6 +22,7 @@ import ( "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/rbac/policy" "github.com/coder/coder/v2/coderd/telemetry" + "github.com/coder/coder/v2/coderd/util/namesgenerator" "github.com/coder/coder/v2/codersdk" ) @@ -102,7 +102,7 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) { } } - tokenName := namesgenerator.GetRandomName(1) + tokenName := namesgenerator.NameDigitWith("_") if len(createToken.TokenName) != 0 { tokenName = createToken.TokenName diff --git a/coderd/audit.go b/coderd/audit.go index 3a3237a9fed..6f2e66d3234 100644 --- a/coderd/audit.go +++ b/coderd/audit.go @@ -26,6 +26,11 @@ import ( "github.com/coder/coder/v2/codersdk" ) +// Limit the count query to avoid a slow sequential scan due to joins +// on a large table. Set to 0 to disable capping (but also see the note +// in the SQL query). +const auditLogCountCap = 2000 + // @Summary Get audit logs // @ID get-audit-logs // @Security CoderSessionToken @@ -66,7 +71,7 @@ func (api *API) auditLogs(rw http.ResponseWriter, r *http.Request) { countFilter.Username = "" } - // Use the same filters to count the number of audit logs + countFilter.CountCap = auditLogCountCap count, err := api.Database.CountAuditLogs(ctx, countFilter) if dbauthz.IsNotAuthorizedError(err) { httpapi.Forbidden(rw) @@ -81,6 +86,7 @@ func (api *API) auditLogs(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, codersdk.AuditLogResponse{ AuditLogs: []codersdk.AuditLog{}, Count: 0, + CountCap: auditLogCountCap, }) return } @@ -98,6 +104,7 @@ func (api *API) auditLogs(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, codersdk.AuditLogResponse{ AuditLogs: api.convertAuditLogs(ctx, dblogs), Count: count, + CountCap: auditLogCountCap, }) } diff --git a/coderd/authlink/authlink.go b/coderd/authlink/authlink.go new file mode 100644 index 00000000000..8bcd655ebef --- /dev/null +++ b/coderd/authlink/authlink.go @@ -0,0 +1,135 @@ +package authlink + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "sort" + + "golang.org/x/xerrors" + + "github.com/coder/coder/v2/coderd/database" +) + +// OIDCLinkAnalysis contains the results of analyzing OIDC user links +// grouped by their issuer prefix. +type OIDCLinkAnalysis struct { + Total int // Total OIDC user links + Unlinked int // linked_id == "" + CorrectIssuer int // linked_id starts with expectedIssuer|| + MismatchedCounts map[string]int // issuer -> count for non-matching issuers +} + +// MismatchedTotal returns the total number of links with a non-matching issuer. +func (a OIDCLinkAnalysis) MismatchedTotal() int { + total := 0 + for _, count := range a.MismatchedCounts { + total += count + } + return total +} + +// AnalyzeOIDCLinks queries OIDC user links grouped by issuer prefix and +// categorizes them relative to expectedIssuer. +func AnalyzeOIDCLinks(ctx context.Context, db database.Store, expectedIssuer string) (OIDCLinkAnalysis, error) { + rows, err := db.CountOIDCLinkedIDsByIssuer(ctx) + if err != nil { + return OIDCLinkAnalysis{}, xerrors.Errorf("count OIDC linked IDs by issuer: %w", err) + } + + analysis := OIDCLinkAnalysis{ + MismatchedCounts: make(map[string]int), + } + for _, row := range rows { + count := int(row.Count) + analysis.Total += count + switch { + case row.IssuerPrefix == "": + analysis.Unlinked += count + case row.IssuerPrefix == expectedIssuer: + analysis.CorrectIssuer += count + default: + analysis.MismatchedCounts[row.IssuerPrefix] += count + } + } + return analysis, nil +} + +// ResetMismatchedOIDCLinks resets linked_id to empty for all OIDC links whose +// issuer prefix does not match expectedIssuer. Returns the number of rows +// affected. +func ResetMismatchedOIDCLinks(ctx context.Context, db database.Store, expectedIssuer string) (int64, error) { + prefix := expectedIssuer + "||" + count, err := db.UnlinkOIDCUsersByIssuerMismatch(ctx, prefix) + if err != nil { + return 0, xerrors.Errorf("unlink OIDC users by issuer mismatch: %w", err) + } + return count, nil +} + +// ResolveIssuer uses OIDC discovery to fetch the canonical issuer string +// from the provider's .well-known/openid-configuration endpoint. +// This does not require OIDC client credentials. +// +// This works the same as `oidc.NewProvider`. The `oidc` package does not +// expose a method to extract the Issuer. So we have to manually make the +// http request. +func ResolveIssuer(ctx context.Context, cli *http.Client, issuerURL string) (string, error) { + wellKnownURL, err := url.JoinPath(issuerURL, "/.well-known/openid-configuration") + if err != nil { + return "", xerrors.Errorf("resolve issuer URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, wellKnownURL, nil) + if err != nil { + return "", xerrors.Errorf("create discovery request: %w", err) + } + + resp, err := cli.Do(req) + if err != nil { + return "", xerrors.Errorf("fetch OIDC discovery document: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", xerrors.Errorf("OIDC discovery returned HTTP %d", resp.StatusCode) + } + + var discovery struct { + Issuer string `json:"issuer"` + } + if err := json.NewDecoder(resp.Body).Decode(&discovery); err != nil { + return "", xerrors.Errorf("decode OIDC discovery document: %w", err) + } + if discovery.Issuer == "" { + return "", xerrors.New("OIDC discovery document has empty issuer field") + } + return discovery.Issuer, nil +} + +// PrintAnalysis writes a human-readable summary of the OIDC link analysis. +// Used for the cli command and debugging. +func PrintAnalysis(w io.Writer, analysis OIDCLinkAnalysis, issuer string) { + _, _ = fmt.Fprintf(w, "OIDC Link Analysis (issuer: %s)\n", issuer) + _, _ = fmt.Fprintf(w, " Total OIDC users: %d\n", analysis.Total) + _, _ = fmt.Fprintf(w, " Correctly linked: %d\n", analysis.CorrectIssuer) + _, _ = fmt.Fprintf(w, " Unlinked (empty linked_id): %d\n", analysis.Unlinked) + + mismatchedTotal := analysis.MismatchedTotal() + _, _ = fmt.Fprintf(w, " Linked to other issuers: %d\n", mismatchedTotal) + + if mismatchedTotal > 0 { + // Sort issuer keys for deterministic output. + issuers := make([]string, 0, len(analysis.MismatchedCounts)) + for issuer := range analysis.MismatchedCounts { + issuers = append(issuers, issuer) + } + sort.Strings(issuers) + for _, iss := range issuers { + _, _ = fmt.Fprintf(w, " %s: %d\n", iss, analysis.MismatchedCounts[iss]) + } + } +} diff --git a/coderd/authlink/authlink_test.go b/coderd/authlink/authlink_test.go new file mode 100644 index 00000000000..cf7a0725cc4 --- /dev/null +++ b/coderd/authlink/authlink_test.go @@ -0,0 +1,305 @@ +package authlink_test + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/coder/coder/v2/coderd/authlink" + "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/dbgen" + "github.com/coder/coder/v2/coderd/database/dbtestutil" + "github.com/coder/coder/v2/testutil" +) + +func TestAnalyzeOIDCLinks(t *testing.T) { + t.Parallel() + + t.Run("MixedIssuers", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + db, _ := dbtestutil.NewDB(t) + const expectedIssuer = "https://accounts.google.com" + + // 3 users linked to the expected issuer. + for i := 0; i < 3; i++ { + user := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: user.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: expectedIssuer + "||sub-" + user.ID.String(), + }) + } + + // 2 users linked to an old issuer. + for i := 0; i < 2; i++ { + user := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: user.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: "https://old-issuer.example.com||sub-" + user.ID.String(), + }) + } + + // 1 user linked to another old issuer. + user := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: user.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: "https://staging.example.com||sub-" + user.ID.String(), + }) + + // 1 unlinked user (empty linked_id). + unlinkedUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: unlinkedUser.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: "", + }) + + analysis, err := authlink.AnalyzeOIDCLinks(ctx, db, expectedIssuer) + require.NoError(t, err) + + require.Equal(t, 7, analysis.Total) + require.Equal(t, 3, analysis.CorrectIssuer) + require.Equal(t, 1, analysis.Unlinked) + require.Equal(t, 3, analysis.MismatchedTotal()) + require.Equal(t, 2, analysis.MismatchedCounts["https://old-issuer.example.com"]) + require.Equal(t, 1, analysis.MismatchedCounts["https://staging.example.com"]) + }) + + t.Run("NoOIDCUsers", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + db, _ := dbtestutil.NewDB(t) + + analysis, err := authlink.AnalyzeOIDCLinks(ctx, db, "https://issuer.example.com") + require.NoError(t, err) + require.Equal(t, 0, analysis.Total) + require.Equal(t, 0, analysis.CorrectIssuer) + require.Equal(t, 0, analysis.Unlinked) + require.Equal(t, 0, analysis.MismatchedTotal()) + }) + + t.Run("AllCorrect", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + db, _ := dbtestutil.NewDB(t) + const expectedIssuer = "https://accounts.google.com" + + for i := 0; i < 3; i++ { + user := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: user.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: expectedIssuer + "||sub-" + user.ID.String(), + }) + } + + analysis, err := authlink.AnalyzeOIDCLinks(ctx, db, expectedIssuer) + require.NoError(t, err) + require.Equal(t, 3, analysis.Total) + require.Equal(t, 3, analysis.CorrectIssuer) + require.Equal(t, 0, analysis.Unlinked) + require.Equal(t, 0, analysis.MismatchedTotal()) + }) + + t.Run("DeletedUsersExcluded", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + db, _ := dbtestutil.NewDB(t) + const expectedIssuer = "https://accounts.google.com" + + // Active user with mismatched issuer. + activeUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: activeUser.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: "https://old-issuer.example.com||sub-active", + }) + + // Create user and link first, then soft-delete the user. + // The DB trigger prevents inserting links for already-deleted users. + deletedUser := dbgen.User(t, db, database.User{ + LoginType: database.LoginTypeOIDC, + }) + dbgen.UserLink(t, db, database.UserLink{ + UserID: deletedUser.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: "https://old-issuer.example.com||sub-deleted", + }) + require.NoError(t, db.UpdateUserDeletedByID(ctx, deletedUser.ID)) + + analysis, err := authlink.AnalyzeOIDCLinks(ctx, db, expectedIssuer) + require.NoError(t, err) + require.Equal(t, 1, analysis.Total) + require.Equal(t, 1, analysis.MismatchedTotal()) + }) + + t.Run("NonOIDCLinksExcluded", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + db, _ := dbtestutil.NewDB(t) + const expectedIssuer = "https://accounts.google.com" + + // GitHub user link should not be counted. + ghUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeGithub}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: ghUser.ID, + LoginType: database.LoginTypeGithub, + LinkedID: "github||12345", + }) + + analysis, err := authlink.AnalyzeOIDCLinks(ctx, db, expectedIssuer) + require.NoError(t, err) + require.Equal(t, 0, analysis.Total) + }) +} + +func TestResetMismatchedOIDCLinks(t *testing.T) { + t.Parallel() + + t.Run("ResetsOnlyMismatched", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + db, _ := dbtestutil.NewDB(t) + const expectedIssuer = "https://accounts.google.com" + + // Correctly linked user. + correctUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + correctLink := dbgen.UserLink(t, db, database.UserLink{ + UserID: correctUser.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: expectedIssuer + "||sub-correct", + }) + + // Mismatched user. + mismatchedUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: mismatchedUser.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: "https://old-issuer.example.com||sub-mismatched", + }) + + // Unlinked user (empty linked_id). + unlinkedUser := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: unlinkedUser.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: "", + }) + + count, err := authlink.ResetMismatchedOIDCLinks(ctx, db, expectedIssuer) + require.NoError(t, err) + require.EqualValues(t, 1, count) + + // Verify the correct link is unchanged. + link, err := db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{ + UserID: correctUser.ID, + LoginType: database.LoginTypeOIDC, + }) + require.NoError(t, err) + require.Equal(t, correctLink.LinkedID, link.LinkedID) + + // Verify the mismatched link was reset. + link, err = db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{ + UserID: mismatchedUser.ID, + LoginType: database.LoginTypeOIDC, + }) + require.NoError(t, err) + require.Equal(t, "", link.LinkedID) + + // Verify the unlinked user is still unlinked. + link, err = db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{ + UserID: unlinkedUser.ID, + LoginType: database.LoginTypeOIDC, + }) + require.NoError(t, err) + require.Equal(t, "", link.LinkedID) + }) + + t.Run("NothingToReset", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + db, _ := dbtestutil.NewDB(t) + const expectedIssuer = "https://accounts.google.com" + + user := dbgen.User(t, db, database.User{LoginType: database.LoginTypeOIDC}) + dbgen.UserLink(t, db, database.UserLink{ + UserID: user.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: expectedIssuer + "||sub-correct", + }) + + count, err := authlink.ResetMismatchedOIDCLinks(ctx, db, expectedIssuer) + require.NoError(t, err) + require.EqualValues(t, 0, count) + }) +} + +func TestResolveIssuer(t *testing.T) { + t.Parallel() + + t.Run("Success", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + const expectedIssuer = "https://accounts.google.com" + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/.well-known/openid-configuration" { + http.NotFound(w, r) + return + } + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(map[string]string{ + "issuer": expectedIssuer, + }) + })) + defer srv.Close() + + issuer, err := authlink.ResolveIssuer(ctx, srv.Client(), srv.URL) + require.NoError(t, err) + require.Equal(t, expectedIssuer, issuer) + }) + + t.Run("EmptyIssuer", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(map[string]string{ + "issuer": "", + }) + })) + defer srv.Close() + + _, err := authlink.ResolveIssuer(ctx, srv.Client(), srv.URL) + require.Error(t, err) + require.Contains(t, err.Error(), "empty issuer") + }) + + t.Run("HTTPError", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + defer srv.Close() + + _, err := authlink.ResolveIssuer(ctx, srv.Client(), srv.URL) + require.Error(t, err) + require.Contains(t, err.Error(), "HTTP 500") + }) +} diff --git a/coderd/authlink/doc.go b/coderd/authlink/doc.go new file mode 100644 index 00000000000..50396907c98 --- /dev/null +++ b/coderd/authlink/doc.go @@ -0,0 +1,10 @@ +// Package authlink provides analysis and repair utilities for OIDC user link +// records stored in the user_links table. +// +// When an OIDC provider is changed, the issuer (and possibly subject) in the +// linked_id column changes. Because linked_id is composed as "issuer||subject", +// existing users get locked out with "Account already linked" errors. The +// functions in this package let an administrator inspect which links are +// affected and reset the mismatched ones so users can re-authenticate under the +// new provider. +package authlink diff --git a/coderd/azureidentity/azureidentity.go b/coderd/azureidentity/azureidentity.go index c1c766bcc98..a3535cd218b 100644 --- a/coderd/azureidentity/azureidentity.go +++ b/coderd/azureidentity/azureidentity.go @@ -6,14 +6,15 @@ import ( "encoding/base64" "encoding/json" "encoding/pem" - "errors" "io" + "net" "net/http" + "net/url" "regexp" "sync" "time" - "go.mozilla.org/pkcs7" + "github.com/smallstep/pkcs7" "golang.org/x/xerrors" ) @@ -25,17 +26,188 @@ var allowedSigners = regexp.MustCompile(`^(.*\.)?metadata\.(azure\.(com|us|cn)|m // each time a parse occurs. var pkcs7Mutex sync.Mutex +// allowedCertHosts contains the hosts Azure intermediate +// certificates are served from. Only these hosts are permitted +// when fetching issuing certificates referenced in the signer +// certificate. This prevents SSRF via crafted +// IssuingCertificateURL values. +// +// Source: https://learn.microsoft.com/en-us/azure/security/fundamentals/azure-ca-details +var allowedCertHosts = map[string]bool{ + "www.microsoft.com": true, + "cacerts.digicert.com": true, +} + +// maxCertResponseBytes is the maximum size of a certificate +// response body we will read. Azure intermediate certificates +// are typically under 4 KiB; 1 MiB is a generous upper bound +// that prevents memory exhaustion from malicious responses. +const maxCertResponseBytes = 1 << 20 // 1 MiB + +// extraBlockedNetworks lists special-use CIDR ranges that the +// stdlib classification methods (IsLoopback, IsPrivate, etc.) do +// not cover. Blocking these prevents SSRF against carrier-grade +// NAT, network-benchmarking, documentation, discard-only, and +// the all-zeros "this network" range. +// +// IPv6 ranges already handled by stdlib: +// - ::1/128 (IsLoopback) +// - fc00::/7 (IsPrivate, ULA) +// - fe80::/10 (IsLinkLocalUnicast) +// - ff00::/8 (IsMulticast) +// - ::/128 (IsUnspecified) +var extraBlockedNetworks []*net.IPNet + +func init() { + for _, cidr := range []string{ + // IPv4 special-use ranges. + "0.0.0.0/8", // RFC 1122 "this network". + "100.64.0.0/10", // RFC 6598 carrier-grade NAT. + "198.18.0.0/15", // RFC 2544 benchmarking. + + // IPv6 special-use ranges not covered by stdlib. + "64:ff9b:1::/48", // RFC 8215 IPv4/IPv6 translation. + "100::/64", // RFC 6666 discard-only. + "2001:2::/48", // RFC 5180 benchmarking. + "2001:db8::/32", // RFC 3849 documentation. + } { + _, network, _ := net.ParseCIDR(cidr) + extraBlockedNetworks = append(extraBlockedNetworks, network) + } +} + +// isPrivateIP reports whether the IP is on a network that must +// not be reachable when fetching certificates. IPv4-mapped IPv6 +// addresses are canonicalized to IPv4 first so a literal like +// ::ffff:169.254.169.254 cannot bypass the IPv4 ranges. +func isPrivateIP(ip net.IP) bool { + if v4 := ip.To4(); v4 != nil { + ip = v4 + } + if ip.IsLoopback() || + ip.IsPrivate() || + ip.IsLinkLocalUnicast() || + ip.IsLinkLocalMulticast() || + ip.IsMulticast() || + ip.IsUnspecified() || + ip.IsInterfaceLocalMulticast() { + return true + } + for _, network := range extraBlockedNetworks { + if network.Contains(ip) { + return true + } + } + return false +} + +// certFetchClient is an HTTP client that refuses to connect +// to private or link-local IP addresses. This provides +// defense-in-depth against SSRF even if the host allowlist is +// somehow bypassed (e.g. via DNS rebinding). +var certFetchClient = &http.Client{ + Timeout: 5 * time.Second, + Transport: &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + host, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, xerrors.Errorf("split host/port: %w", err) + } + ips, err := net.DefaultResolver.LookupIPAddr(ctx, host) + if err != nil { + return nil, xerrors.Errorf("resolve host: %w", err) + } + if len(ips) == 0 { + return nil, xerrors.Errorf("no addresses for %q", host) + } + // Reject up front so a single tainted answer + // short-circuits the dial rather than racing it. + for _, ip := range ips { + if isPrivateIP(ip.IP) { + return nil, xerrors.Errorf( + "certificate fetch blocked: %q resolved to private IP %s", + host, ip.IP, + ) + } + } + // Dial the validated IP directly. If we dialed by + // hostname here, Go's stdlib would re-resolve and a + // hostile resolver could swap in a private IP after + // validation (DNS rebinding). TLS verification still + // uses the URL host via the Transport's TLS config. + var d net.Dialer + var firstErr error + for _, ip := range ips { + conn, derr := d.DialContext(ctx, network, net.JoinHostPort(ip.IP.String(), port)) + if derr == nil { + return conn, nil + } + if firstErr == nil { + firstErr = derr + } + } + return nil, firstErr + }, + }, +} + +// IsAllowedCertificateURL reports whether rawURL points to a +// host on the allowlist, uses http or https, and targets a +// standard PKI distribution port. Microsoft and DigiCert serve +// these artifacts on 80/443 only; any other port is rejected to +// keep the SSRF surface as narrow as the hostname itself. +func IsAllowedCertificateURL(rawURL string) bool { + if rawURL == "" { + return false + } + u, err := url.Parse(rawURL) + if err != nil { + return false + } + if u.Scheme != "http" && u.Scheme != "https" { + return false + } + if !allowedCertHosts[u.Hostname()] { + return false + } + switch u.Port() { + case "", "80", "443": + return true + default: + return false + } +} + type metadata struct { VMID string `json:"vmId"` } type Options struct { - x509.VerifyOptions + // Roots is the trusted root certificate pool. If nil, + // the embedded root certificate pool is used. + Roots *x509.CertPool + // Intermediates are additional intermediate certificates to + // inject into the PKCS7 object for chain verification. Azure + // PKCS7 envelopes typically only contain the signing cert, so + // intermediates must be supplied externally. When nil, the + // hardcoded Azure intermediate certificates are used. + Intermediates []*x509.Certificate + // CurrentTime, if non-zero, overrides the verification + // timestamp for certificate chain validation. + CurrentTime time.Time + // Offline disables fetching of issuing certificates when + // chain verification fails. Offline bool } // Validate ensures the signature was signed by an Azure certificate. // It returns the associated VM ID if successful. +// +// Verification has two parts, both handled by VerifyWithChainAtTime: +// 1. PKCS7 signature check: proves the content was signed by the +// private key corresponding to the certificate in the envelope. +// 2. Certificate chain check: proves the signing certificate +// chains to a trusted root through known intermediates. func Validate(ctx context.Context, signature string, options Options) (string, error) { data, err := base64.StdEncoding.DecodeString(signature) if err != nil { @@ -54,56 +226,87 @@ func Validate(ctx context.Context, signature string, options Options) (string, e if !allowedSigners.MatchString(signer.Subject.CommonName) { return "", xerrors.Errorf("unmatched common name of signer: %q", signer.Subject.CommonName) } - if options.Intermediates == nil { - options.Intermediates = x509.NewCertPool() - for _, cert := range Certificates { - block, rest := pem.Decode([]byte(cert)) - if len(rest) != 0 { - return "", xerrors.Errorf("invalid certificate. %d bytes remain", len(rest)) - } - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return "", xerrors.Errorf("parse certificate: %w", err) - } - options.Intermediates.AddCert(cert) + // Azure PKCS7 envelopes typically contain only the signing + // certificate. Inject intermediate certificates so the + // library can build a chain from signer to trusted root. + intermediates := options.Intermediates + if intermediates == nil { + intermediates, err = ParseCertificates() + if err != nil { + return "", xerrors.Errorf("parse hardcoded certificates: %w", err) } } - _, err = signer.Verify(options.VerifyOptions) - if err != nil { - if !errors.As(err, &x509.UnknownAuthorityError{}) { - return "", xerrors.Errorf("verify signature: %w", err) + pkcs7Data.Certificates = append(pkcs7Data.Certificates, intermediates...) + // Resolve root trust store. VerifyWithChainAtTime skips + // chain verification when the trust store is nil, so we + // must always provide one. + roots := options.Roots + if roots == nil { + roots, err = x509.SystemCertPool() + if err != nil { + return "", xerrors.Errorf("load roots: %w", err) } + } + + currentTime := options.CurrentTime + if currentTime.IsZero() { + currentTime = time.Now() + } + + // VerifyWithChainAtTime validates both the PKCS7 signature + // (proving the content was signed by the certificate's + // private key) and the certificate chain (proving the signer + // chains to a trusted root). + err = pkcs7Data.VerifyWithChainAtTime(roots, currentTime) + if err != nil { if options.Offline { - return "", xerrors.Errorf("certificate from %v is not cached: %w", signer.IssuingCertificateURL, err) + return "", xerrors.Errorf("verify pkcs7: %w", err) } + // The chain verification may fail when the signing + // certificate was issued by an intermediate not yet in + // our hardcoded list. Fetch the issuing certificates + // and retry. ctx, cancelFunc := context.WithTimeout(ctx, 5*time.Second) defer cancelFunc() for _, certURL := range signer.IssuingCertificateURL { + if !IsAllowedCertificateURL(certURL) { + return "", xerrors.New("issuing certificate URL not on allowlist") + } req, err := http.NewRequestWithContext(ctx, "GET", certURL, nil) if err != nil { - return "", xerrors.Errorf("new request %q: %w", certURL, err) + return "", xerrors.New("construct certificate request") } - res, err := http.DefaultClient.Do(req) + res, err := certFetchClient.Do(req) if err != nil { - return "", xerrors.Errorf("no cached certificate for %q found. error fetching: %w", certURL, err) + return "", xerrors.New("certificate fetch unsuccessful") } - data, err := io.ReadAll(res.Body) + limited := io.LimitReader(res.Body, maxCertResponseBytes+1) + certData, err := io.ReadAll(limited) + _ = res.Body.Close() if err != nil { - _ = res.Body.Close() - return "", xerrors.Errorf("read body %q: %w", certURL, err) + return "", xerrors.New("read certificate response body") } - _ = res.Body.Close() - cert, err := x509.ParseCertificate(data) + if int64(len(certData)) > maxCertResponseBytes { + return "", xerrors.New( + "certificate response exceeds maximum size", + ) + } + cert, err := x509.ParseCertificate(certData) if err != nil { - return "", xerrors.Errorf("parse certificate %q: %w", certURL, err) + // Do not wrap the parse error; it may contain + // fragments of the HTTP response body, which + // could leak internal data to the caller. + return "", xerrors.New( + "fetched data is not a valid certificate", + ) } - options.Intermediates.AddCert(cert) + pkcs7Data.Certificates = append(pkcs7Data.Certificates, cert) } - _, err = signer.Verify(options.VerifyOptions) + err = pkcs7Data.VerifyWithChainAtTime(roots, currentTime) if err != nil { - return "", err + return "", xerrors.New("signature verification failed after fetching issuing certificates") } } @@ -115,6 +318,24 @@ func Validate(ctx context.Context, signature string, options Options) (string, e return metadata.VMID, nil } +// ParseCertificates parses the hardcoded Azure intermediate +// certificates and returns them as x509.Certificate values. +func ParseCertificates() ([]*x509.Certificate, error) { + var certs []*x509.Certificate + for _, certPEM := range Certificates { + block, rest := pem.Decode([]byte(certPEM)) + if len(rest) != 0 { + return nil, xerrors.Errorf("invalid certificate. %d bytes remain", len(rest)) + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, xerrors.Errorf("parse certificate: %w", err) + } + certs = append(certs, cert) + } + return certs, nil +} + // Certificates are manually downloaded from Azure, then processed with OpenSSL // and added here. See: https://learn.microsoft.com/en-us/azure/security/fundamentals/azure-ca-details // @@ -461,5 +682,398 @@ cS5BhMDPPeG3oPZwXRHBJFAkY4O4AF7RIAAUW6EzDflUoDHKv83zOiPfYGcpHc9s kxAInCedk7QSgXvMARjjOqdakor21DTmNIUotxo8kHv5hwRlGhBJwps6fEVi1Bt0 trpM/3wYxlr473WSPUFZPgP1j519kLpWOJ8z09wxay+Br29irPcBYv0GMXlHqThy 8y4m/HyTQeI2IMvMrQnwqPpY+rLIXyviI2vLoI+4xKE4Rn38ZZ8m +-----END CERTIFICATE-----`, + // Microsoft TLS RSA Root G2 + `-----BEGIN CERTIFICATE----- +MIIFiTCCBHGgAwIBAgIQCwxrLEZpF7BHc8ZH1K/AyDANBgkqhkiG9w0BAQwFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0yNTA1MjEwMDAwMDBaFw0yOTA2MTkyMzU5NTlaMFExCzAJBgNVBAYTAlVT +MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xIjAgBgNVBAMTGU1pY3Jv +c29mdCBUTFMgUlNBIFJvb3QgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQDf6oufR+EoEHGvQdYZ25JX3mur5i7erTpgg7cTmKxbuTILe+ufcidrXUCr +vhgGk7IN0hLtuHT1fy/qqBeU9jMWV4reIHwh3bfarN5OZLBazUt18+8CZE3tUtqj +jwTokfjX+z8Z/U5FOV7oKcPW8mevswCUwY3h8EoYmDn6wAmEM0EFAwWr9HXhU6Uh +klxETOZgV6SQApfH1diTBDJK7YVR7dbFuqA/Noovb0w5qARpIoQ7dRT32T60qdAH +QTiBfkZIHegZ5nC4oKoY3XK/fn21bE4ZcBGEBBOB1GL9nGvxHN3/7Kfg5seNMUu/ +8mszzNGMtv6xG6NKqF8OfzF2OD8HR2wBqKylFNqCsF8fbLyJGsASKst7lx8oLjEW +ilNMdWb5fQHWwmCqZY8xnnLLzJst5UQZk1erbo7C2S5lsHIt56HDoX5JHVln1gnU +GBJtwJVFeMnxYGrk9u4GJDtzSloRwj6XYcB47u8TpzDiSjgt7lgXEyC3NirfCzK0 +wjixkd0SsEW2fMCxHWKhnd1xEhWWAZ0KCfWx3bPZ4DhCNPZptsOvFnP+1EP4Q+RY ++U+z8+zWPZQ6QDgVqwyG0GTOGmPohJRVCVq2BLbRPpoVx2QRgNAbgg5N/0WesmUH +JR/bmsjG7NZbhVAEnxzLXSCCZ5554t/o8uhvxCByMIblnXUnNQIDAQABo4IBSzCC +AUcwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU3pGGSLehMVkx8UtfB6nciHna +qHYwHwYDVR0jBBgwFoAUTiJUIBiV5uNu5g/6+rkS7QYXjzkwDgYDVR0PAQH/BAQD +AgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMBMHYGCCsGAQUFBwEBBGowaDAkBggrBgEF +BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEAGCCsGAQUFBzAChjRodHRw +Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290RzIuY3J0 +MEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdp +Q2VydEdsb2JhbFJvb3RHMi5jcmwwEwYDVR0gBAwwCjAIBgZngQwBAgIwDQYJKoZI +hvcNAQEMBQADggEBAAu8tCs3dMVLpzYCNsav4RPMipqXG/zjRIzuVADl5EEaRvAL +djT/mVViNaqtipwMWmLMQ8DL6kodvWsdr7EZJWac93luWyWAJIGFx3ktNV9CCXjt +n+Jl1cQgUIIQj2o67RiOSImrpgn44YD8BnUWJyVaj7g6cGwYR/Bj9FMO2RU1IPOR +PRMBoOL6JAhFVnfRZ6kxQtBX/xomvsVD2FepY/+v8zrY9ntLEKKXoc9mvmdnCfm1 +TOerGSu/Ij193sb372M4LN1WxPkJUtrf44hv1W1r9whBL44+hjGf8XxK9dZhpEZG +KO9XurBvktjSdyXte6YpzjtyeRHU4KdUbTUrpHo= +-----END CERTIFICATE-----`, + // Microsoft TLS G2 RSA CA OCSP 02 + `-----BEGIN CERTIFICATE----- +MIIHuDCCBaCgAwIBAgITMwAAAAxJZKFvRCA7IgAAAAAADDANBgkqhkiG9w0BAQwF +ADBRMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u +MSIwIAYDVQQDExlNaWNyb3NvZnQgVExTIFJTQSBSb290IEcyMB4XDTI1MDgwMTIw +MDMwMFoXDTI5MDYwMzIwMDMwMFowVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1p +Y3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IFRMUyBHMiBS +U0EgQ0EgT0NTUCAwMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALFf +yY9swhGdLUa31wstRz9z5Kg7nDbxaCBFQF5wYUrMSZceyBaSsy13mG08dhwgisMv +DGOfv69rBwYah+MKkNaUAN7gHXT1xc44NZMg+QhaZqjbsyA0nUOFRRIIF3ClrguD +qttEyOtoR1WahF3ZqRjCUoahH2JAZa7U81468pFe21rbtaROBWKY7N0Voa+FJ8ZL +rDKswmimzMnSfTdrxhCQBXkivGPm2X7ZwxCMknFtfeJ2FD0Ki8sjYBC4GBl2xOKh +dtoBzYO9Ae3YGK9XQu4Nha6pkhh5ywEzxk6CbETWKfTPxlF+4ZFi+Iyo6tr5QKBY +yHhumjrUQOdQGMmZHupCPme+dwWLnBsIthM85cE8p4yir0mhkUVlMZgDwPUhu8QP +3x4DFqW+OHlq2puE5aOXX4d3ypb/u1H47yEkwuK1fDl7ROViyRaIHNsTIuz4trEc +AFVOPpZ63AwFHI3jXiMALVv/4lWAQYU2lTD1mZO3buY0RbwzlYZzCimVwZdX1dbu +n8F0w8WgYj530r1tEONpi36oUbDYSsNBvqhP2mrDWCUWHFk8rQ113LE/VRzRdguI +56IxJQN7UUxZKzf+lSRUQqu6J1874QcvdqDAy8t2kR6dpuf9SkDi1I+hPbqGRJ1p +2Bkji1+hg+VlV4tN1nykYypkQ1RHhS8EsKrBL0o/AgMBAAGjggKBMIICfTAOBgNV +HQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFLgvM6Z8UU9/ +Hy3VyBVCOKSyDo8vMBMGA1UdIAQMMAowCAYGZ4EMAQICMBMGA1UdJQQMMAoGCCsG +AQUFBwMBMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMBIGA1UdEwEB/wQIMAYB +Af8CAQAwHwYDVR0jBBgwFoAU3pGGSLehMVkx8UtfB6nciHnaqHYwgasGA1UdHwSB +ozCBoDCBnaCBmqCBl4ZJaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j +cmwvTWljcm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyLmNybIZKaHR0cDov +L2NybDIubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRMUyUy +MFJTQSUyMFJvb3QlMjBHMi5jcmwwggEQBggrBgEFBQcBAQSCAQIwgf8wYwYIKwYB +BQUHMAKGV2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj +cm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyJTIwLSUyMHhzaWduLmNydDBp +BggrBgEFBQcwAoZdaHR0cDovL2NhaXNzdWVycy5taWNyb3NvZnQuY29tL3BraW9w +cy9jZXJ0cy9NaWNyb3NvZnQlMjBUTFMlMjBSU0ElMjBSb290JTIwRzIlMjAtJTIw +eHNpZ24uY3J0MC0GCCsGAQUFBzABhiFodHRwOi8vb25lb2NzcC5taWNyb3NvZnQu +Y29tL29jc3AwDQYJKoZIhvcNAQEMBQADggIBACGusqgM8zXYTiHTNvrDXqobFI9g +GF1dNgkZIizyNNI8EMiG/fq7bhDwbokxZH2xDIfoNgtGI8r88DX8dQV3aUm07IKW +lu/qV9VJO8gF5/GyxHrgxCvW/IXBoJNnHGLyCWH6rJjuwG3cGIPYplNMUfRnyGCk +SYR1qcRW0Dx5OTh/JlrXAy7/UJIBU9COSAlKv1APr49CYz4iYl25la+tEonWkVE2 +qZHrnRuCxyOR7mYlQWKIzdkQVnChmsvzjEjgkW3qv4dHGvanfUeKlou+t0tm4MB7 +rm2wmTV4ydACIEzKDnV40wNz7JFHAgJ6KtGDk8KfhIk1Nn2iRPxzo34EIBWL9uuU +E6C3le07w3Z1LoABEJ2vYMKPFVUwG7v4A1+Y5QQtGrGs9NrpHA6QGOkOypPIyHp/ +hoZ2Gp3WkyN5UXNDKJIGmE/clGQt86/K3MqZ9RiwwnHYM0+IO/KTinNTSbW+ZhMg +Fxki/Ug55kLA33b4T+cT6HUXWr5yM9iLAW3oyxTIhld1nD5esMt70bNF7WgLW0AA +txkxhDYDmKQ3oyHrrGPZWLz4N7wxHCZbyHbDgjCyiPYujpqsQ6fxthalQtkV6ycu +GLP2sZhSv89myfSgfHkwtcr7bRL0my0R94CXneQhqcXG3undRwlgikU9gfiuTaZG +h8VmoQHGVMiqVtXE +-----END CERTIFICATE-----`, + // Microsoft TLS G2 RSA CA OCSP 04 + `-----BEGIN CERTIFICATE----- +MIIHuDCCBaCgAwIBAgITMwAAAAsT5WZ9SptVgAAAAAAACzANBgkqhkiG9w0BAQwF +ADBRMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u +MSIwIAYDVQQDExlNaWNyb3NvZnQgVExTIFJTQSBSb290IEcyMB4XDTI1MDgwMTIw +MDI1OVoXDTI5MDYwMzIwMDI1OVowVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1p +Y3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IFRMUyBHMiBS +U0EgQ0EgT0NTUCAwNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJw6 +JAhaGJLyntVgzeLm+4BH20SuK91tEHAhFUUpqtLH3ObEQrGgjVLgT1w5VQY2TRfB +WGY04oVSn+Kk/sbewsI7hr/KrYcpBusdSR1fgdu3pKxWGtYSh/1fQEnioAxqhMZO +b98kuJqVdFpZf63pPBMVeEDM7NrviDZKkN7qYweUw4NqGq6Y5vFkgFopZwToVvQh +psVGjcjdAqu8BvBsR3gjuziwu/tNcbDfIsN/Gn75napBKtHeaN2VdCU4ZskWEcVZ +PSqxaLmTO2boPOH8p/8sa1DgwLnIcXOTsXe/7apNDgpV2xOccuBprYFM2iP5Bss/ +7UKKhowN0gwVJdCGaOt4VqouXAizTTOATu41PC/Den3BZnJgaJD06/YI7BPXiZJf +XFL0h5V4sTbhs0JTbjo3NwfIc3Ueu11uZ8mafMtK88bN8E71hvsUNRlGPZeGcmTd +Qzbv1FeCACIMozrts2VwZfmCpbq40urAaIo1N6BA9f4CiWaoMPiUR2JXR7J7m4zH +lbzrmvbGjESJ2xbmHv3nifyBNTUw6i99iWRSs0YZNOM7V08KGCAx78X9ubEn9pdZ +NfsKwkTW0LLtVU0dV0h1EtfymGAWnsQGnNSufi5lx1PkIiUYMGNqkFfFlLT35U1M +DVTHH6k9TQpGCWLQIyJR4443TMX0AUCZBYLTorBTAgMBAAGjggKBMIICfTAOBgNV +HQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFFQMvOwY933x +A+KEvjRkRGfPdR9lMBMGA1UdIAQMMAowCAYGZ4EMAQICMBMGA1UdJQQMMAoGCCsG +AQUFBwMBMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMBIGA1UdEwEB/wQIMAYB +Af8CAQAwHwYDVR0jBBgwFoAU3pGGSLehMVkx8UtfB6nciHnaqHYwgasGA1UdHwSB +ozCBoDCBnaCBmqCBl4ZJaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j +cmwvTWljcm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyLmNybIZKaHR0cDov +L2NybDIubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRMUyUy +MFJTQSUyMFJvb3QlMjBHMi5jcmwwggEQBggrBgEFBQcBAQSCAQIwgf8wYwYIKwYB +BQUHMAKGV2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj +cm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyJTIwLSUyMHhzaWduLmNydDBp +BggrBgEFBQcwAoZdaHR0cDovL2NhaXNzdWVycy5taWNyb3NvZnQuY29tL3BraW9w +cy9jZXJ0cy9NaWNyb3NvZnQlMjBUTFMlMjBSU0ElMjBSb290JTIwRzIlMjAtJTIw +eHNpZ24uY3J0MC0GCCsGAQUFBzABhiFodHRwOi8vb25lb2NzcC5taWNyb3NvZnQu +Y29tL29jc3AwDQYJKoZIhvcNAQEMBQADggIBAHxIccK2wEWrdA/GP0ni/A/Wdf3N +UNHgS7Oz0aiZX/5dNQ1sC93QrWFgGIk44vC3NdK1IToMDliZOHzU190CTdTc9e6Q +43tnk6is1BtQu8VP5tPxtR7w/5m8IzOwyKimJ9bRW+1vFN5LBxoMUP0O377rT7KY +EMsiKuYd10unrhXRATYJC4ZDT07nxX5co2uDLkk+lIiZi1LTlj9xmCQvN4L6bHTy +vNsGIbu4UGdwJBW2CyKP97kn5AN8hJW3ZgSpklXCvRHHIQpyf2XAYKZQSen2I0gg +Oo6SJqgXjJivFKc9zkytwI6MPETxf/sT+RTXezM9EF5k5yc9DEzicddmzq73TrZk +ulQrt/0D15hnmDeyCmMg5bD72KSNOi5CIpoi9CZVgzAVx6JCs7/QNsU2UqdzZ3pz +blSsvOmJ2KXrH22sJ1DEyOvUHFQpTbu23qvXx/EfFGS6f0cxZe95fRTE8BnkgHbn +OygAm0RvJFf1B9yOWrQAWJdWsQv6CHVx3htTyO698KsiTL1rul2KRFk8JGuqvOl9 +i19KTdeMVLCrdpuAKE1FdGUQYCH5jnlf2pL7F4QA28SuglmBPCd3nlb3B8i9vj2R +xZeK5pwPWRZYSGx9pBYy7RbJLaeW9eT9xc9lpN3XAOjJDvSdsqQCgMwb8CjrsDF3 +NJ7DzNfImza7xSXi +-----END CERTIFICATE-----`, + // Microsoft TLS G2 RSA CA OCSP 06 + `-----BEGIN CERTIFICATE----- +MIIHuDCCBaCgAwIBAgITMwAAAA3vac0tciNzVgAAAAAADTANBgkqhkiG9w0BAQwF +ADBRMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u +MSIwIAYDVQQDExlNaWNyb3NvZnQgVExTIFJTQSBSb290IEcyMB4XDTI1MDgwMTIw +MDMwMVoXDTI5MDYwMzIwMDMwMVowVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1p +Y3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IFRMUyBHMiBS +U0EgQ0EgT0NTUCAwNjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMA6 +3O0lAmKs1KFQsHRLSvpoHnItA3OBYhuwcRTjN+/jZp2gCThyItWRknozQ1z2e3ku +VknTIZzBVzgMAbMC5vGd1WNYEatYL2jU+9MtrLUKJyVpCEkGFavOSHJh/7y0wNJd +MdGceI32eNhzOjJjg7BuvwRreP7wop6GJOQ0qMX/aFwgk63E9AbzyEwqaBMR3GjJ +eTvmzs6k6TgXpT0nxP6mtVkK6bL+AmR5pm+6SKwr0dFJszzpn18qFsep36B1IaPD +jf9/vnjnCplS96Yni2wPSLmEAgSnIw677sQKlwjcWZw9Hsr/h3KUn3EewxdQItrq +5Ss1hYNd/ILa7oGzwkf6Z0KyK2UvYxjWTNzdun3nvfXhqWOKUqde1S3nIh46tCQz +m3jlEKKQd/YBBziZfHABUYrs2X859cEihTJENpRXJcwOnr5/fz78ZntgsCGpzepk +inb9QoxGwiU4fhAEZ1sjPnILE64/6mbRfH79nkl1runTkuDJfRMUGtWtKUI+8Rkr +Ji5x7sACp2nPYY/d631rda0pmRzmSbqPuma5thB96714U3d28epdz8Pu6xudP31c +YX0WF6UGuxocZtUZtrbzoQ9m0dBtdC3tD/pnbO6Kk1oJ1AlwKjLNWhj77HkauWon +Ah1b6vznIL614ukB0lg3xXOjNcwxaUqKa5te1Ea9AgMBAAGjggKBMIICfTAOBgNV +HQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFAxda81KNAFg +NDQkAeA/UAWD66hFMBMGA1UdIAQMMAowCAYGZ4EMAQICMBMGA1UdJQQMMAoGCCsG +AQUFBwMBMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMBIGA1UdEwEB/wQIMAYB +Af8CAQAwHwYDVR0jBBgwFoAU3pGGSLehMVkx8UtfB6nciHnaqHYwgasGA1UdHwSB +ozCBoDCBnaCBmqCBl4ZJaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j +cmwvTWljcm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyLmNybIZKaHR0cDov +L2NybDIubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRMUyUy +MFJTQSUyMFJvb3QlMjBHMi5jcmwwggEQBggrBgEFBQcBAQSCAQIwgf8wYwYIKwYB +BQUHMAKGV2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj +cm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyJTIwLSUyMHhzaWduLmNydDBp +BggrBgEFBQcwAoZdaHR0cDovL2NhaXNzdWVycy5taWNyb3NvZnQuY29tL3BraW9w +cy9jZXJ0cy9NaWNyb3NvZnQlMjBUTFMlMjBSU0ElMjBSb290JTIwRzIlMjAtJTIw +eHNpZ24uY3J0MC0GCCsGAQUFBzABhiFodHRwOi8vb25lb2NzcC5taWNyb3NvZnQu +Y29tL29jc3AwDQYJKoZIhvcNAQEMBQADggIBAMN7IRVg4E0mXAS3hbmC1eXyI7Vc +ZEHqZawlEK8DD8wM8pQnws+95Pd7kRhqie7pyibPRXbGtdHtScqOkE7bbjmrGKe+ +GdG6wLUP8TD02NaPmho9pqumBRz2PoXwyNztvgooOywDxDXAxtGVV0vKc7tPYCbb +3KAHZJkJM6Kuee9DWVwEmhsiXryZjsGwBD7fEoXcC8BOtwekpZiu9SWM5ETTFRyr +tIUgy2S2IYSI6yxgska7/NTJuc6yjfs71c6QO8KJ+Bz1yoepefpVuZ4t269mej8k +jE1ri+3tKa4iNlCBVpLk9moe0Jtir267WQk46CjJd5VuUw79Q+rkupbTM0hoIIdA +GUeWhPBooyuE6CP6vpmyhGQooYCeUk3CGG8zkv+yhGjyoM/sCu54OfqxoMukmeut +eMn9FRVD5FyltEZ5FZ2p7p+aGqjsg5poy5fLyl4qfAEDhKdM7ZLqy6D4Is6POqof +fdRfQ+r3VVvXI9dHr4o49zMQVgUUV/la+kOWk+WqNZrh+aONK09gs2fReMK8xExF +ntTP6qV5mbRsgKxea/w+jLWTYyHLdPOsA1OaifWGVBNIzlaH5wrWhyoRwRKb+1I0 +2sBzhfNVJf8gDI/lxJEpPTgIjMTm97Q+KW8C1QMprzVbUWVisUMp0Azxm+ZE4PoM +KWDfAOw0TwsQ6jyn +-----END CERTIFICATE-----`, + // Microsoft TLS G2 RSA CA OCSP 08 + `-----BEGIN CERTIFICATE----- +MIIHuDCCBaCgAwIBAgITMwAAABHxAKfrBeuhAAAAAAAAETANBgkqhkiG9w0BAQwF +ADBRMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u +MSIwIAYDVQQDExlNaWNyb3NvZnQgVExTIFJTQSBSb290IEcyMB4XDTI1MDgxNDIz +MDM0MFoXDTI5MDYwMzIzMDM0MFowVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1p +Y3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IFRMUyBHMiBS +U0EgQ0EgT0NTUCAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOdW +tSZphPC6ib4yyTEHy9WgBJ0sdgI+X31mtN8N9QouoqaVVKURKVPbfJnmmZMuD/n4 +hedo1DDxuO5qc1bEfF1hWbiltLwGE+cttQqPyzgu4KYhnj8buvoj4kVElrXgc+9n +qTJ5LeHIdeMCGKMbAgmjVlNrw8mq5PX1n3iWg9dZIcBe/wDsEcG7h+MFxsrZ4Ebu +sNZuxBGjo8O2xIkJi76spN1iTDG4jhrTOQU7viUCzVAWAPnV0/AQbRXCtgz0hozA +46d0+vdh99UDO3MAaqtHU60TQzFovz3HJJ6eGVRh11oIT4JFchuYPZcAF8JfiF6W +PaW8ihg4lXRGbijiy+OnT9Cs26Mga6PyfyiIW3MQ5MKwN9zL5q1J0gZjhTqd8h+5 ++/QlptCMhuoVkc/UGsvVOVtlKbdn5cp5QK3xVN40z+o+Yh5Qh2RHizK1aXFkU6E7 +K6yGLtIevJCaQjAoTrGj4JnphmqU7k4Fx1MwxV/gpvkJh3bml5SUck+F6QHZc44K +lTFgJB4a94tTD7LbbysFNXtnBFlD9/rOJB9lj1wL2yzPRe7kcgUay0Is+ZAa22bK +7y0JhD7sN8K+DqmU/Q8NliECD65IDH0MzPyhleKes5zDL1TC79p7NGoMZk/uKlcL +VKETn1u878Zjj5YwFLyiQT76L4zI887/da70Q2cBAgMBAAGjggKBMIICfTAOBgNV +HQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFA+yMoDtf4qc +AIQ45tjX9nCFd+16MBMGA1UdIAQMMAowCAYGZ4EMAQICMBMGA1UdJQQMMAoGCCsG +AQUFBwMBMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMBIGA1UdEwEB/wQIMAYB +Af8CAQAwHwYDVR0jBBgwFoAU3pGGSLehMVkx8UtfB6nciHnaqHYwgasGA1UdHwSB +ozCBoDCBnaCBmqCBl4ZJaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j +cmwvTWljcm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyLmNybIZKaHR0cDov +L2NybDIubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRMUyUy +MFJTQSUyMFJvb3QlMjBHMi5jcmwwggEQBggrBgEFBQcBAQSCAQIwgf8wYwYIKwYB +BQUHMAKGV2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj +cm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyJTIwLSUyMHhzaWduLmNydDBp +BggrBgEFBQcwAoZdaHR0cDovL2NhaXNzdWVycy5taWNyb3NvZnQuY29tL3BraW9w +cy9jZXJ0cy9NaWNyb3NvZnQlMjBUTFMlMjBSU0ElMjBSb290JTIwRzIlMjAtJTIw +eHNpZ24uY3J0MC0GCCsGAQUFBzABhiFodHRwOi8vb25lb2NzcC5taWNyb3NvZnQu +Y29tL29jc3AwDQYJKoZIhvcNAQEMBQADggIBALH1cTiVRvY627z2zjtZPaftzKA5 +tsGFiJF2d9OJZv6EHbZzPq9z5lcSX9YgzWfHecgBO1xNCfP/tmgt4gGWC31L42Hm +AwjXsYB6kZsumOjCEsaVff4o+6dvsVUwjrEmC3Bd3Szmyl5++1ZVIV53mxSLxBOJ +QvpYuwzdC/r7+JO/mB8OmkUPzpXM0MSWtElZE/e6gpcNBnI/y2EU00OhsB+zzQ0H +Kc0Dzk/Qc+P3B1A/xD5ER97Tj14NUz+KfMIIiY5QK6QnoqcrXHdXcXbGCUFixztD +rcVFsc4nazkf8I8QXba4hBm6xetE/7/KIoV0bLEjiP0GtHOEh/u3OSUaVUerdsog +rFnTLeBDyQ6GuTDOl8m/01f8ZRDDnayFpjT8JxfxeKhCXGW/avXsEr3orIzGr720 +WtESmCwsBdPcXwCo6kqzkMNfDk/MGEffOR8w0tHK4IjBYIB2Whh82HX412gslYYc +GfzRoQCQ++/ZZuEeog+c0mWCb59zaAm1772pxD7C0DRtUrCp/lrFWMmga9561S7G +8duFJgbXoOQhfKVE8mrfesrsr5S5hKIVABr1Mgi7XeJePfcEV4qv5+ZHcW8sdFrB +o00ACacNAf4Ys3p/x756lhnDffgY8WA9vST4dIn9WPfBLxE8odWUOUASpReiKbB6 +y/9qbWptUU9CiA3R +-----END CERTIFICATE-----`, + // Microsoft TLS G2 RSA CA OCSP 10 + `-----BEGIN CERTIFICATE----- +MIIHuDCCBaCgAwIBAgITMwAAAA8zIGU37kKuTwAAAAAADzANBgkqhkiG9w0BAQwF +ADBRMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u +MSIwIAYDVQQDExlNaWNyb3NvZnQgVExTIFJTQSBSb290IEcyMB4XDTI1MDgwMTIw +MDMwM1oXDTI5MDYwMzIwMDMwM1owVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1p +Y3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IFRMUyBHMiBS +U0EgQ0EgT0NTUCAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPAM +T1tf/EIctM4/9QcrpoN+yZ15z/bprKV3wzep+vcH9S2Y+BFm60IqDtLRBhn2dxNf +hOzWUZNsIeMOhab/0uz9JIK9BPnvjePhxd110ASaThQ2GfstEqMVwPNvakTVcWzx +S5gbeTD3nBe/fTIJOVs2jKAIu0AslinufL0O+OxtzsFOdsFYLk4ymsd8y8e/t133 +NVR4zLGHugXFNQFwBMPoXfixtN9HzUxmmuhn1J4eoCEfM0cFO0QIz2uIUlkyePVB +jiUu0AINAc929y005GedaLGAtk1SsyCXK6VTjHeVtXOAzYj/2pc24+dvMqB18bu/ ++jxlqzYRv3b9R/9sh2C+DOXqlvULojcnANHnAjAB1YABwpDO77Pr03hgvgo/+2zG +wtGrJxcXCYR5kUKOdmg3EZvOx3Ypv9Vc4nwNX2dS/W05+lEt37KIA/FhIKr4tLKf +0/oosLWn44O6+kQ7d9yiLCvo4lOImvsMIN6ie06AkHEbfJfU2/w9msGh3urnrkzl +rq92rIfNZLyiNBrTZsNrYXyb9eZZefuADhZrwPEp9O2dl446xCmTBzT/4r+tmlkl +m4YdQ37LbpX1juCpi1eATgvmYH3ASdUEvCDKBNJc6j+MSX8dubpgbde0ZLNcNOo4 +8/nB+KkLfrr10fx6G3/bCGV9w5cF7K8vx94M+rI/AgMBAAGjggKBMIICfTAOBgNV +HQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNBMg9GOcS49 +NLH/m3ksjnTU4ngGMBMGA1UdIAQMMAowCAYGZ4EMAQICMBMGA1UdJQQMMAoGCCsG +AQUFBwMBMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMBIGA1UdEwEB/wQIMAYB +Af8CAQAwHwYDVR0jBBgwFoAU3pGGSLehMVkx8UtfB6nciHnaqHYwgasGA1UdHwSB +ozCBoDCBnaCBmqCBl4ZJaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j +cmwvTWljcm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyLmNybIZKaHR0cDov +L2NybDIubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRMUyUy +MFJTQSUyMFJvb3QlMjBHMi5jcmwwggEQBggrBgEFBQcBAQSCAQIwgf8wYwYIKwYB +BQUHMAKGV2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj +cm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyJTIwLSUyMHhzaWduLmNydDBp +BggrBgEFBQcwAoZdaHR0cDovL2NhaXNzdWVycy5taWNyb3NvZnQuY29tL3BraW9w +cy9jZXJ0cy9NaWNyb3NvZnQlMjBUTFMlMjBSU0ElMjBSb290JTIwRzIlMjAtJTIw +eHNpZ24uY3J0MC0GCCsGAQUFBzABhiFodHRwOi8vb25lb2NzcC5taWNyb3NvZnQu +Y29tL29jc3AwDQYJKoZIhvcNAQEMBQADggIBADUZyumodeHYyv0lwTtS4eeeK5Ti +9DrST9oGIlIaARjjorq3txwkMnUNZ0R9nUqCS/rjROlG9gBFCcJS6Wcll8e3i1p3 +fEAelOO8jG04KbwnfRISPcvL5MRG4qUBwBDRIPoOA+RD2yaHJazIoLMEal7wQz8P +e/XOI8O3yb773pt9k7OHPt/G2z3J9KxxANKkZYE2WZ8cNuWJ0XqZSntVS8LVjNB5 +AXmVDzlDi7MKe5LVWhAYdukdDW8yMfS90RbxqKNn8g6acAzjlq8D9G29FHlqNsPx +tnO7xgvVJkaIVEVwqswfPYtv4+QXpoEA+32DWIDi8jw7oxhiEzZn/0/i5W9qZ+bo +WmQ6oEWdPxcMZofwgSc0ILA1JGQodkN6dJjiK4AJCrywuQdHKSgufeB3QaSMNni6 +Mx1WjtkQNYlZgwBpzrd4ve2vgj/OyIkymFkIXeEBlljEZRl9JoWdEJbllcURzoJv +FwZxFQ8svzcyUhVotJWOU12X7ePbEz7BMbF5k3N9cjsbTE8GSRWEc/MdWlEspNRY +4Bm/NUgpYJmr6ntCA76cPRn3R1sLrIJXqg29/yJgMN8sT1fTJdXa/Y4GUU4FNXiY +OKMnMW8xmqmqTaw6RGhgcGj0U2vNsi2uJhiH34xXtfhSwVbnwLFNXwpVaxQrVPs9 +qca9YAf4sPRL4+6r +-----END CERTIFICATE-----`, + // Microsoft TLS G2 RSA CA OCSP 12 + `-----BEGIN CERTIFICATE----- +MIIHuDCCBaCgAwIBAgITMwAAABB9WYYP1k1yQwAAAAAAEDANBgkqhkiG9w0BAQwF +ADBRMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u +MSIwIAYDVQQDExlNaWNyb3NvZnQgVExTIFJTQSBSb290IEcyMB4XDTI1MDgxNDIz +MDMzOVoXDTI5MDYwMzIzMDMzOVowVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1p +Y3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IFRMUyBHMiBS +U0EgQ0EgT0NTUCAxMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKHG +TgBCMs4LbHALHnm618HNCEhlXTLmoRK/un/49LlfpuKidaPMdQ0mNb4pl6iWKnUo +TTRCk638rRcUAemUhpTO9pdPfzX/uaaseB6h88hlBVGQV5UyrE7hGeH3zCMXBVjZ +ghGwt4DKvgO/a3YO43xMupzkFJfx1SddBW4oR160OYgr6FLRELEboaASwYsuoYl8 +wLo0O1SqBxz++ZNEfsspAamx3so6+XLVtpeMME/mOYdwebrBrtzS4nmE/9qknWFT +SLo//8NRd7PQ49pzLGf6CyVCiRZIvG7y2+jesPhICU+s9vJ3qBr2go1jU1h5Rpvv +TPHQGsmNTWpepQKcfBfK5rt8YzF9NHBaaLIAcCe90bIYKENMutS5Z6BVn69ZYyMi +3DklCE3V7uozYYkIei5zoI2NIfdjGQaXaEImqA12cwknJfqkWhA1bErK6n4Gx0Y+ +MqIgIE0wpRBuwrk46ncEAX4NKiRQd1XOpUwKfI/O/I7kdVjrq+Ghd86HJtuSqwUF +WgV3JbUArAqZtgC5LjFoIjf2lCGzuD2uDBSKM9d8dLhcJRWeJDy7pheaQxsDQcxz +cPz0XOdW5KgdZIrkSWjRChpWY5LcCo5O9SEqvJCtmeIo4TUzW5CTxYG6fkEgSSA0 +wiciw/x8SE7YVqxKybryGpZ3y3WxGd2mxktUEhufAgMBAAGjggKBMIICfTAOBgNV +HQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFDGlpYlD78es +MRU+SHrjBsbp7bwqMBMGA1UdIAQMMAowCAYGZ4EMAQICMBMGA1UdJQQMMAoGCCsG +AQUFBwMBMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMBIGA1UdEwEB/wQIMAYB +Af8CAQAwHwYDVR0jBBgwFoAU3pGGSLehMVkx8UtfB6nciHnaqHYwgasGA1UdHwSB +ozCBoDCBnaCBmqCBl4ZJaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j +cmwvTWljcm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyLmNybIZKaHR0cDov +L2NybDIubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRMUyUy +MFJTQSUyMFJvb3QlMjBHMi5jcmwwggEQBggrBgEFBQcBAQSCAQIwgf8wYwYIKwYB +BQUHMAKGV2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj +cm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyJTIwLSUyMHhzaWduLmNydDBp +BggrBgEFBQcwAoZdaHR0cDovL2NhaXNzdWVycy5taWNyb3NvZnQuY29tL3BraW9w +cy9jZXJ0cy9NaWNyb3NvZnQlMjBUTFMlMjBSU0ElMjBSb290JTIwRzIlMjAtJTIw +eHNpZ24uY3J0MC0GCCsGAQUFBzABhiFodHRwOi8vb25lb2NzcC5taWNyb3NvZnQu +Y29tL29jc3AwDQYJKoZIhvcNAQEMBQADggIBAIvpSERgLgnzdc+XVB99zGCGNpur +hIXJ2S+lopZDMMP/lqi4uwX3RSlmjGNKCwfHmMy3KjTMMPqiurxuX3vP6Yx7h3g0 +p0+1m7F3PYBgCibUcMJfwtZbKu/Oot19mHsAsHu01BDZTlPbowPpVD8qNtpsiDl4 +PjOe9/EW5M/HbrKrZg0ZvLm8ezsePgP0CezXoa2SQSlLssUOWUn6iKxdi0d65jXv +FPYRfOSmWKcQ/SBGWeUjsSuctga3DNzExktOHySKjskO3JTYo/hm7hnMdxLeVGHI +poenawCSZH4kxZCkO8SXrqV4gvh88CHlZ12mBvNw2kskEGYTgRdfpfGLudwxdvV+ +AOGu60olNg8VosFWJMcZYPFTAFoZTwdBSprBnt93sBUGXDPwWQNxpSvO50DR+r/u +sdY3/zfFSfQUC5X2/BOuwSUgDdJ2lf/ettl/+TGAVVmNR7PfuwHl5obG3LR964JV +jPLmFw8Vc4CU8YuUStyGwQxse9CPrp9YpcPsztiJB2ugB6/FhxM7UDYfpvdr2nxh +spxBAlg9L1a/mJjzgS0l4kRnmq0zxIMRrMchgi/a7GfwhYq2meVkNd5ectf7SdM5 +O9HIQ5cE3PcHH62mEZW2Y+A09CQ9FQoK1bxf67CbYfFcEy6htrbirmjXVoThyo1P +XoXm1+8l+n5NKWWC +-----END CERTIFICATE-----`, + // Microsoft TLS G2 RSA CA OCSP 14 + `-----BEGIN CERTIFICATE----- +MIIHuDCCBaCgAwIBAgITMwAAABJ+c5NH51vhoQAAAAAAEjANBgkqhkiG9w0BAQwF +ADBRMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u +MSIwIAYDVQQDExlNaWNyb3NvZnQgVExTIFJTQSBSb290IEcyMB4XDTI1MDgxNDIz +MDM0MVoXDTI5MDYwMzIzMDM0MVowVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1p +Y3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IFRMUyBHMiBS +U0EgQ0EgT0NTUCAxNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALQ0 +O5HV7D0M0P5XR9tDj3H/ASlro7t5dRQHJwq8g9plX9RsHSqmsqA28+gFlKjEMc5F +8cJCovAXh51G1mCU+jzzcH/UWEOIEXj5WrEVjigNT3MwnxkWE981eGAxmkFwBiDF +DsnQkRxgHGA3B8RxfsaFcMM5NSm+/EjQ3TaYXFbjn2smJMp9WbdMixVHbS3vNNyQ +0UtnWVBzBTLwrUSaT+e0qC8oUilP2MShMGJ91UZmzvLeYoUfDGHcXIWkFCqkCch4 +6S28IlWc1wagx/uzq+zt1nalPrb54BLUcX07iHXnGOtrJ5sp72g0VrQoWFefhajG +BL9+zQvF+Tzi8isM6WKTe80PC7jmTi/2ze59IkFSnDw2pD36KucFrx0WwwK923MZ +oet9r0JsO6IBBfKWS1BHMfbwsV4MJtnvQaFOdNl/TLfTlgOUFrlggPnLRsFx5hno +UEH3jnhzZcKwrENaEDyijneNs7qrqUf4lJdZe3bV1LoguppP4N0WLu5Jh1TjceLa +6pM9wsGaN4XMxdeyxQHa+W1eLBrjFKSIEUukA97x77XGd3XSRxQnq6F4Y5K98Cqn +aGDWZWZ0IptnXSS5FkK7A9qXVRjnC5waqwWISwi/wliIEJq4Y/Vf7sN3NgrvfYPg +HC39Qo5Fbs/MpwXe+FgPyjUPWpWkE7VL1GX0KucpAgMBAAGjggKBMIICfTAOBgNV +HQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFFJo9PoSVuP2 +2EKvMAtAuDkj9fcrMBMGA1UdIAQMMAowCAYGZ4EMAQICMBMGA1UdJQQMMAoGCCsG +AQUFBwMBMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMBIGA1UdEwEB/wQIMAYB +Af8CAQAwHwYDVR0jBBgwFoAU3pGGSLehMVkx8UtfB6nciHnaqHYwgasGA1UdHwSB +ozCBoDCBnaCBmqCBl4ZJaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j +cmwvTWljcm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyLmNybIZKaHR0cDov +L2NybDIubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRMUyUy +MFJTQSUyMFJvb3QlMjBHMi5jcmwwggEQBggrBgEFBQcBAQSCAQIwgf8wYwYIKwYB +BQUHMAKGV2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj +cm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyJTIwLSUyMHhzaWduLmNydDBp +BggrBgEFBQcwAoZdaHR0cDovL2NhaXNzdWVycy5taWNyb3NvZnQuY29tL3BraW9w +cy9jZXJ0cy9NaWNyb3NvZnQlMjBUTFMlMjBSU0ElMjBSb290JTIwRzIlMjAtJTIw +eHNpZ24uY3J0MC0GCCsGAQUFBzABhiFodHRwOi8vb25lb2NzcC5taWNyb3NvZnQu +Y29tL29jc3AwDQYJKoZIhvcNAQEMBQADggIBAFAy7Y42/cuUwX522YzqhW3Cks15 +m7hqbu3yszkCcAcdOZjPLxXWHp8oPm98u27+yoXreavUQ0bZlMzWsAcw7g6kCjWm +BVh78k1uKxQzlFrHznpMlsEtbgIzuatjCtP70NO2/pe64JzWNRuADvTM/RSKeEnG +WpU3U09YZzc/qEcvzfsLtqN88GX8/may9tDctPDI8Kkx8jdQYLG9bM+Gnm5b0RQH +Ja65N7W50zo16Jjy3jv1zxm+UOvjt27atgcm+EmocqAzUtws7dxdnrdaBmgqndMC +Jg1tNrQ5UxJfXhCgoVurdC/UYMSCxkPMZ0PI1D7yvmJAFzfUTDXGZw+l3V9JwEOg +u+0/a/QcEVDdXLM4cFM+KvmM6NBGFX+ktBvk8IIq8gld7IdTGohZQ9EmpBa32ZT4 +XKU6Atst09IFJYmlr/6X/FaNDeM22Kh7TSlTdjuDA8ybygSVwPjpgKFWho4gAQrX +BhGwff3pRgb2RGDS/Fw91FgLW3NePKcLC6a7u7reXhc/NIBPWoovCE+imo9p9Oem +VTHFF0qvux5MQ78kbeZrxv7x+EU5OK56+jIGpWZFfsdB5La4cwgEkVL7vYfoaRET +T85pMUZup9ZRlYcuqDSfH2r5cokDcwCKjarG8YrjKiQ9i3hLzRs2sQEG3wjf2lrb +B99kBMBp4Ylf6v3t +-----END CERTIFICATE-----`, + // Microsoft TLS G2 RSA CA OCSP 16 + `-----BEGIN CERTIFICATE----- +MIIHuDCCBaCgAwIBAgITMwAAAA5Ck48l3FGpmwAAAAAADjANBgkqhkiG9w0BAQwF +ADBRMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u +MSIwIAYDVQQDExlNaWNyb3NvZnQgVExTIFJTQSBSb290IEcyMB4XDTI1MDgwMTIw +MDMwMloXDTI5MDYwMzIwMDMwMlowVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1p +Y3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IFRMUyBHMiBS +U0EgQ0EgT0NTUCAxNjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJNy +X7D8oHoR3W/OE5vzT0QuP+ym4r+vL8gHmczj1YdNWzOn5VlHxR2Ue+hTR6PxfOQi +pbhH/gAeXr1wd5YE1XZX/IOzeVFX9CQUTXlJmrfcR5L2PKY1KtG2b18b1mC+0YKi +bzeF5WokVOeIh/A+1wBe2ufVNOMOr+HU+HdaVdRnE/dBSF9PLGB1KAGos1pwhcdY +hQbfoUwroVfZqWy6HIa6AfbQFBoF+Isx5ZXyMTfVEaKYnT/vci9REEBe4uMbQpYG +N2gF5Pq41VRdHuGU2vJRo+Q+e77DrqVBQhY9kdqQvQitSirIRRgwLlD3yqZHw+8D +z0o9fmx8sqe5RhonEpqZEkyiK1ql5aO7ocrOcu9HY7C+c0lHzsKp1US0QY3zRzfM +bAdjHNiWguQ/bnZTZJ3c+MIzrovLWxR0QC0ICE+g8gOUz4LH4jOIUKkf0sF6UCwh +xs3AYjG2/tEC5lOksVJ5lu5lWTnR26I0owa+IWrima4tKugtCDqQWojn8AGp69AE +xCFpDz3Jpn7xvzlygpCXOEy27yV+YfL/DL71ve19R3VW+PbzqOFtgzLIUV/9JpKB +38iUFDKAlq6mCd5M12QokTJaJ5JpZIRKoR68xBG7FVUd0IynFmcgR0RaZ2wYugHe +lDzagm1XcVRDbPLKvM27gBdVztl7jC2dUE/27+iHAgMBAAGjggKBMIICfTAOBgNV +HQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFAY58FbR7ZDI +NqOgD5T+YpSn5vw3MBMGA1UdIAQMMAowCAYGZ4EMAQICMBMGA1UdJQQMMAoGCCsG +AQUFBwMBMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMBIGA1UdEwEB/wQIMAYB +Af8CAQAwHwYDVR0jBBgwFoAU3pGGSLehMVkx8UtfB6nciHnaqHYwgasGA1UdHwSB +ozCBoDCBnaCBmqCBl4ZJaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j +cmwvTWljcm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyLmNybIZKaHR0cDov +L2NybDIubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRMUyUy +MFJTQSUyMFJvb3QlMjBHMi5jcmwwggEQBggrBgEFBQcBAQSCAQIwgf8wYwYIKwYB +BQUHMAKGV2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj +cm9zb2Z0JTIwVExTJTIwUlNBJTIwUm9vdCUyMEcyJTIwLSUyMHhzaWduLmNydDBp +BggrBgEFBQcwAoZdaHR0cDovL2NhaXNzdWVycy5taWNyb3NvZnQuY29tL3BraW9w +cy9jZXJ0cy9NaWNyb3NvZnQlMjBUTFMlMjBSU0ElMjBSb290JTIwRzIlMjAtJTIw +eHNpZ24uY3J0MC0GCCsGAQUFBzABhiFodHRwOi8vb25lb2NzcC5taWNyb3NvZnQu +Y29tL29jc3AwDQYJKoZIhvcNAQEMBQADggIBAIGGI1JWs93TO6gypc7n3H7V5Qim +hS8nVFE3Y3ZNdG7utJvyrxAgO1d7q52kBgwLZ1M8lcluTDmrfCIZu+vs+UyNmZ6J +h+kAJgGwmTKPCqTihbJ/h10jiSoW4JftFu5QMljZdJ14UlLrQTwwfYGxrd0QVnqz +r4S8Q/rP/2DTBQSQj/uLauKBaVKoPQL10IxIkcuIj83C0aMqPUDZWjXgy8dBEej8 +tMKgBlK3O5nN5ZkXAPkXjI1FIZRL03QD8besLM+Vb4tlcvb2k8XdQpEv0RK8bjeY +66I+Q2anOq0kQI6oiJ4c/QFEoFLVcJiCTY86hZmTSw1i4Tsnxhwy5N7UtK7SGJ3m +JAJwhdwy3lrMPgShw2yzLlbbODGYqwa7BzpDPQEtEHVdbK78Qv03TWH/w6KQGv2I +FtqjVibfJnsQEgjms0mr6hRODs4G0LIfBqDs4JC2o5AnDc/N2/CDhnVdfHbMrvbc +2fqNxx/4TQevSBliM5pN5s3nQR166CCTmavh92N49ykEb3Q+iHY6hBkI76e/Db4b +daeq7IdaXEMYURG5kj3kn70K4SY3cUCHoRNdkQQzNXB7OIW5jgG65HL9F1uSh9B7 +KmJjEVz9Kzh/Kx9y3KEmb4eRyi4tc9CtEkFY3CmW0gbpBXhwmzEGHQ6T08YoSoiR +DpR9auXiVitH82FI -----END CERTIFICATE-----`, } diff --git a/coderd/azureidentity/azureidentity_internal_test.go b/coderd/azureidentity/azureidentity_internal_test.go new file mode 100644 index 00000000000..a4b9ddcdb4d --- /dev/null +++ b/coderd/azureidentity/azureidentity_internal_test.go @@ -0,0 +1,76 @@ +package azureidentity + +import ( + "context" + "net" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestIsPrivateIP(t *testing.T) { + t.Parallel() + cases := []struct { + name string + ip string + blocked bool + }{ + {"loopback v4", "127.0.0.1", true}, + {"loopback v6", "::1", true}, + {"link local v4 (azure metadata)", "169.254.169.254", true}, + {"link local v6", "fe80::1", true}, + {"rfc1918 10/8", "10.0.0.1", true}, + {"rfc1918 172.16/12", "172.16.0.1", true}, + {"rfc1918 192.168/16", "192.168.0.1", true}, + {"ipv6 ula", "fc00::1", true}, + {"unspecified v4", "0.0.0.0", true}, + {"unspecified v6", "::", true}, + {"this-network 0.0.0.0/8", "0.1.2.3", true}, + {"cgnat 100.64/10", "100.64.0.1", true}, + {"benchmarking 198.18/15", "198.18.0.1", true}, + {"multicast v4", "224.0.0.1", true}, + {"ipv6 nat64 well-known", "64:ff9b:1::1", true}, + {"ipv6 discard-only", "100::1", true}, + {"ipv6 benchmarking", "2001:2::1", true}, + {"ipv6 documentation", "2001:db8::1", true}, + // IPv4-mapped IPv6: must canonicalize to v4 before + // classification, otherwise an attacker could bypass + // the metadata block via ::ffff:169.254.169.254. + {"ipv4-mapped metadata", "::ffff:169.254.169.254", true}, + {"ipv4-mapped rfc1918", "::ffff:10.0.0.1", true}, + + {"public v4", "8.8.8.8", false}, + {"public v6", "2606:4700:4700::1111", false}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + ip := net.ParseIP(tc.ip) + require.NotNil(t, ip, "parse %q", tc.ip) + require.Equal(t, tc.blocked, isPrivateIP(ip)) + }) + } +} + +// TestCertFetchClientRejectsLoopback proves the dialer refuses +// to connect even when the URL itself would have passed an +// allowlist (httptest.Server always binds to 127.0.0.1, so a +// successful fetch here would mean the SSRF guard had failed). +func TestCertFetchClientRejectsLoopback(t *testing.T) { + t.Parallel() + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte("should never be reached")) + })) + t.Cleanup(srv.Close) + + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, srv.URL, nil) + require.NoError(t, err) + resp, err := certFetchClient.Do(req) + if resp != nil { + defer resp.Body.Close() + } + require.Error(t, err) + require.Contains(t, err.Error(), "private IP") +} diff --git a/coderd/azureidentity/azureidentity_test.go b/coderd/azureidentity/azureidentity_test.go index bd94f836beb..c10bbfbe803 100644 --- a/coderd/azureidentity/azureidentity_test.go +++ b/coderd/azureidentity/azureidentity_test.go @@ -1,13 +1,19 @@ package azureidentity_test import ( + "bytes" "context" + "crypto/rand" + "crypto/rsa" "crypto/x509" - "encoding/pem" + "crypto/x509/pkix" + "encoding/base64" + "math/big" "runtime" "testing" "time" + "github.com/smallstep/pkcs7" "github.com/stretchr/testify/require" "github.com/coder/coder/v2/coderd/azureidentity" @@ -15,6 +21,7 @@ import ( func TestValidate(t *testing.T) { t.Parallel() + t.Skip("See https://github.com/coder/internal/issues/1602") if runtime.GOOS == "darwin" { // This test fails on MacOS for some reason. See https://github.com/coder/coder/issues/12978 t.Skip() @@ -50,10 +57,8 @@ func TestValidate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() vm, err := azureidentity.Validate(context.Background(), tc.payload, azureidentity.Options{ - VerifyOptions: x509.VerifyOptions{ - CurrentTime: tc.date, - }, - Offline: true, + CurrentTime: tc.date, + Offline: true, }) require.NoError(t, err) require.Equal(t, tc.vmID, vm) @@ -69,12 +74,10 @@ func TestExpiresSoon(t *testing.T) { t.Skip() const threshold = 1 - for _, c := range azureidentity.Certificates { - block, rest := pem.Decode([]byte(c)) - require.Zero(t, len(rest)) - cert, err := x509.ParseCertificate(block.Bytes) - require.NoError(t, err) + certs, err := azureidentity.ParseCertificates() + require.NoError(t, err) + for _, cert := range certs { expiresSoon := cert.NotAfter.Before(time.Now().AddDate(0, threshold, 0)) if expiresSoon { t.Errorf("certificate expires within %d months %s: %s", threshold, cert.NotAfter, cert.Subject.CommonName) @@ -87,3 +90,206 @@ func TestExpiresSoon(t *testing.T) { } } } + +func TestIsAllowedCertificateURL(t *testing.T) { + t.Parallel() + tests := []struct { + name string + url string + allowed bool + }{ + {"microsoft http", "http://www.microsoft.com/pki/mscorp/cert.crt", true}, + {"microsoft https", "https://www.microsoft.com/pkiops/certs/cert.crt", true}, + {"digicert http", "http://cacerts.digicert.com/DigiCertGlobalRootG2.crt", true}, + {"digicert https", "https://cacerts.digicert.com/DigiCertGlobalRootG3.crt", true}, + {"evil domain", "http://evil.example.com/cert.crt", false}, + {"metadata endpoint", "http://169.254.169.254/latest/meta-data/", false}, + {"localhost", "http://localhost/secret", false}, + {"subdomain trick", "http://www.microsoft.com.evil.com/cert.crt", false}, + {"empty string", "", false}, + {"ftp scheme", "ftp://www.microsoft.com/cert.crt", false}, + {"no scheme", "www.microsoft.com/cert.crt", false}, + {"javascript scheme", "javascript:alert(1)", false}, + {"microsoft with path", "http://www.microsoft.com/pkiops/certs/cert.crt", true}, + {"microsoft explicit port 80", "http://www.microsoft.com:80/cert.crt", true}, + {"microsoft explicit port 443", "https://www.microsoft.com:443/cert.crt", true}, + {"microsoft non-standard port", "http://www.microsoft.com:8080/cert.crt", false}, + {"microsoft port 22", "http://www.microsoft.com:22/cert.crt", false}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + result := azureidentity.IsAllowedCertificateURL(tc.url) + require.Equal(t, tc.allowed, result, "URL: %s", tc.url) + }) + } +} + +// testCertChain holds a three-level certificate hierarchy (Root CA, +// Intermediate CA, Signing/leaf) together with their private keys. +type testCertChain struct { + RootCert *x509.Certificate + RootKey *rsa.PrivateKey + IntermediateCert *x509.Certificate + IntermediateKey *rsa.PrivateKey + SigningCert *x509.Certificate + SigningKey *rsa.PrivateKey +} + +// newTestCertChain creates a fresh three-level certificate chain for +// testing. All certificates are valid at time.Now(). +func newTestCertChain(t *testing.T) testCertChain { + t.Helper() + + // Smaller key sizes are fine for tests; keeps them fast. + const keyBits = 2048 + + // ---- Root CA ---- + rootKey, err := rsa.GenerateKey(rand.Reader, keyBits) + require.NoError(t, err) + rootTmpl := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{CommonName: "Test Root CA"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(24 * time.Hour), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + BasicConstraintsValid: true, + IsCA: true, + } + rootDER, err := x509.CreateCertificate(rand.Reader, rootTmpl, rootTmpl, &rootKey.PublicKey, rootKey) + require.NoError(t, err) + rootCert, err := x509.ParseCertificate(rootDER) + require.NoError(t, err) + + // ---- Intermediate CA ---- + intermediateKey, err := rsa.GenerateKey(rand.Reader, keyBits) + require.NoError(t, err) + intermediateTmpl := &x509.Certificate{ + SerialNumber: big.NewInt(2), + Subject: pkix.Name{CommonName: "Test Intermediate CA"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(24 * time.Hour), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + BasicConstraintsValid: true, + IsCA: true, + } + intermediateDER, err := x509.CreateCertificate(rand.Reader, intermediateTmpl, rootCert, &intermediateKey.PublicKey, rootKey) + require.NoError(t, err) + intermediateCert, err := x509.ParseCertificate(intermediateDER) + require.NoError(t, err) + + // ---- Signing (leaf) certificate ---- + signingKey, err := rsa.GenerateKey(rand.Reader, keyBits) + require.NoError(t, err) + signingTmpl := &x509.Certificate{ + SerialNumber: big.NewInt(3), + Subject: pkix.Name{CommonName: "metadata.azure.com"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(24 * time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + } + signingDER, err := x509.CreateCertificate(rand.Reader, signingTmpl, intermediateCert, &signingKey.PublicKey, intermediateKey) + require.NoError(t, err) + signingCert, err := x509.ParseCertificate(signingDER) + require.NoError(t, err) + + return testCertChain{ + RootCert: rootCert, + RootKey: rootKey, + IntermediateCert: intermediateCert, + IntermediateKey: intermediateKey, + SigningCert: signingCert, + SigningKey: signingKey, + } +} + +// createSignedPKCS7 produces a base64-encoded PKCS7 SignedData +// envelope over content, signed by the chain's leaf certificate. +func (tc *testCertChain) createSignedPKCS7(t *testing.T, content []byte) string { + t.Helper() + + sd, err := pkcs7.NewSignedData(content) + require.NoError(t, err) + err = sd.AddSignerChain(tc.SigningCert, tc.SigningKey, []*x509.Certificate{tc.IntermediateCert}, pkcs7.SignerInfoConfig{}) + require.NoError(t, err) + der, err := sd.Finish() + require.NoError(t, err) + return base64.StdEncoding.EncodeToString(der) +} + +// validationOptions returns azureidentity.Options that trust only this +// chain's Root CA. +func (tc *testCertChain) validationOptions() azureidentity.Options { + roots := x509.NewCertPool() + roots.AddCert(tc.RootCert) + return azureidentity.Options{ + Roots: roots, + Intermediates: []*x509.Certificate{tc.IntermediateCert}, + Offline: true, + } +} + +func TestValidate_TamperedContent(t *testing.T) { + t.Parallel() + if runtime.GOOS == "darwin" { + t.Skip("pkcs7 signing uses SHA1 which may be restricted on macOS") + } + + chain := newTestCertChain(t) + + // Build a valid PKCS7 envelope. + original := []byte(`{"vmId":"tamper-test-vm"}`) + signed := chain.createSignedPKCS7(t, original) + + // Decode, tamper with the content, re-encode. + raw, err := base64.StdEncoding.DecodeString(signed) + require.NoError(t, err) + tampered := bytes.Replace(raw, []byte("tamper-test-vm"), []byte("tampered!!!!!!"), 1) + require.NotEqual(t, raw, tampered, "payload should have changed") + tamperedB64 := base64.StdEncoding.EncodeToString(tampered) + + opts := chain.validationOptions() + _, err = azureidentity.Validate(context.Background(), tamperedB64, opts) + require.Error(t, err, "tampered content must not pass validation") +} + +func TestValidate_UntrustedCertWithValidSignature(t *testing.T) { + t.Parallel() + if runtime.GOOS == "darwin" { + t.Skip("pkcs7 signing uses SHA1 which may be restricted on macOS") + } + + chain := newTestCertChain(t) + + content := []byte(`{"vmId":"untrusted-test-vm"}`) + signed := chain.createSignedPKCS7(t, content) + + // Build options that trust a DIFFERENT root, so the chain + // should not verify. + otherRoot, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + otherRootTmpl := &x509.Certificate{ + SerialNumber: big.NewInt(99), + Subject: pkix.Name{CommonName: "Other Root CA"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(24 * time.Hour), + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + BasicConstraintsValid: true, + IsCA: true, + } + otherRootDER, err := x509.CreateCertificate(rand.Reader, otherRootTmpl, otherRootTmpl, &otherRoot.PublicKey, otherRoot) + require.NoError(t, err) + otherRootCert, err := x509.ParseCertificate(otherRootDER) + require.NoError(t, err) + + untrustedRoots := x509.NewCertPool() + untrustedRoots.AddCert(otherRootCert) + opts := azureidentity.Options{ + Roots: untrustedRoots, + Intermediates: []*x509.Certificate{chain.IntermediateCert}, + Offline: true, + } + + _, err = azureidentity.Validate(context.Background(), signed, opts) + require.Error(t, err, "signature from untrusted CA must not pass validation") +} diff --git a/coderd/azureidentity/generate.sh b/coderd/azureidentity/generate.sh index 8d8973259a4..df64d2bf878 100755 --- a/coderd/azureidentity/generate.sh +++ b/coderd/azureidentity/generate.sh @@ -12,6 +12,18 @@ declare -a CERTIFICATES=( "Microsoft Azure TLS Issuing CA 02=https://www.microsoft.com/pki/certs/Microsoft%20Azure%20TLS%20Issuing%20CA%2002.cer" "Microsoft Azure TLS Issuing CA 05=https://www.microsoft.com/pki/certs/Microsoft%20Azure%20TLS%20Issuing%20CA%2005.cer" "Microsoft Azure TLS Issuing CA 06=https://www.microsoft.com/pki/certs/Microsoft%20Azure%20TLS%20Issuing%20CA%2006.cer" + + # Azure IMDS G2 attested data chains can use the cross-signed + # Microsoft TLS RSA Root G2 to sign these OCSP intermediates. + "Microsoft TLS RSA Root G2=https://www.microsoft.com/pkiops/certs/Microsoft%20TLS%20RSA%20Root%20G2%20-%20xsign.crt" + "Microsoft TLS G2 RSA CA OCSP 02=https://www.microsoft.com/pkiops/certs/Microsoft%20TLS%20G2%20RSA%20CA%20OCSP%2002.crt" + "Microsoft TLS G2 RSA CA OCSP 04=https://www.microsoft.com/pkiops/certs/Microsoft%20TLS%20G2%20RSA%20CA%20OCSP%2004.crt" + "Microsoft TLS G2 RSA CA OCSP 06=https://www.microsoft.com/pkiops/certs/Microsoft%20TLS%20G2%20RSA%20CA%20OCSP%2006.crt" + "Microsoft TLS G2 RSA CA OCSP 08=https://www.microsoft.com/pkiops/certs/Microsoft%20TLS%20G2%20RSA%20CA%20OCSP%2008.crt" + "Microsoft TLS G2 RSA CA OCSP 10=https://www.microsoft.com/pkiops/certs/Microsoft%20TLS%20G2%20RSA%20CA%20OCSP%2010.crt" + "Microsoft TLS G2 RSA CA OCSP 12=https://www.microsoft.com/pkiops/certs/Microsoft%20TLS%20G2%20RSA%20CA%20OCSP%2012.crt" + "Microsoft TLS G2 RSA CA OCSP 14=https://www.microsoft.com/pkiops/certs/Microsoft%20TLS%20G2%20RSA%20CA%20OCSP%2014.crt" + "Microsoft TLS G2 RSA CA OCSP 16=https://www.microsoft.com/pkiops/certs/Microsoft%20TLS%20G2%20RSA%20CA%20OCSP%2016.crt" ) CONTENT="var Certificates = []string{" diff --git a/coderd/coderd.go b/coderd/coderd.go index 641fd4e7f31..a940d1b3fa9 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -3,7 +3,6 @@ package coderd import ( "context" "crypto/tls" - "crypto/x509" "database/sql" "errors" "expvar" @@ -67,6 +66,7 @@ import ( "github.com/coder/coder/v2/coderd/appearance" "github.com/coder/coder/v2/coderd/audit" "github.com/coder/coder/v2/coderd/awsidentity" + "github.com/coder/coder/v2/coderd/azureidentity" "github.com/coder/coder/v2/coderd/connectionlog" "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbauthz" @@ -168,7 +168,7 @@ type Options struct { AgentInactiveDisconnectTimeout time.Duration AWSCertificates awsidentity.Certificates Authorizer rbac.Authorizer - AzureCertificates x509.VerifyOptions + AzureCertificates azureidentity.Options GoogleTokenValidator *idtoken.Validator GithubOAuth2Config *GithubOAuth2Config OIDCConfig *OIDCConfig @@ -868,7 +868,9 @@ func New(options *Options) *API { tracing.Middleware(api.TracerProvider), httpmw.AttachRequestID, httpmw.ExtractRealIP(api.RealIPConfig), - loggermw.Logger(api.Logger), + loggermw.Logger(api.Logger, func(r *http.Request) string { + return httpmw.EffectiveHost(api.RealIPConfig, r) + }), singleSlashMW, rolestore.CustomRoleMW, prometheusMW, @@ -1336,6 +1338,8 @@ func New(options *Options) *API { }) r.Get("/appearance", api.userAppearanceSettings) r.Put("/appearance", api.putUserAppearanceSettings) + r.Get("/preferences", api.userPreferenceSettings) + r.Put("/preferences", api.putUserPreferenceSettings) r.Route("/password", func(r chi.Router) { r.Use(httpmw.RateLimit(options.LoginRateLimit, time.Minute)) r.Put("/", api.putUserPassword) @@ -1499,6 +1503,7 @@ func New(options *Options) *API { r.Get("/parameters", api.workspaceBuildParameters) r.Get("/resources", api.workspaceBuildResourcesDeprecated) r.Get("/state", api.workspaceBuildState) + r.Put("/state", api.workspaceBuildUpdateState) r.Get("/timings", api.workspaceBuildTimings) }) r.Route("/authcheck", func(r chi.Router) { @@ -1714,29 +1719,50 @@ func New(options *Options) *API { // Add CSP headers to all static assets and pages. CSP headers only affect // browsers, so these don't make sense on api routes. - cspMW := httpmw.CSPHeaders( - options.Telemetry.Enabled(), func() []*proxyhealth.ProxyHost { - if api.DeploymentValues.Dangerous.AllowAllCors { - // In this mode, allow all external requests. - return []*proxyhealth.ProxyHost{ - { - Host: "*", - AppHost: "*", - }, - } - } - // Always add the primary, since the app host may be on a sub-domain. - proxies := []*proxyhealth.ProxyHost{ + cspProxyHosts := func() []*proxyhealth.ProxyHost { + if api.DeploymentValues.Dangerous.AllowAllCors { + // In this mode, allow all external requests. + return []*proxyhealth.ProxyHost{ { - Host: api.AccessURL.Host, - AppHost: appurl.ConvertAppHostForCSP(api.AccessURL.Host, api.AppHostname), + Host: "*", + AppHost: "*", }, } - if f := api.WorkspaceProxyHostsFn.Load(); f != nil { - proxies = append(proxies, (*f)()...) - } - return proxies - }, additionalCSPHeaders) + } + // Always add the primary, since the app host may be on a sub-domain. + proxies := []*proxyhealth.ProxyHost{ + { + Host: api.AccessURL.Host, + AppHost: appurl.ConvertAppHostForCSP(api.AccessURL.Host, api.AppHostname), + }, + } + if f := api.WorkspaceProxyHostsFn.Load(); f != nil { + proxies = append(proxies, (*f)()...) + } + return proxies + } + cspMW := httpmw.CSPHeaders(options.Telemetry.Enabled(), cspProxyHosts, additionalCSPHeaders) + + // Embed routes (e.g. VS Code extension chat) are designed to be + // loaded inside iframes, so they must not include frame-ancestors + // in their CSP. The CSP wildcard '*' only matches network schemes + // (http, https, ws, wss) and cannot cover custom schemes like + // vscode-webview://, so the only way to allow all embedders is + // to omit the directive entirely. If the operator explicitly + // configured frame-ancestors via CODER_ADDITIONAL_CSP_POLICY, + // respect that setting. + + embedCSPHeaders := make(map[httpmw.CSPFetchDirective][]string, len(additionalCSPHeaders)) + for k, v := range additionalCSPHeaders { + embedCSPHeaders[k] = v + } + if _, ok := additionalCSPHeaders[httpmw.CSPFrameAncestors]; !ok { + embedCSPHeaders[httpmw.CSPFrameAncestors] = []string{} + } + embedCSPMW := httpmw.CSPHeaders(options.Telemetry.Enabled(), cspProxyHosts, embedCSPHeaders) + embedHandler := embedCSPMW(compressHandler(httpmw.HSTS(api.SiteHandler, options.StrictTransportSecurityCfg))) + r.Get("/agents/{agentId}/embed", embedHandler.ServeHTTP) + r.Get("/agents/{agentId}/embed/*", embedHandler.ServeHTTP) // Static file handler must be wrapped with HSTS handler if the // StrictTransportSecurityAge is set. We only need to set this header on @@ -2033,7 +2059,7 @@ func (api *API) CreateInMemoryTaggedProvisionerDaemon(dialCtx context.Context, n if err != nil { return nil, err } - server := drpcserver.NewWithOptions(&tracing.DRPCHandler{Handler: mux}, + server := drpcsdk.NewServer(logger, &tracing.DRPCHandler{Handler: mux}, drpcserver.Options{ Manager: drpcsdk.DefaultDRPCOptions(nil), Log: func(err error) { diff --git a/coderd/coderd_test.go b/coderd/coderd_test.go index c9446281499..e2eefd222e8 100644 --- a/coderd/coderd_test.go +++ b/coderd/coderd_test.go @@ -385,9 +385,9 @@ func TestCSRFExempt(t *testing.T) { data, _ := io.ReadAll(resp.Body) _ = resp.Body.Close() - // A StatusBadGateway means Coderd tried to proxy to the agent and failed because the agent + // A StatusNotFound means Coderd tried to proxy to the agent and failed because the agent // was not there. This means CSRF did not block the app request, which is what we want. - require.Equal(t, http.StatusBadGateway, resp.StatusCode, "status code 500 is CSRF failure") + require.Equal(t, http.StatusNotFound, resp.StatusCode, "status code 500 is CSRF failure") require.NotContains(t, string(data), "CSRF") }) } diff --git a/coderd/coderdtest/authorize.go b/coderd/coderdtest/authorize.go index f53ef3fa3be..42146f94098 100644 --- a/coderd/coderdtest/authorize.go +++ b/coderd/coderdtest/authorize.go @@ -11,7 +11,6 @@ import ( "testing" "github.com/google/uuid" - "github.com/moby/moby/pkg/namesgenerator" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/xerrors" @@ -22,6 +21,7 @@ import ( "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/rbac/policy" "github.com/coder/coder/v2/coderd/rbac/regosql" + "github.com/coder/coder/v2/coderd/util/namesgenerator" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/cryptorand" ) @@ -439,10 +439,10 @@ func RandomRBACObject() rbac.Object { OrgID: uuid.NewString(), Type: randomRBACType(), ACLUserList: map[string][]policy.Action{ - namesgenerator.GetRandomName(1): {RandomRBACAction()}, + namesgenerator.UniqueName(): {RandomRBACAction()}, }, ACLGroupList: map[string][]policy.Action{ - namesgenerator.GetRandomName(1): {RandomRBACAction()}, + namesgenerator.UniqueName(): {RandomRBACAction()}, }, } } @@ -471,7 +471,7 @@ func RandomRBACSubject() rbac.Subject { return rbac.Subject{ ID: uuid.NewString(), Roles: rbac.RoleIdentifiers{rbac.RoleMember()}, - Groups: []string{namesgenerator.GetRandomName(1)}, + Groups: []string{namesgenerator.UniqueName()}, Scope: rbac.ScopeAll, } } diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index ac362295f0e..a3ce3a413b7 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -30,17 +30,17 @@ import ( "sync/atomic" "testing" "time" - "unicode" "cloud.google.com/go/compute/metadata" - "github.com/fullsailor/pkcs7" "github.com/go-chi/chi/v5" "github.com/golang-jwt/jwt/v4" "github.com/google/uuid" - "github.com/moby/moby/pkg/namesgenerator" "github.com/prometheus/client_golang/prometheus" + "github.com/smallstep/pkcs7" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/text/cases" + "golang.org/x/text/language" "golang.org/x/xerrors" "google.golang.org/api/idtoken" "google.golang.org/api/option" @@ -63,6 +63,7 @@ import ( "github.com/coder/coder/v2/coderd/audit" "github.com/coder/coder/v2/coderd/autobuild" "github.com/coder/coder/v2/coderd/awsidentity" + "github.com/coder/coder/v2/coderd/azureidentity" "github.com/coder/coder/v2/coderd/connectionlog" "github.com/coder/coder/v2/coderd/cryptokeys" "github.com/coder/coder/v2/coderd/database" @@ -83,6 +84,8 @@ import ( "github.com/coder/coder/v2/coderd/schedule" "github.com/coder/coder/v2/coderd/telemetry" "github.com/coder/coder/v2/coderd/updatecheck" + "github.com/coder/coder/v2/coderd/usage" + "github.com/coder/coder/v2/coderd/util/namesgenerator" "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/coderd/webpush" "github.com/coder/coder/v2/coderd/workspaceapps" @@ -102,6 +105,8 @@ import ( "github.com/coder/coder/v2/testutil" ) +const DefaultDERPMeshKey = "test-key" + const defaultTestDaemonName = "test-daemon" type Options struct { @@ -112,7 +117,7 @@ type Options struct { AppHostname string AWSCertificates awsidentity.Certificates Authorizer rbac.Authorizer - AzureCertificates x509.VerifyOptions + AzureCertificates azureidentity.Options GithubOAuth2Config *coderd.GithubOAuth2Config RealIPConfig *httpmw.RealIPConfig OIDCConfig *coderd.OIDCConfig @@ -186,6 +191,7 @@ type Options struct { TelemetryReporter telemetry.Reporter ProvisionerdServerMetrics *provisionerdserver.Metrics + UsageInserter usage.Inserter } // New constructs a codersdk client connected to an in-memory API instance. @@ -266,6 +272,11 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can } } + var usageInserter *atomic.Pointer[usage.Inserter] + if options.UsageInserter != nil { + usageInserter = &atomic.Pointer[usage.Inserter]{} + usageInserter.Store(&options.UsageInserter) + } if options.Database == nil { options.Database, options.Pubsub = dbtestutil.NewDB(t) } @@ -499,8 +510,18 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can stunAddresses = options.DeploymentValues.DERP.Server.STUNAddresses.Value() } - derpServer := derp.NewServer(key.NewNode(), tailnet.Logger(options.Logger.Named("derp").Leveled(slog.LevelDebug))) - derpServer.SetMeshKey("test-key") + const derpMeshKey = "test-key" + // Technically AGPL coderd servers don't set this value, but it doesn't + // change any behavior. It's useful for enterprise tests. + err = options.Database.InsertDERPMeshKey(dbauthz.AsSystemRestricted(ctx), derpMeshKey) //nolint:gocritic // test + if !database.IsUniqueViolation(err, database.UniqueSiteConfigsKeyKey) { + require.NoError(t, err, "insert DERP mesh key") + } + var derpServer *derp.Server + if options.DeploymentValues.DERP.Server.Enable.Value() { + derpServer = derp.NewServer(key.NewNode(), tailnet.Logger(options.Logger.Named("derp").Leveled(slog.LevelDebug))) + derpServer.SetMeshKey(derpMeshKey) + } // match default with cli default if options.SSHKeygenAlgorithm == "" { @@ -559,6 +580,7 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can Database: options.Database, Pubsub: options.Pubsub, ExternalAuthConfigs: options.ExternalAuthConfigs, + UsageInserter: usageInserter, Auditor: options.Auditor, ConnectionLogger: options.ConnectionLogger, @@ -793,7 +815,7 @@ func AuthzUserSubject(user codersdk.User, orgID uuid.UUID) rbac.Subject { func createAnotherUserRetry(t testing.TB, client *codersdk.Client, organizationIDs []uuid.UUID, retries int, roles []rbac.RoleIdentifier, mutators ...func(r *codersdk.CreateUserRequestWithOrgs)) (*codersdk.Client, codersdk.User) { req := codersdk.CreateUserRequestWithOrgs{ - Email: namesgenerator.GetRandomName(10) + "@coder.com", + Email: namesgenerator.UniqueName() + "@coder.com", Username: RandomUsername(t), Name: RandomName(t), Password: "SomeSecurePassword!", @@ -1378,10 +1400,11 @@ func RequestExternalAuthCallback(t testing.TB, providerID string, client *coders // NewGoogleInstanceIdentity returns a metadata client and ID token validator for faking // instance authentication for Google Cloud. // nolint:revive -func NewGoogleInstanceIdentity(t testing.TB, instanceID string, expired bool) (*idtoken.Validator, *metadata.Client) { +func NewGoogleInstanceIdentity(t testing.TB, instanceID string, expired bool, audience string) (*idtoken.Validator, *metadata.Client) { keyID, err := cryptorand.String(12) require.NoError(t, err) claims := jwt.MapClaims{ + "aud": audience, "google": map[string]interface{}{ "compute_engine": map[string]string{ "instance_id": instanceID, @@ -1498,27 +1521,63 @@ func NewAWSInstanceIdentity(t testing.TB, instanceID string) (awsidentity.Certif } } -// NewAzureInstanceIdentity returns a metadata client and ID token validator for faking -// instance authentication for Azure. -func NewAzureInstanceIdentity(t testing.TB, instanceID string) (x509.VerifyOptions, *http.Client) { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) +// NewAzureInstanceIdentity returns a metadata client and ID token +// validator for faking instance authentication for Azure. It builds +// a realistic 3-level certificate chain (Root CA -> Intermediate -> +// Signing Cert) to match the real Azure trust hierarchy. +func NewAzureInstanceIdentity(t testing.TB, instanceID string) (azureidentity.Options, *http.Client) { + // Root CA (self-signed, trusted). + rootKey, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + rootTmpl := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{CommonName: "Test Root CA"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().AddDate(10, 0, 0), + IsCA: true, + BasicConstraintsValid: true, + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + } + rootDER, err := x509.CreateCertificate(rand.Reader, rootTmpl, rootTmpl, &rootKey.PublicKey, rootKey) + require.NoError(t, err) + rootCert, err := x509.ParseCertificate(rootDER) require.NoError(t, err) - rawCertificate, err := x509.CreateCertificate(rand.Reader, &x509.Certificate{ - SerialNumber: big.NewInt(2022), - NotAfter: time.Now().AddDate(1, 0, 0), - Subject: pkix.Name{ - CommonName: "metadata.azure.com", - }, - }, &x509.Certificate{}, &privateKey.PublicKey, privateKey) + // Intermediate CA (signed by root). + interKey, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + interTmpl := &x509.Certificate{ + SerialNumber: big.NewInt(2), + Subject: pkix.Name{CommonName: "Test Intermediate CA"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().AddDate(5, 0, 0), + IsCA: true, + BasicConstraintsValid: true, + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + } + interDER, err := x509.CreateCertificate(rand.Reader, interTmpl, rootCert, &interKey.PublicKey, rootKey) + require.NoError(t, err) + interCert, err := x509.ParseCertificate(interDER) require.NoError(t, err) - certificate, err := x509.ParseCertificate(rawCertificate) + // Signing cert (leaf, signed by intermediate). + signKey, err := rsa.GenerateKey(rand.Reader, 2048) + require.NoError(t, err) + signTmpl := &x509.Certificate{ + SerialNumber: big.NewInt(3), + Subject: pkix.Name{CommonName: "metadata.azure.com"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().AddDate(1, 0, 0), + } + signDER, err := x509.CreateCertificate(rand.Reader, signTmpl, interCert, &signKey.PublicKey, interKey) + require.NoError(t, err) + signCert, err := x509.ParseCertificate(signDER) require.NoError(t, err) + // Build PKCS7 signed data with only the signing cert. signed, err := pkcs7.NewSignedData([]byte(`{"vmId":"` + instanceID + `"}`)) require.NoError(t, err) - err = signed.AddSigner(certificate, privateKey, pkcs7.SignerInfoConfig{}) + err = signed.AddSigner(signCert, signKey, pkcs7.SignerInfoConfig{}) require.NoError(t, err) signatureRaw, err := signed.Finish() require.NoError(t, err) @@ -1531,12 +1590,12 @@ func NewAzureInstanceIdentity(t testing.TB, instanceID string) (x509.VerifyOptio }) require.NoError(t, err) - certPool := x509.NewCertPool() - certPool.AddCert(certificate) + roots := x509.NewCertPool() + roots.AddCert(rootCert) - return x509.VerifyOptions{ - Intermediates: certPool, - Roots: certPool, + return azureidentity.Options{ + Roots: roots, + Intermediates: []*x509.Certificate{interCert}, }, &http.Client{ Transport: roundTripper(func(r *http.Request) (*http.Response, error) { // Only handle metadata server requests. @@ -1557,37 +1616,15 @@ func NewAzureInstanceIdentity(t testing.TB, instanceID string) (x509.VerifyOptio } } -func RandomUsername(t testing.TB) string { - suffix, err := cryptorand.String(3) - require.NoError(t, err) - suffix = "-" + suffix - n := strings.ReplaceAll(namesgenerator.GetRandomName(10), "_", "-") + suffix - if len(n) > 32 { - n = n[:32-len(suffix)] + suffix - } - return n +func RandomUsername(_ testing.TB) string { + return namesgenerator.UniqueNameWith("-") } -func RandomName(t testing.TB) string { - var sb strings.Builder - var err error - ss := strings.Split(namesgenerator.GetRandomName(10), "_") - for si, s := range ss { - for ri, r := range s { - if ri == 0 { - _, err = sb.WriteRune(unicode.ToTitle(r)) - require.NoError(t, err) - } else { - _, err = sb.WriteRune(r) - require.NoError(t, err) - } - } - if si < len(ss)-1 { - _, err = sb.WriteRune(' ') - require.NoError(t, err) - } - } - return sb.String() +// RandomName returns a random name in title case (e.g. "Happy Einstein"). +func RandomName(_ testing.TB) string { + return cases.Title(language.English).String( + namesgenerator.NameWith(" "), + ) } // Used to easily create an HTTP transport! diff --git a/coderd/coderdtest/coderdtest_test.go b/coderd/coderdtest/coderdtest_test.go index 8bd4898fe2f..aa776b84fea 100644 --- a/coderd/coderdtest/coderdtest_test.go +++ b/coderd/coderdtest/coderdtest_test.go @@ -1,8 +1,11 @@ package coderdtest_test import ( + "strings" "testing" + "unicode" + "github.com/stretchr/testify/require" "go.uber.org/goleak" "github.com/coder/coder/v2/coderd/coderdtest" @@ -25,6 +28,25 @@ func TestNew(t *testing.T) { workspace := coderdtest.CreateWorkspace(t, client, template.ID) coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID) coderdtest.AwaitWorkspaceAgents(t, client, workspace.ID) - _, _ = coderdtest.NewGoogleInstanceIdentity(t, "example", false) + _, _ = coderdtest.NewGoogleInstanceIdentity(t, "example", false, "coder") _, _ = coderdtest.NewAWSInstanceIdentity(t, "an-instance") } + +func TestRandomName(t *testing.T) { + t.Parallel() + + for range 10 { + name := coderdtest.RandomName(t) + + require.NotEmpty(t, name, "name should not be empty") + require.NotContains(t, name, "_", "name should not contain underscores") + + // Should be title cased (e.g., "Happy Einstein"). + words := strings.Split(name, " ") + require.Len(t, words, 2, "name should have exactly two words") + for _, word := range words { + firstRune := []rune(word)[0] + require.True(t, unicode.IsUpper(firstRune), "word %q should start with uppercase letter", word) + } + } +} diff --git a/coderd/coderdtest/oidctest/idp.go b/coderd/coderdtest/oidctest/idp.go index d5215b9964a..99794aa3dec 100644 --- a/coderd/coderdtest/oidctest/idp.go +++ b/coderd/coderdtest/oidctest/idp.go @@ -213,8 +213,9 @@ type FakeIDP struct { hookAuthenticateClient func(t testing.TB, req *http.Request) (url.Values, error) serve bool // optional middlewares - middlewares chi.Middlewares - defaultExpire time.Duration + middlewares chi.Middlewares + defaultExpire time.Duration + omitEmailVerifiedDefault bool } func StatusError(code int, err error) error { @@ -369,6 +370,15 @@ func WithIssuer(issuer string) func(*FakeIDP) { } } +// WithOmitEmailVerifiedDefault suppresses the default email_verified=true +// injection in encodeClaims. Use this for tests that exercise the handler's +// absent-claim rejection path. +func WithOmitEmailVerifiedDefault() func(*FakeIDP) { + return func(f *FakeIDP) { + f.omitEmailVerifiedDefault = true + } +} + type With429Arguments struct { AllPaths bool TokenPath bool @@ -882,6 +892,17 @@ func (f *FakeIDP) encodeClaims(t testing.TB, claims jwt.MapClaims) string { claims["iss"] = f.locked.Issuer() } + // Default email_verified to true so that tests that do not care + // about the email_verified flow are not forced to set it. + // Tests that need a different value can set it explicitly. + // Use WithOmitEmailVerifiedDefault() to suppress this default + // for tests that need to exercise the absent-claim path. + if !f.omitEmailVerifiedDefault { + if _, ok := claims["email_verified"]; !ok { + claims["email_verified"] = true + } + } + signed, err := jwt.NewWithClaims(jwt.SigningMethodRS256, claims).SignedString(f.locked.PrivateKey()) require.NoError(t, err) diff --git a/coderd/coderdtest/usage.go b/coderd/coderdtest/usage.go new file mode 100644 index 00000000000..4da724b1779 --- /dev/null +++ b/coderd/coderdtest/usage.go @@ -0,0 +1,44 @@ +package coderdtest + +import ( + "context" + "sync" + + "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/usage" + "github.com/coder/coder/v2/coderd/usage/usagetypes" +) + +var _ usage.Inserter = (*UsageInserter)(nil) + +type UsageInserter struct { + sync.Mutex + events []usagetypes.DiscreteEvent +} + +func NewUsageInserter() *UsageInserter { + return &UsageInserter{ + events: []usagetypes.DiscreteEvent{}, + } +} + +func (u *UsageInserter) InsertDiscreteUsageEvent(_ context.Context, _ database.Store, event usagetypes.DiscreteEvent) error { + u.Lock() + defer u.Unlock() + u.events = append(u.events, event) + return nil +} + +func (u *UsageInserter) GetEvents() []usagetypes.DiscreteEvent { + u.Lock() + defer u.Unlock() + eventsCopy := make([]usagetypes.DiscreteEvent, len(u.events)) + copy(eventsCopy, u.events) + return eventsCopy +} + +func (u *UsageInserter) Reset() { + u.Lock() + defer u.Unlock() + u.events = []usagetypes.DiscreteEvent{} +} diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 2c29b90431d..a6554cd3c7b 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -399,6 +399,11 @@ var ( User: []rbac.Permission{}, ByOrgID: map[string]rbac.OrgPermissions{ orgID.String(): { + Org: rbac.Permissions(map[string][]policy.Action{ + // SubAgentAPI needs to check metadata of templates + // potentially shared via group_acl. + rbac.ResourceTemplate.Type: {policy.ActionRead}, + }), Member: rbac.Permissions(map[string][]policy.Action{ rbac.ResourceWorkspace.Type: {policy.ActionRead, policy.ActionUpdate, policy.ActionCreateAgent, policy.ActionDeleteAgent}, }), @@ -1536,6 +1541,14 @@ func (q *querier) CountInProgressPrebuilds(ctx context.Context) ([]database.Coun return q.db.CountInProgressPrebuilds(ctx) } +func (q *querier) CountOIDCLinkedIDsByIssuer(ctx context.Context) ([]database.CountOIDCLinkedIDsByIssuerRow, error) { + // Requires the ability to read all user's personal data. + if err := q.authorizeContext(ctx, policy.ActionReadPersonal, rbac.ResourceUser); err != nil { + return nil, err + } + return q.db.CountOIDCLinkedIDsByIssuer(ctx) +} + func (q *querier) CountPendingNonActivePrebuilds(ctx context.Context) ([]database.CountPendingNonActivePrebuildsRow, error) { if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceWorkspace.All()); err != nil { return nil, err @@ -3431,6 +3444,17 @@ func (q *querier) GetUserStatusCounts(ctx context.Context, arg database.GetUserS return q.db.GetUserStatusCounts(ctx, arg) } +func (q *querier) GetUserTaskNotificationAlertDismissed(ctx context.Context, userID uuid.UUID) (bool, error) { + user, err := q.db.GetUserByID(ctx, userID) + if err != nil { + return false, err + } + if err := q.authorizeContext(ctx, policy.ActionReadPersonal, user); err != nil { + return false, err + } + return q.db.GetUserTaskNotificationAlertDismissed(ctx, userID) +} + func (q *querier) GetUserTerminalFont(ctx context.Context, userID uuid.UUID) (string, error) { u, err := q.db.GetUserByID(ctx, userID) if err != nil { @@ -4783,6 +4807,13 @@ func (q *querier) UnfavoriteWorkspace(ctx context.Context, id uuid.UUID) error { return update(q.log, q.auth, fetch, q.db.UnfavoriteWorkspace)(ctx, id) } +func (q *querier) UnlinkOIDCUsersByIssuerMismatch(ctx context.Context, expectedPrefix string) (int64, error) { + if err := q.authorizeContext(ctx, policy.ActionUpdatePersonal, rbac.ResourceUser); err != nil { + return 0, err + } + return q.db.UnlinkOIDCUsersByIssuerMismatch(ctx, expectedPrefix) +} + func (q *querier) UpdateAIBridgeInterceptionEnded(ctx context.Context, params database.UpdateAIBridgeInterceptionEndedParams) (database.AIBridgeInterception, error) { if err := q.authorizeAIBridgeInterceptionAction(ctx, policy.ActionUpdate, params.ID); err != nil { return database.AIBridgeInterception{}, err @@ -5379,7 +5410,7 @@ func (q *querier) UpdateUserLink(ctx context.Context, arg database.UpdateUserLin } func (q *querier) UpdateUserLinkedID(ctx context.Context, arg database.UpdateUserLinkedIDParams) (database.UserLink, error) { - if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceSystem); err != nil { + if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceUserObject(arg.UserID)); err != nil { return database.UserLink{}, err } return q.db.UpdateUserLinkedID(ctx, arg) @@ -5464,6 +5495,17 @@ func (q *querier) UpdateUserStatus(ctx context.Context, arg database.UpdateUserS return updateWithReturn(q.log, q.auth, fetch, q.db.UpdateUserStatus)(ctx, arg) } +func (q *querier) UpdateUserTaskNotificationAlertDismissed(ctx context.Context, arg database.UpdateUserTaskNotificationAlertDismissedParams) (bool, error) { + user, err := q.db.GetUserByID(ctx, arg.UserID) + if err != nil { + return false, err + } + if err := q.authorizeContext(ctx, policy.ActionUpdatePersonal, user); err != nil { + return false, err + } + return q.db.UpdateUserTaskNotificationAlertDismissed(ctx, arg) +} + func (q *querier) UpdateUserTerminalFont(ctx context.Context, arg database.UpdateUserTerminalFontParams) (database.UserConfig, error) { u, err := q.db.GetUserByID(ctx, arg.UserID) if err != nil { diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index d14429a1054..78f4876cf9b 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -7,6 +7,7 @@ import ( "fmt" "net" "reflect" + "strconv" "testing" "time" @@ -1477,6 +1478,21 @@ func (s *MethodTestSuite) TestUser() { dbm.EXPECT().UpdateUserTerminalFont(gomock.Any(), arg).Return(uc, nil).AnyTimes() check.Args(arg).Asserts(u, policy.ActionUpdatePersonal).Returns(uc) })) + s.Run("GetUserTaskNotificationAlertDismissed", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) { + u := testutil.Fake(s.T(), faker, database.User{}) + dbm.EXPECT().GetUserByID(gomock.Any(), u.ID).Return(u, nil).AnyTimes() + dbm.EXPECT().GetUserTaskNotificationAlertDismissed(gomock.Any(), u.ID).Return(false, nil).AnyTimes() + check.Args(u.ID).Asserts(u, policy.ActionReadPersonal).Returns(false) + })) + s.Run("UpdateUserTaskNotificationAlertDismissed", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) { + user := testutil.Fake(s.T(), faker, database.User{}) + userConfig := database.UserConfig{UserID: user.ID, Key: "task_notification_alert_dismissed", Value: "false"} + userConfigValue, _ := strconv.ParseBool(userConfig.Value) + arg := database.UpdateUserTaskNotificationAlertDismissedParams{UserID: user.ID, TaskNotificationAlertDismissed: userConfigValue} + dbm.EXPECT().GetUserByID(gomock.Any(), user.ID).Return(user, nil).AnyTimes() + dbm.EXPECT().UpdateUserTaskNotificationAlertDismissed(gomock.Any(), arg).Return(false, nil).AnyTimes() + check.Args(arg).Asserts(user, policy.ActionUpdatePersonal).Returns(userConfigValue) + })) s.Run("UpdateUserStatus", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) { u := testutil.Fake(s.T(), faker, database.User{}) arg := database.UpdateUserStatusParams{ID: u.ID, Status: u.Status, UpdatedAt: u.UpdatedAt} @@ -1522,7 +1538,7 @@ func (s *MethodTestSuite) TestUser() { })) s.Run("UpdateExternalAuthLinkRefreshToken", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) { link := testutil.Fake(s.T(), faker, database.ExternalAuthLink{}) - arg := database.UpdateExternalAuthLinkRefreshTokenParams{OAuthRefreshToken: "", OAuthRefreshTokenKeyID: "", ProviderID: link.ProviderID, UserID: link.UserID, UpdatedAt: link.UpdatedAt} + arg := database.UpdateExternalAuthLinkRefreshTokenParams{OAuthRefreshToken: "", OAuthRefreshTokenKeyID: "", ProviderID: link.ProviderID, UserID: link.UserID, UpdatedAt: link.UpdatedAt, OldOauthRefreshToken: link.OAuthRefreshToken} dbm.EXPECT().GetExternalAuthLink(gomock.Any(), database.GetExternalAuthLinkParams{ProviderID: link.ProviderID, UserID: link.UserID}).Return(link, nil).AnyTimes() dbm.EXPECT().UpdateExternalAuthLinkRefreshToken(gomock.Any(), arg).Return(nil).AnyTimes() check.Args(arg).Asserts(link, policy.ActionUpdatePersonal) @@ -1541,6 +1557,12 @@ func (s *MethodTestSuite) TestUser() { dbm.EXPECT().UpdateUserLink(gomock.Any(), arg).Return(link, nil).AnyTimes() check.Args(arg).Asserts(link, policy.ActionUpdatePersonal).Returns(link) })) + s.Run("UpdateUserLinkedID", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) { + link := testutil.Fake(s.T(), faker, database.UserLink{}) + arg := database.UpdateUserLinkedIDParams{LinkedID: link.LinkedID, UserID: link.UserID, LoginType: link.LoginType} + dbm.EXPECT().UpdateUserLinkedID(gomock.Any(), arg).Return(link, nil).AnyTimes() + check.Args(arg).Asserts(link, policy.ActionUpdate).Returns(link) + })) s.Run("UpdateUserRoles", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) { u := testutil.Fake(s.T(), faker, database.User{RBACRoles: []string{codersdk.RoleTemplateAdmin}}) o := u @@ -2857,13 +2879,6 @@ func (s *MethodTestSuite) TestCryptoKeys() { } func (s *MethodTestSuite) TestSystemFunctions() { - s.Run("UpdateUserLinkedID", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) { - u := testutil.Fake(s.T(), faker, database.User{}) - l := testutil.Fake(s.T(), faker, database.UserLink{UserID: u.ID}) - arg := database.UpdateUserLinkedIDParams{UserID: u.ID, LinkedID: l.LinkedID, LoginType: database.LoginTypeGithub} - dbm.EXPECT().UpdateUserLinkedID(gomock.Any(), arg).Return(l, nil).AnyTimes() - check.Args(arg).Asserts(rbac.ResourceSystem, policy.ActionUpdate).Returns(l) - })) s.Run("GetLatestWorkspaceAppStatusByAppID", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) { appID := uuid.New() dbm.EXPECT().GetLatestWorkspaceAppStatusByAppID(gomock.Any(), appID).Return(database.WorkspaceAppStatus{}, nil).AnyTimes() @@ -2895,6 +2910,15 @@ func (s *MethodTestSuite) TestSystemFunctions() { dbm.EXPECT().GetUserLinkByLinkedID(gomock.Any(), l.LinkedID).Return(l, nil).AnyTimes() check.Args(l.LinkedID).Asserts(rbac.ResourceSystem, policy.ActionRead).Returns(l) })) + s.Run("CountOIDCLinkedIDsByIssuer", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) { + dbm.EXPECT().CountOIDCLinkedIDsByIssuer(gomock.Any()).Return([]database.CountOIDCLinkedIDsByIssuerRow{}, nil).AnyTimes() + check.Args().Asserts(rbac.ResourceUser, policy.ActionReadPersonal).Returns([]database.CountOIDCLinkedIDsByIssuerRow{}) + })) + s.Run("UnlinkOIDCUsersByIssuerMismatch", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) { + dbm.EXPECT().UnlinkOIDCUsersByIssuerMismatch(gomock.Any(), "issuer||").Return(int64(0), nil).AnyTimes() + check.Args("issuer||").Asserts(rbac.ResourceUser, policy.ActionUpdatePersonal).Returns(int64(0)) + })) + s.Run("GetUserLinkByUserIDLoginType", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) { l := testutil.Fake(s.T(), faker, database.UserLink{}) arg := database.GetUserLinkByUserIDLoginTypeParams{UserID: l.UserID, LoginType: l.LoginType} diff --git a/coderd/database/dbmetrics/querymetrics.go b/coderd/database/dbmetrics/querymetrics.go index 89269b295cb..a6faa0a08df 100644 --- a/coderd/database/dbmetrics/querymetrics.go +++ b/coderd/database/dbmetrics/querymetrics.go @@ -221,6 +221,13 @@ func (m queryMetricsStore) CountInProgressPrebuilds(ctx context.Context) ([]data return r0, r1 } +func (m queryMetricsStore) CountOIDCLinkedIDsByIssuer(ctx context.Context) ([]database.CountOIDCLinkedIDsByIssuerRow, error) { + start := time.Now() + r0, r1 := m.s.CountOIDCLinkedIDsByIssuer(ctx) + m.queryLatencies.WithLabelValues("CountOIDCLinkedIDsByIssuer").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m queryMetricsStore) CountPendingNonActivePrebuilds(ctx context.Context) ([]database.CountPendingNonActivePrebuildsRow, error) { start := time.Now() r0, r1 := m.s.CountPendingNonActivePrebuilds(ctx) @@ -1845,6 +1852,13 @@ func (m queryMetricsStore) GetUserStatusCounts(ctx context.Context, arg database return r0, r1 } +func (m queryMetricsStore) GetUserTaskNotificationAlertDismissed(ctx context.Context, userID uuid.UUID) (bool, error) { + start := time.Now() + r0, r1 := m.s.GetUserTaskNotificationAlertDismissed(ctx, userID) + m.queryLatencies.WithLabelValues("GetUserTaskNotificationAlertDismissed").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m queryMetricsStore) GetUserTerminalFont(ctx context.Context, userID uuid.UUID) (string, error) { start := time.Now() r0, r1 := m.s.GetUserTerminalFont(ctx, userID) @@ -2944,6 +2958,13 @@ func (m queryMetricsStore) UnfavoriteWorkspace(ctx context.Context, arg uuid.UUI return r0 } +func (m queryMetricsStore) UnlinkOIDCUsersByIssuerMismatch(ctx context.Context, expectedPrefix string) (int64, error) { + start := time.Now() + r0, r1 := m.s.UnlinkOIDCUsersByIssuerMismatch(ctx, expectedPrefix) + m.queryLatencies.WithLabelValues("UnlinkOIDCUsersByIssuerMismatch").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m queryMetricsStore) UpdateAIBridgeInterceptionEnded(ctx context.Context, id database.UpdateAIBridgeInterceptionEndedParams) (database.AIBridgeInterception, error) { start := time.Now() r0, r1 := m.s.UpdateAIBridgeInterceptionEnded(ctx, id) @@ -3350,6 +3371,13 @@ func (m queryMetricsStore) UpdateUserStatus(ctx context.Context, arg database.Up return user, err } +func (m queryMetricsStore) UpdateUserTaskNotificationAlertDismissed(ctx context.Context, arg database.UpdateUserTaskNotificationAlertDismissedParams) (bool, error) { + start := time.Now() + r0, r1 := m.s.UpdateUserTaskNotificationAlertDismissed(ctx, arg) + m.queryLatencies.WithLabelValues("UpdateUserTaskNotificationAlertDismissed").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m queryMetricsStore) UpdateUserTerminalFont(ctx context.Context, arg database.UpdateUserTerminalFontParams) (database.UserConfig, error) { start := time.Now() r0, r1 := m.s.UpdateUserTerminalFont(ctx, arg) diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index ce50bf87d20..e3881fc7ded 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -367,6 +367,21 @@ func (mr *MockStoreMockRecorder) CountInProgressPrebuilds(ctx any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountInProgressPrebuilds", reflect.TypeOf((*MockStore)(nil).CountInProgressPrebuilds), ctx) } +// CountOIDCLinkedIDsByIssuer mocks base method. +func (m *MockStore) CountOIDCLinkedIDsByIssuer(ctx context.Context) ([]database.CountOIDCLinkedIDsByIssuerRow, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CountOIDCLinkedIDsByIssuer", ctx) + ret0, _ := ret[0].([]database.CountOIDCLinkedIDsByIssuerRow) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CountOIDCLinkedIDsByIssuer indicates an expected call of CountOIDCLinkedIDsByIssuer. +func (mr *MockStoreMockRecorder) CountOIDCLinkedIDsByIssuer(ctx any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountOIDCLinkedIDsByIssuer", reflect.TypeOf((*MockStore)(nil).CountOIDCLinkedIDsByIssuer), ctx) +} + // CountPendingNonActivePrebuilds mocks base method. func (m *MockStore) CountPendingNonActivePrebuilds(ctx context.Context) ([]database.CountPendingNonActivePrebuildsRow, error) { m.ctrl.T.Helper() @@ -3942,6 +3957,21 @@ func (mr *MockStoreMockRecorder) GetUserStatusCounts(ctx, arg any) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserStatusCounts", reflect.TypeOf((*MockStore)(nil).GetUserStatusCounts), ctx, arg) } +// GetUserTaskNotificationAlertDismissed mocks base method. +func (m *MockStore) GetUserTaskNotificationAlertDismissed(ctx context.Context, userID uuid.UUID) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetUserTaskNotificationAlertDismissed", ctx, userID) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetUserTaskNotificationAlertDismissed indicates an expected call of GetUserTaskNotificationAlertDismissed. +func (mr *MockStoreMockRecorder) GetUserTaskNotificationAlertDismissed(ctx, userID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserTaskNotificationAlertDismissed", reflect.TypeOf((*MockStore)(nil).GetUserTaskNotificationAlertDismissed), ctx, userID) +} + // GetUserTerminalFont mocks base method. func (m *MockStore) GetUserTerminalFont(ctx context.Context, userID uuid.UUID) (string, error) { m.ctrl.T.Helper() @@ -6334,6 +6364,21 @@ func (mr *MockStoreMockRecorder) UnfavoriteWorkspace(ctx, id any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnfavoriteWorkspace", reflect.TypeOf((*MockStore)(nil).UnfavoriteWorkspace), ctx, id) } +// UnlinkOIDCUsersByIssuerMismatch mocks base method. +func (m *MockStore) UnlinkOIDCUsersByIssuerMismatch(ctx context.Context, expectedPrefix string) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnlinkOIDCUsersByIssuerMismatch", ctx, expectedPrefix) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UnlinkOIDCUsersByIssuerMismatch indicates an expected call of UnlinkOIDCUsersByIssuerMismatch. +func (mr *MockStoreMockRecorder) UnlinkOIDCUsersByIssuerMismatch(ctx, expectedPrefix any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnlinkOIDCUsersByIssuerMismatch", reflect.TypeOf((*MockStore)(nil).UnlinkOIDCUsersByIssuerMismatch), ctx, expectedPrefix) +} + // UpdateAIBridgeInterceptionEnded mocks base method. func (m *MockStore) UpdateAIBridgeInterceptionEnded(ctx context.Context, arg database.UpdateAIBridgeInterceptionEndedParams) (database.AIBridgeInterception, error) { m.ctrl.T.Helper() @@ -7174,6 +7219,21 @@ func (mr *MockStoreMockRecorder) UpdateUserStatus(ctx, arg any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserStatus", reflect.TypeOf((*MockStore)(nil).UpdateUserStatus), ctx, arg) } +// UpdateUserTaskNotificationAlertDismissed mocks base method. +func (m *MockStore) UpdateUserTaskNotificationAlertDismissed(ctx context.Context, arg database.UpdateUserTaskNotificationAlertDismissedParams) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateUserTaskNotificationAlertDismissed", ctx, arg) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateUserTaskNotificationAlertDismissed indicates an expected call of UpdateUserTaskNotificationAlertDismissed. +func (mr *MockStoreMockRecorder) UpdateUserTaskNotificationAlertDismissed(ctx, arg any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserTaskNotificationAlertDismissed", reflect.TypeOf((*MockStore)(nil).UpdateUserTaskNotificationAlertDismissed), ctx, arg) +} + // UpdateUserTerminalFont mocks base method. func (m *MockStore) UpdateUserTerminalFont(ctx context.Context, arg database.UpdateUserTerminalFontParams) (database.UserConfig, error) { m.ctrl.T.Helper() diff --git a/coderd/database/migrations/000401_add_workspace_agents_index.down.sql b/coderd/database/migrations/000401_add_workspace_agents_index.down.sql index 3b2a25345fc..bd2bbe47cbd 100644 --- a/coderd/database/migrations/000401_add_workspace_agents_index.down.sql +++ b/coderd/database/migrations/000401_add_workspace_agents_index.down.sql @@ -1 +1 @@ -DROP INDEX IF EXISTS public.workspace_agents_auth_instance_id_deleted_idx; +DROP INDEX IF EXISTS workspace_agents_auth_instance_id_deleted_idx; diff --git a/coderd/database/migrations/000401_add_workspace_agents_index.up.sql b/coderd/database/migrations/000401_add_workspace_agents_index.up.sql index db67cb400f1..dbaaad68422 100644 --- a/coderd/database/migrations/000401_add_workspace_agents_index.up.sql +++ b/coderd/database/migrations/000401_add_workspace_agents_index.up.sql @@ -1 +1 @@ -CREATE INDEX IF NOT EXISTS workspace_agents_auth_instance_id_deleted_idx ON public.workspace_agents (auth_instance_id, deleted); +CREATE INDEX IF NOT EXISTS workspace_agents_auth_instance_id_deleted_idx ON workspace_agents (auth_instance_id, deleted); diff --git a/coderd/database/migrations/testdata/fixtures/000022_initial_v0.6.6.up.sql b/coderd/database/migrations/testdata/fixtures/000022_initial_v0.6.6.up.sql index 102150bf6eb..2192c9b44d4 100644 --- a/coderd/database/migrations/testdata/fixtures/000022_initial_v0.6.6.up.sql +++ b/coderd/database/migrations/testdata/fixtures/000022_initial_v0.6.6.up.sql @@ -16,7 +16,7 @@ SET lock_timeout = 0; SET idle_in_transaction_session_timeout = 0; SET client_encoding = 'UTF8'; SET standard_conforming_strings = on; --- Setting search_path breaks fixtures lacking schema (public.) in queries. +-- Setting search_path breaks fixtures lacking schema () in queries. -- SELECT pg_catalog.set_config('search_path', '', false); SET check_function_bodies = false; SET xmloption = content; @@ -27,16 +27,16 @@ SET row_security = off; -- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.users VALUES ('30095c71-380b-457a-8995-97b8ee6e5307', 'admin@coder.com', 'admin', '\x2470626b6466322d736861323536243635353335247671694c624664385450734355445664376f723653512479686362786c436d485855327965356e616b4a73416e4868316642576447544b48732f2b6a426b4a716377466e2b362f77376f61354632444e35596458584131425639442f43714f7a7541485a652f6f4a4d674d3451', '2022-11-02 13:02:55.13827+02', '2022-11-02 13:02:55.13827+02', 'active', '{admin}') ON CONFLICT DO NOTHING; -INSERT INTO public.users VALUES ('0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'oauthuser1@coder.com', 'oauthuser1', '\x', '2022-11-02 13:05:21.445455+02', '2022-11-02 13:05:21.445455+02', 'active', '{}') ON CONFLICT DO NOTHING; +INSERT INTO users VALUES ('30095c71-380b-457a-8995-97b8ee6e5307', 'admin@coder.com', 'admin', '\x2470626b6466322d736861323536243635353335247671694c624664385450734355445664376f723653512479686362786c436d485855327965356e616b4a73416e4868316642576447544b48732f2b6a426b4a716377466e2b362f77376f61354632444e35596458584131425639442f43714f7a7541485a652f6f4a4d674d3451', '2022-11-02 13:02:55.13827+02', '2022-11-02 13:02:55.13827+02', 'active', '{admin}') ON CONFLICT DO NOTHING; +INSERT INTO users VALUES ('0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'oauthuser1@coder.com', 'oauthuser1', '\x', '2022-11-02 13:05:21.445455+02', '2022-11-02 13:05:21.445455+02', 'active', '{}') ON CONFLICT DO NOTHING; -- -- Data for Name: api_keys; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.api_keys VALUES ('WEG2T4MNno', '\x50ae3bffeac2d2a30e3a7534ca67400c4190c513f2044b5721954b72a1b66ca0', '30095c71-380b-457a-8995-97b8ee6e5307', '2022-11-02 13:03:25.653862+02', '2022-11-03 13:02:55.340963+02', '2022-11-02 13:02:55.340964+02', '2022-11-02 13:02:55.340964+02', 'password', '', '', '', '0001-01-01 01:39:49+01:39:49', 86400) ON CONFLICT DO NOTHING; -INSERT INTO public.api_keys VALUES ('peuLZhMXt4', '\x665200a4744e4f318551a7ca6944e070774e8903a680e8a0a592e0f2c328efb3', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', '2022-11-02 13:05:21.681575+02', '2022-11-03 13:05:21.449566+02', '2022-11-02 13:05:21.449567+02', '2022-11-02 13:05:21.449567+02', 'github', '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', '', '', '0001-01-01 01:39:49+01:39:49', 86400) ON CONFLICT DO NOTHING; +INSERT INTO api_keys VALUES ('WEG2T4MNno', '\x50ae3bffeac2d2a30e3a7534ca67400c4190c513f2044b5721954b72a1b66ca0', '30095c71-380b-457a-8995-97b8ee6e5307', '2022-11-02 13:03:25.653862+02', '2022-11-03 13:02:55.340963+02', '2022-11-02 13:02:55.340964+02', '2022-11-02 13:02:55.340964+02', 'password', '', '', '', '0001-01-01 01:39:49+01:39:49', 86400) ON CONFLICT DO NOTHING; +INSERT INTO api_keys VALUES ('peuLZhMXt4', '\x665200a4744e4f318551a7ca6944e070774e8903a680e8a0a592e0f2c328efb3', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', '2022-11-02 13:05:21.681575+02', '2022-11-03 13:05:21.449566+02', '2022-11-02 13:05:21.449567+02', '2022-11-02 13:05:21.449567+02', 'github', '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', '', '', '0001-01-01 01:39:49+01:39:49', 86400) ON CONFLICT DO NOTHING; -- @@ -49,14 +49,14 @@ INSERT INTO public.api_keys VALUES ('peuLZhMXt4', '\x665200a4744e4f318551a7ca694 -- Data for Name: files; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.files VALUES ('5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', '2022-11-02 13:03:31.667863+02', '30095c71-380b-457a-8995-97b8ee6e5307', 'application/x-tar', '\x524541444d452e6d64000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003030303036343400303030313735300030303031373530003030303030303031333636003134333330343437313532003031313733360020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075737461720030306d616600000000000000000000000000000000000000000000000000000000006d616600000000000000000000000000000000000000000000000000000000003030303030303000303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d2d2d0a6e616d653a20446576656c6f7020636f64652d73657276657220696e20446f636b65720a6465736372697074696f6e3a2052756e20636f64652d73657276657220696e206120446f636b657220646576656c6f706d656e7420656e7669726f6e6d656e740a746167733a205b6c6f63616c2c20646f636b65725d0a2d2d2d0a0a2320636f64652d73657276657220696e20446f636b65720a0a23232047657474696e6720737461727465640a0a52756e2060636f6465722074656d706c6174657320696e69746020616e642073656c65637420746869732074656d706c6174652e20466f6c6c6f772074686520696e737472756374696f6e732074686174206170706561722e0a0a232320537570706f7274656420506172616d65746572730a0a596f752063616e2063726561746520612066696c6520636f6e7461696e696e6720706172616d657465727320616e6420706173732074686520617267756d656e740a602d2d706172616d657465722d66696c656020746f2060636f6465722074656d706c6174657320637265617465602e0a5365652060706172616d732e73616d706c652e79616d6c6020666f72206d6f726520696e666f726d6174696f6e2e0a0a546869732074656d706c617465206861732074686520666f6c6c6f77696e6720707265646566696e656420706172616d65746572733a0a0a2d2060646f636b65725f686f7374603a205061746820746f20286f722061646472657373206f66292074686520446f636b657220736f636b65742e0a20203e20596f752063616e2064657465726d696e652074686520636f72726563742076616c756520666f7220746869732062792072756e6e696e670a20203e2060646f636b657220636f6e74657874206c73602e0a2d2060646f636b65725f61726368603a20417263686974656374757265206f662074686520686f73742072756e6e696e6720446f636b65722e0a2020546869732063616e2062652060616d643634602c206061726d3634602c206f72206061726d7637602e0a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006d61696e2e74660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003030303036343400303030313735300030303031373530003030303030303034323631003134333330343437313532003031313733330020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075737461720030306d616600000000000000000000000000000000000000000000000000000000006d616600000000000000000000000000000000000000000000000000000000003030303030303000303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007465727261666f726d207b0a202072657175697265645f70726f766964657273207b0a20202020636f646572203d207b0a202020202020736f7572636520203d2022636f6465722f636f646572220a20202020202076657273696f6e203d2022302e352e30220a202020207d0a20202020646f636b6572203d207b0a202020202020736f7572636520203d20226b7265757a7765726b65722f646f636b6572220a20202020202076657273696f6e203d20227e3e20322e32302e32220a202020207d0a20207d0a7d0a0a646174612022636f6465725f70726f766973696f6e65722220226d6522207b0a7d0a0a70726f76696465722022646f636b657222207b0a7d0a0a646174612022636f6465725f776f726b73706163652220226d6522207b0a7d0a0a7265736f757263652022636f6465725f6167656e742220226d61696e22207b0a20206172636820202020202020202020203d20646174612e636f6465725f70726f766973696f6e65722e6d652e617263680a20206f73202020202020202020202020203d20226c696e7578220a2020737461727475705f736372697074203d2022636f64652d736572766572202d2d61757468206e6f6e65220a0a20202320546865736520656e7669726f6e6d656e74207661726961626c657320616c6c6f7720796f7520746f206d616b652047697420636f6d6d6974732072696768742061776179206166746572206372656174696e6720610a20202320776f726b73706163652e204e6f7465207468617420746865792074616b6520707265636564656e6365206f76657220636f6e66696775726174696f6e20646566696e656420696e207e2f2e676974636f6e666967210a20202320596f752063616e2072656d6f7665207468697320626c6f636b20696620796f7527642070726566657220746f20636f6e66696775726520476974206d616e75616c6c79206f72207573696e670a20202320646f7466696c65732e202873656520646f63732f646f7466696c65732e6d64290a2020656e76203d207b0a202020204749545f415554484f525f4e414d4520202020203d2022247b646174612e636f6465725f776f726b73706163652e6d652e6f776e65727d220a202020204749545f434f4d4d49545445525f4e414d4520203d2022247b646174612e636f6465725f776f726b73706163652e6d652e6f776e65727d220a202020204749545f415554484f525f454d41494c202020203d2022247b646174612e636f6465725f776f726b73706163652e6d652e6f776e65725f656d61696c7d220a202020204749545f434f4d4d49545445525f454d41494c203d2022247b646174612e636f6465725f776f726b73706163652e6d652e6f776e65725f656d61696c7d220a20207d0a7d0a0a7265736f757263652022636f6465725f617070222022636f64652d73657276657222207b0a20206167656e745f6964203d20636f6465725f6167656e742e6d61696e2e69640a202075726c2020202020203d2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202069636f6e20202020203d20222f69636f6e2f636f64652e737667220a0a20206865616c7468636865636b207b0a2020202075726c202020202020203d2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020696e74657276616c20203d20330a202020207468726573686f6c64203d2031300a20207d0a7d0a0a7265736f757263652022646f636b65725f766f6c756d65222022686f6d655f766f6c756d6522207b0a20206e616d65203d2022636f6465722d247b646174612e636f6465725f776f726b73706163652e6d652e6f776e65727d2d247b646174612e636f6465725f776f726b73706163652e6d652e6e616d657d2d726f6f74220a7d0a0a7265736f757263652022646f636b65725f636f6e7461696e6572222022776f726b737061636522207b0a2020636f756e74203d20646174612e636f6465725f776f726b73706163652e6d652e73746172745f636f756e740a2020696d616765203d2022636f646572636f6d2f636f64652d7365727665723a6c6174657374220a2020232055736573206c6f776572282920746f2061766f696420446f636b6572207265737472696374696f6e206f6e20636f6e7461696e6572206e616d65732e0a20206e616d6520202020203d2022636f6465722d247b646174612e636f6465725f776f726b73706163652e6d652e6f776e65727d2d247b6c6f77657228646174612e636f6465725f776f726b73706163652e6d652e6e616d65297d220a2020686f73746e616d65203d206c6f77657228646174612e636f6465725f776f726b73706163652e6d652e6e616d65290a2020646e732020202020203d205b22312e312e312e31225d0a202023205573652074686520646f636b6572206761746577617920696620746865206163636573732055524c206973203132372e302e302e310a2020656e747279706f696e74203d205b227368222c20222d63222c207265706c61636528636f6465725f6167656e742e6d61696e2e696e69745f7363726970742c20222f6c6f63616c686f73747c3132375c5c2e305c5c2e305c5c2e312f222c2022686f73742e646f636b65722e696e7465726e616c22295d0a2020656e7620202020202020203d205b22434f4445525f4147454e545f544f4b454e3d247b636f6465725f6167656e742e6d61696e2e746f6b656e7d225d0a2020686f7374207b0a20202020686f7374203d2022686f73742e646f636b65722e696e7465726e616c220a2020202069702020203d2022686f73742d67617465776179220a20207d0a2020766f6c756d6573207b0a20202020636f6e7461696e65725f70617468203d20222f686f6d652f636f6465722f220a20202020766f6c756d655f6e616d65202020203d20646f636b65725f766f6c756d652e686f6d655f766f6c756d652e6e616d650a20202020726561645f6f6e6c792020202020203d2066616c73650a20207d0a7d0a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000706172616d732e73616d706c652e79616d6c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003030303036343400303030313735300030303031373530003030303030303030313030003134333330343437313532003031343036370020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075737461720030306d616600000000000000000000000000000000000000000000000000000000006d61660000000000000000000000000000000000000000000000000000000000303030303030300030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000646f636b65725f686f73743a2022756e69783a2f2f2f7661722f72756e2f646f636b65722e736f636b220a646f636b65725f617263683a2022616d643634220a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') ON CONFLICT DO NOTHING; +INSERT INTO files VALUES ('5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', '2022-11-02 13:03:31.667863+02', '30095c71-380b-457a-8995-97b8ee6e5307', 'application/x-tar', '\x524541444d452e6d64000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003030303036343400303030313735300030303031373530003030303030303031333636003134333330343437313532003031313733360020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075737461720030306d616600000000000000000000000000000000000000000000000000000000006d616600000000000000000000000000000000000000000000000000000000003030303030303000303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d2d2d0a6e616d653a20446576656c6f7020636f64652d73657276657220696e20446f636b65720a6465736372697074696f6e3a2052756e20636f64652d73657276657220696e206120446f636b657220646576656c6f706d656e7420656e7669726f6e6d656e740a746167733a205b6c6f63616c2c20646f636b65725d0a2d2d2d0a0a2320636f64652d73657276657220696e20446f636b65720a0a23232047657474696e6720737461727465640a0a52756e2060636f6465722074656d706c6174657320696e69746020616e642073656c65637420746869732074656d706c6174652e20466f6c6c6f772074686520696e737472756374696f6e732074686174206170706561722e0a0a232320537570706f7274656420506172616d65746572730a0a596f752063616e2063726561746520612066696c6520636f6e7461696e696e6720706172616d657465727320616e6420706173732074686520617267756d656e740a602d2d706172616d657465722d66696c656020746f2060636f6465722074656d706c6174657320637265617465602e0a5365652060706172616d732e73616d706c652e79616d6c6020666f72206d6f726520696e666f726d6174696f6e2e0a0a546869732074656d706c617465206861732074686520666f6c6c6f77696e6720707265646566696e656420706172616d65746572733a0a0a2d2060646f636b65725f686f7374603a205061746820746f20286f722061646472657373206f66292074686520446f636b657220736f636b65742e0a20203e20596f752063616e2064657465726d696e652074686520636f72726563742076616c756520666f7220746869732062792072756e6e696e670a20203e2060646f636b657220636f6e74657874206c73602e0a2d2060646f636b65725f61726368603a20417263686974656374757265206f662074686520686f73742072756e6e696e6720446f636b65722e0a2020546869732063616e2062652060616d643634602c206061726d3634602c206f72206061726d7637602e0a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006d61696e2e74660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003030303036343400303030313735300030303031373530003030303030303034323631003134333330343437313532003031313733330020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075737461720030306d616600000000000000000000000000000000000000000000000000000000006d616600000000000000000000000000000000000000000000000000000000003030303030303000303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007465727261666f726d207b0a202072657175697265645f70726f766964657273207b0a20202020636f646572203d207b0a202020202020736f7572636520203d2022636f6465722f636f646572220a20202020202076657273696f6e203d2022302e352e30220a202020207d0a20202020646f636b6572203d207b0a202020202020736f7572636520203d20226b7265757a7765726b65722f646f636b6572220a20202020202076657273696f6e203d20227e3e20322e32302e32220a202020207d0a20207d0a7d0a0a646174612022636f6465725f70726f766973696f6e65722220226d6522207b0a7d0a0a70726f76696465722022646f636b657222207b0a7d0a0a646174612022636f6465725f776f726b73706163652220226d6522207b0a7d0a0a7265736f757263652022636f6465725f6167656e742220226d61696e22207b0a20206172636820202020202020202020203d20646174612e636f6465725f70726f766973696f6e65722e6d652e617263680a20206f73202020202020202020202020203d20226c696e7578220a2020737461727475705f736372697074203d2022636f64652d736572766572202d2d61757468206e6f6e65220a0a20202320546865736520656e7669726f6e6d656e74207661726961626c657320616c6c6f7720796f7520746f206d616b652047697420636f6d6d6974732072696768742061776179206166746572206372656174696e6720610a20202320776f726b73706163652e204e6f7465207468617420746865792074616b6520707265636564656e6365206f76657220636f6e66696775726174696f6e20646566696e656420696e207e2f2e676974636f6e666967210a20202320596f752063616e2072656d6f7665207468697320626c6f636b20696620796f7527642070726566657220746f20636f6e66696775726520476974206d616e75616c6c79206f72207573696e670a20202320646f7466696c65732e202873656520646f63732f646f7466696c65732e6d64290a2020656e76203d207b0a202020204749545f415554484f525f4e414d4520202020203d2022247b646174612e636f6465725f776f726b73706163652e6d652e6f776e65727d220a202020204749545f434f4d4d49545445525f4e414d4520203d2022247b646174612e636f6465725f776f726b73706163652e6d652e6f776e65727d220a202020204749545f415554484f525f454d41494c202020203d2022247b646174612e636f6465725f776f726b73706163652e6d652e6f776e65725f656d61696c7d220a202020204749545f434f4d4d49545445525f454d41494c203d2022247b646174612e636f6465725f776f726b73706163652e6d652e6f776e65725f656d61696c7d220a20207d0a7d0a0a7265736f757263652022636f6465725f617070222022636f64652d73657276657222207b0a20206167656e745f6964203d20636f6465725f6167656e742e6d61696e2e69640a202075726c2020202020203d2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202069636f6e20202020203d20222f69636f6e2f636f64652e737667220a0a20206865616c7468636865636b207b0a2020202075726c202020202020203d2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020696e74657276616c20203d20330a202020207468726573686f6c64203d2031300a20207d0a7d0a0a7265736f757263652022646f636b65725f766f6c756d65222022686f6d655f766f6c756d6522207b0a20206e616d65203d2022636f6465722d247b646174612e636f6465725f776f726b73706163652e6d652e6f776e65727d2d247b646174612e636f6465725f776f726b73706163652e6d652e6e616d657d2d726f6f74220a7d0a0a7265736f757263652022646f636b65725f636f6e7461696e6572222022776f726b737061636522207b0a2020636f756e74203d20646174612e636f6465725f776f726b73706163652e6d652e73746172745f636f756e740a2020696d616765203d2022636f646572636f6d2f636f64652d7365727665723a6c6174657374220a2020232055736573206c6f776572282920746f2061766f696420446f636b6572207265737472696374696f6e206f6e20636f6e7461696e6572206e616d65732e0a20206e616d6520202020203d2022636f6465722d247b646174612e636f6465725f776f726b73706163652e6d652e6f776e65727d2d247b6c6f77657228646174612e636f6465725f776f726b73706163652e6d652e6e616d65297d220a2020686f73746e616d65203d206c6f77657228646174612e636f6465725f776f726b73706163652e6d652e6e616d65290a2020646e732020202020203d205b22312e312e312e31225d0a202023205573652074686520646f636b6572206761746577617920696620746865206163636573732055524c206973203132372e302e302e310a2020656e747279706f696e74203d205b227368222c20222d63222c207265706c61636528636f6465725f6167656e742e6d61696e2e696e69745f7363726970742c20222f6c6f63616c686f73747c3132375c5c2e305c5c2e305c5c2e312f222c2022686f73742e646f636b65722e696e7465726e616c22295d0a2020656e7620202020202020203d205b22434f4445525f4147454e545f544f4b454e3d247b636f6465725f6167656e742e6d61696e2e746f6b656e7d225d0a2020686f7374207b0a20202020686f7374203d2022686f73742e646f636b65722e696e7465726e616c220a2020202069702020203d2022686f73742d67617465776179220a20207d0a2020766f6c756d6573207b0a20202020636f6e7461696e65725f70617468203d20222f686f6d652f636f6465722f220a20202020766f6c756d655f6e616d65202020203d20646f636b65725f766f6c756d652e686f6d655f766f6c756d652e6e616d650a20202020726561645f6f6e6c792020202020203d2066616c73650a20207d0a7d0a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000706172616d732e73616d706c652e79616d6c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003030303036343400303030313735300030303031373530003030303030303030313030003134333330343437313532003031343036370020300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000075737461720030306d616600000000000000000000000000000000000000000000000000000000006d61660000000000000000000000000000000000000000000000000000000000303030303030300030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000646f636b65725f686f73743a2022756e69783a2f2f2f7661722f72756e2f646f636b65722e736f636b220a646f636b65725f617263683a2022616d643634220a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') ON CONFLICT DO NOTHING; -- -- Data for Name: gitsshkeys; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.gitsshkeys VALUES ('30095c71-380b-457a-8995-97b8ee6e5307', '2022-11-02 13:02:55.237442+02', '2022-11-02 13:02:55.237442+02', '-----BEGIN OPENSSH PRIVATE KEY----- +INSERT INTO gitsshkeys VALUES ('30095c71-380b-457a-8995-97b8ee6e5307', '2022-11-02 13:02:55.237442+02', '2022-11-02 13:02:55.237442+02', '-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz c2gtZWQyNTUxOQAAACD3vPanWRzQc0b00kDF3Lj5tNU6zigkbI587uzMatB5lQAA AIiOWWpljllqZQAAAAtzc2gtZWQyNTUxOQAAACD3vPanWRzQc0b00kDF3Lj5tNU6 @@ -65,7 +65,7 @@ qPe89qdZHNBzRvTSQMXcuPm01TrOKCRsjnzu7Mxq0HmVAAAAAAECAwQF -----END OPENSSH PRIVATE KEY----- ', 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPe89qdZHNBzRvTSQMXcuPm01TrOKCRsjnzu7Mxq0HmV ') ON CONFLICT DO NOTHING; -INSERT INTO public.gitsshkeys VALUES ('0ed9befc-4911-4ccf-a8e2-559bf72daa94', '2022-11-02 13:05:21.446353+02', '2022-11-02 13:07:03.586482+02', '-----BEGIN OPENSSH PRIVATE KEY----- +INSERT INTO gitsshkeys VALUES ('0ed9befc-4911-4ccf-a8e2-559bf72daa94', '2022-11-02 13:05:21.446353+02', '2022-11-02 13:07:03.586482+02', '-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz c2gtZWQyNTUxOQAAACCMB3az+6SAPpM1KJp5E/7lvpORrjnwUGdAkY+d7sMhXQAA AIgXCxlfFwsZXwAAAAtzc2gtZWQyNTUxOQAAACCMB3az+6SAPpM1KJp5E/7lvpOR @@ -86,32 +86,32 @@ rowHdrP7pIA+kzUomnkT/uW+k5GuOfBQZ0CRj53uwyFdAAAAAAECAwQF -- Data for Name: organizations; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.organizations VALUES ('bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', 'admin', '', '2022-11-02 13:02:55.136653+02', '2022-11-02 13:02:55.136653+02') ON CONFLICT DO NOTHING; +INSERT INTO organizations VALUES ('bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', 'admin', '', '2022-11-02 13:02:55.136653+02', '2022-11-02 13:02:55.136653+02') ON CONFLICT DO NOTHING; -- -- Data for Name: organization_members; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.organization_members VALUES ('30095c71-380b-457a-8995-97b8ee6e5307', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:02:55.238286+02', '2022-11-02 13:02:55.238286+02', '{organization-admin:bb640d07-ca8a-4869-b6bc-ae61ebb2fda1}') ON CONFLICT DO NOTHING; -INSERT INTO public.organization_members VALUES ('0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:05:21.447595+02', '2022-11-02 13:05:21.447595+02', '{}') ON CONFLICT DO NOTHING; +INSERT INTO organization_members VALUES ('30095c71-380b-457a-8995-97b8ee6e5307', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:02:55.238286+02', '2022-11-02 13:02:55.238286+02', '{organization-admin:bb640d07-ca8a-4869-b6bc-ae61ebb2fda1}') ON CONFLICT DO NOTHING; +INSERT INTO organization_members VALUES ('0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:05:21.447595+02', '2022-11-02 13:05:21.447595+02', '{}') ON CONFLICT DO NOTHING; -- -- Data for Name: provisioner_jobs; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.provisioner_jobs VALUES ('424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:03.554876+02', '2022-11-02 13:06:08.668806+02', '2022-11-02 13:06:03.928629+02', NULL, '2022-11-02 13:06:08.659881+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "a7477610-c69b-46d6-97fb-d6a3425e1ab4"}', '22c3662c-60eb-408b-99e1-85b447c2b7e5') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_jobs VALUES ('52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:51.763412+02', '2022-11-02 13:09:54.75528+02', '2022-11-02 13:09:51.92866+02', NULL, '2022-11-02 13:09:54.750397+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "b56215f2-3b5e-406a-ac54-41c284dc9af3"}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_jobs VALUES ('d6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.223922+02', '2022-11-02 13:04:58.770357+02', '2022-11-02 13:04:55.428757+02', NULL, '2022-11-02 13:04:58.762854+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "c1d2c9d5-6f30-4cd0-9ac5-6bf1c6039988"}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_jobs VALUES ('452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.135318+02', '2022-11-02 13:08:10.808446+02', '2022-11-02 13:08:07.428275+02', NULL, '2022-11-02 13:08:10.801894+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "cd6fe03b-d6cf-4d5d-a448-cef52c3ddea2"}', '22c3662c-60eb-408b-99e1-85b447c2b7e5') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_jobs VALUES ('104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.072314+02', '2022-11-02 13:04:51.51136+02', '2022-11-02 13:04:48.428806+02', NULL, '2022-11-02 13:04:51.504973+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "ea36844d-8eb6-41a2-a237-e9a8ae3f99ea"}', '22c3662c-60eb-408b-99e1-85b447c2b7e5') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_jobs VALUES ('f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:31.675595+02', '2022-11-02 13:03:45.057747+02', '2022-11-02 13:03:31.928784+02', NULL, '2022-11-02 13:03:45.055613+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'template_version_import', '{}', '22c3662c-60eb-408b-99e1-85b447c2b7e5') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_jobs VALUES ('52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.044082+02', '2022-11-02 13:04:22.830929+02', '2022-11-02 13:04:19.428129+02', NULL, '2022-11-02 13:04:22.821799+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "a8c0b8c5-c9a8-4f33-93a4-8142e6858244"}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_jobs VALUES ('3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.3012+02', '2022-11-02 13:04:14.077613+02', '2022-11-02 13:04:11.428743+02', NULL, '2022-11-02 13:04:14.075961+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'template_version_dry_run', '{"workspace_name": "my-workspace", "parameter_values": [], "template_version_id": "920baba5-4c64-4686-8b7d-d1bef5683eae"}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_jobs VALUES ('4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.181124+02', '2022-11-02 13:05:43.884672+02', '2022-11-02 13:05:40.428424+02', NULL, '2022-11-02 13:05:43.877519+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "c7c0a371-db1e-4f10-ab34-6779f573554c"}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_jobs VALUES ('f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:52.489029+02', '2022-11-02 13:07:57.535327+02', '2022-11-02 13:07:52.928569+02', NULL, '2022-11-02 13:07:57.53319+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'template_version_import', '{}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_jobs VALUES ('d842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.103996+02', '2022-11-02 13:10:45.613609+02', '2022-11-02 13:10:42.428688+02', NULL, '2022-11-02 13:10:45.606436+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "2bfa1945-e81f-44e4-8f41-394200f6cb30"}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_jobs VALUES ('424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:03.554876+02', '2022-11-02 13:06:08.668806+02', '2022-11-02 13:06:03.928629+02', NULL, '2022-11-02 13:06:08.659881+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "a7477610-c69b-46d6-97fb-d6a3425e1ab4"}', '22c3662c-60eb-408b-99e1-85b447c2b7e5') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_jobs VALUES ('52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:51.763412+02', '2022-11-02 13:09:54.75528+02', '2022-11-02 13:09:51.92866+02', NULL, '2022-11-02 13:09:54.750397+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "b56215f2-3b5e-406a-ac54-41c284dc9af3"}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_jobs VALUES ('d6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.223922+02', '2022-11-02 13:04:58.770357+02', '2022-11-02 13:04:55.428757+02', NULL, '2022-11-02 13:04:58.762854+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "c1d2c9d5-6f30-4cd0-9ac5-6bf1c6039988"}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_jobs VALUES ('452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.135318+02', '2022-11-02 13:08:10.808446+02', '2022-11-02 13:08:07.428275+02', NULL, '2022-11-02 13:08:10.801894+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "cd6fe03b-d6cf-4d5d-a448-cef52c3ddea2"}', '22c3662c-60eb-408b-99e1-85b447c2b7e5') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_jobs VALUES ('104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.072314+02', '2022-11-02 13:04:51.51136+02', '2022-11-02 13:04:48.428806+02', NULL, '2022-11-02 13:04:51.504973+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "ea36844d-8eb6-41a2-a237-e9a8ae3f99ea"}', '22c3662c-60eb-408b-99e1-85b447c2b7e5') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_jobs VALUES ('f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:31.675595+02', '2022-11-02 13:03:45.057747+02', '2022-11-02 13:03:31.928784+02', NULL, '2022-11-02 13:03:45.055613+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'template_version_import', '{}', '22c3662c-60eb-408b-99e1-85b447c2b7e5') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_jobs VALUES ('52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.044082+02', '2022-11-02 13:04:22.830929+02', '2022-11-02 13:04:19.428129+02', NULL, '2022-11-02 13:04:22.821799+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "a8c0b8c5-c9a8-4f33-93a4-8142e6858244"}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_jobs VALUES ('3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.3012+02', '2022-11-02 13:04:14.077613+02', '2022-11-02 13:04:11.428743+02', NULL, '2022-11-02 13:04:14.075961+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'template_version_dry_run', '{"workspace_name": "my-workspace", "parameter_values": [], "template_version_id": "920baba5-4c64-4686-8b7d-d1bef5683eae"}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_jobs VALUES ('4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.181124+02', '2022-11-02 13:05:43.884672+02', '2022-11-02 13:05:40.428424+02', NULL, '2022-11-02 13:05:43.877519+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "c7c0a371-db1e-4f10-ab34-6779f573554c"}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_jobs VALUES ('f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:52.489029+02', '2022-11-02 13:07:57.535327+02', '2022-11-02 13:07:52.928569+02', NULL, '2022-11-02 13:07:57.53319+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'template_version_import', '{}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_jobs VALUES ('d842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.103996+02', '2022-11-02 13:10:45.613609+02', '2022-11-02 13:10:42.428688+02', NULL, '2022-11-02 13:10:45.606436+02', NULL, 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '30095c71-380b-457a-8995-97b8ee6e5307', 'terraform', 'file', '5cedf49ccf841e2d6a7f84e18c86f38aad8b50495c032d19226118b91addf196', 'workspace_build', '{"dry_run": false, "workspace_build_id": "2bfa1945-e81f-44e4-8f41-394200f6cb30"}', 'e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37') ON CONFLICT DO NOTHING; -- @@ -130,643 +130,643 @@ INSERT INTO public.provisioner_jobs VALUES ('d842dc52-93b9-46ed-842d-d1569b50ddf -- Data for Name: provisioner_daemons; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.provisioner_daemons VALUES ('22c3662c-60eb-408b-99e1-85b447c2b7e5', '2022-11-02 13:02:32.925313+02', NULL, 'gallant_mahavira7', '{echo,terraform}') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_daemons VALUES ('e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37', '2022-11-02 13:02:32.92604+02', NULL, 'brave_borg8', '{echo,terraform}') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_daemons VALUES ('315adce9-adc9-407c-9ff7-905c1c71d020', '2022-11-02 13:02:32.926164+02', NULL, 'lucid_lederberg6', '{echo,terraform}') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_daemons VALUES ('a6246fc1-23f3-4206-ad2d-ff6f85b017b4', '2022-11-02 13:10:56.115722+02', NULL, 'trusting_lamport1', '{echo,terraform}') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_daemons VALUES ('fc5728a8-e0f9-47a8-a00e-31943fc46b29', '2022-11-02 13:10:56.121368+02', NULL, 'wizardly_lumiere8', '{echo,terraform}') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_daemons VALUES ('0a6664b4-07a8-461c-9275-637b1da715bb', '2022-11-02 13:10:56.123247+02', NULL, 'eager_einstein5', '{echo,terraform}') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_daemons VALUES ('22c3662c-60eb-408b-99e1-85b447c2b7e5', '2022-11-02 13:02:32.925313+02', NULL, 'gallant_mahavira7', '{echo,terraform}') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_daemons VALUES ('e70c7dd0-bba3-4ab8-bfe1-f75e42c67e37', '2022-11-02 13:02:32.92604+02', NULL, 'brave_borg8', '{echo,terraform}') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_daemons VALUES ('315adce9-adc9-407c-9ff7-905c1c71d020', '2022-11-02 13:02:32.926164+02', NULL, 'lucid_lederberg6', '{echo,terraform}') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_daemons VALUES ('a6246fc1-23f3-4206-ad2d-ff6f85b017b4', '2022-11-02 13:10:56.115722+02', NULL, 'trusting_lamport1', '{echo,terraform}') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_daemons VALUES ('fc5728a8-e0f9-47a8-a00e-31943fc46b29', '2022-11-02 13:10:56.121368+02', NULL, 'wizardly_lumiere8', '{echo,terraform}') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_daemons VALUES ('0a6664b4-07a8-461c-9275-637b1da715bb', '2022-11-02 13:10:56.123247+02', NULL, 'eager_einstein5', '{echo,terraform}') ON CONFLICT DO NOTHING; -- -- Data for Name: provisioner_job_logs; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.provisioner_job_logs VALUES ('d09b6083-e482-41ac-ad06-3aa731ec4fc6', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:31.936+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('fe61a03b-d5b2-4ba4-a437-253ea724d149', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:31.943+02', 'provisioner_daemon', 'info', 'Adding README.md...', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1badda17-67b2-4795-9351-6a2cee04c22a', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:31.949+02', 'provisioner_daemon', 'info', 'Parse parameters', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('80b05e59-1fdb-48cf-99ed-27ed0aba8ecc', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:31.956+02', 'provisioner_daemon', 'info', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4a5c2ec6-c2e2-4136-9423-14cdff1d6f13', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:32.046+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7da3c668-9812-47b7-9d22-43138800a307', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:32.05+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Initializing the backend...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2f752172-65f0-4a43-9378-c5d0997eca76', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:32.054+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f6c293ae-a496-40c2-ab8e-b7d64aca4bdb', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:32.057+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('15948e16-e69b-47c2-821e-6c0b375dccd8', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:32.059+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('62d872e4-9a8c-4cc5-9f21-083bfcb6a03c', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:32.28+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bce64698-1b64-4ef6-94b7-b5fd071b31e8', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:33.826+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Installing coder/coder v0.5.0...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('35c436dd-3beb-4c5d-9f8a-16198d2fbcb7', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:36.186+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Installed coder/coder v0.5.0 (self-signed, key ID 93C75807601AA0EC)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('aa80ec91-8b8f-4268-88a4-111e5b368bf9', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:37.718+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Installing kreuzwerker/docker v2.20.3...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e322b727-fddf-4d31-b0ad-fe9fb0532586', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:40.978+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Installed kreuzwerker/docker v2.20.3 (self-signed, key ID BD080C4571C6104C)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('26cac587-a5c7-4691-bab4-0fa1436724c3', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:40.984+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b192bac6-94a0-4e89-8d88-eba6c1028abe', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:40.988+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Partner and community providers are signed by their developers.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('57162911-66a9-4597-a289-643724057e2d', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:40.992+02', 'provisioner', 'debug', 'Detecting persistent resources', 'If you''d like to know more about provider signing, you can read about it here:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('19227832-c872-4d8d-b610-c649da56984e', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:40.996+02', 'provisioner', 'debug', 'Detecting persistent resources', 'https://www.terraform.io/docs/cli/plugins/signing.html') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('dce2aa32-5dbd-4c04-af85-c7ef585dca6a', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6e220907-0654-46dc-bb56-212153032d5e', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.003+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8da2943d-7295-43a6-b560-e946c127b0a0', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.006+02', 'provisioner', 'debug', 'Detecting persistent resources', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('79b02372-6db4-4cd6-8c69-c94e9ef2fb5b', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.009+02', 'provisioner', 'debug', 'Detecting persistent resources', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4b810053-fde0-4579-b2fa-f3a80d9f3f5f', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.012+02', 'provisioner', 'debug', 'Detecting persistent resources', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('76a7934d-b410-4ccc-a57e-1329aefe0203', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.015+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('133aa0b6-4da8-48e4-9428-62f6b7635510', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.018+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('a0ed73d5-e07a-4b3a-8046-8fdaefc1dacb', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.027+02', 'provisioner', 'info', 'Detecting persistent resources', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8b67db19-e012-461d-a538-d1dea4f91ef4', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.781+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('60637ec5-5231-4407-b62d-13e44074eb7f', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.784+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c3ed554e-b395-4adf-8642-f7596a71c4cc', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.787+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_provisioner.me: Refresh complete after 0s [id=20990c34-2afd-4e54-bfa3-9b7e5beea09e]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8b988379-c1e4-442b-9232-65fd926eecff', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.791+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_workspace.me: Refresh complete after 0s [id=fc8ca501-3820-427d-92c5-d8ca4fd45362]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9a4e58db-512e-42db-9349-29ebef3c750f', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.816+02', 'provisioner', 'info', 'Detecting persistent resources', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('45f82833-a53c-49ca-be8e-1545917d7062', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.82+02', 'provisioner', 'info', 'Detecting persistent resources', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5c0eb2ac-825d-4ad8-bed8-63d59266030e', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.823+02', 'provisioner', 'info', 'Detecting persistent resources', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9fd0dc4c-e75d-47ac-9676-2e4182da7edd', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.826+02', 'provisioner', 'info', 'Detecting persistent resources', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b9a391a5-117f-45e0-9a2c-89b13c32d201', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.829+02', 'provisioner', 'info', 'Detecting persistent resources', 'Plan: 4 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7470c318-e457-4b2c-b61d-4fbab6f84fc3', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:42.617+02', 'provisioner_daemon', 'info', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('975d23d1-aa43-4efc-b533-55a07e8cc3bf', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:42.705+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f4cad918-2fb0-46a2-b64a-daa899b659fe', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:42.708+02', 'provisioner', 'debug', 'Detecting ephemeral resources', 'Initializing the backend...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('dc6331d7-41a8-4f65-8a89-da54638b5aa0', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:42.846+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ca307075-af47-4fa5-9d3b-4744acc7a0ac', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:42.851+02', 'provisioner', 'debug', 'Detecting ephemeral resources', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('61df6634-b81b-4fde-b7cb-13ed3f97b210', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:42.855+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Reusing previous version of coder/coder from the dependency lock file') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9f227da4-3fa0-4d7d-a65e-38ccf06bba2f', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:43.06+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Reusing previous version of kreuzwerker/docker from the dependency lock file') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e79c1b3c-5db1-48fd-ac61-3fb667e3fc97', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:43.319+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Using previously-installed coder/coder v0.5.0') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('28ec8811-7aaa-460c-b6e2-a9b85eb46b4b', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:43.405+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Using previously-installed kreuzwerker/docker v2.20.3') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('cef26c13-8a06-4ae6-946f-216db5cc0996', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:43.411+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('fe2bc9eb-b7ea-4d20-8d7a-b2be1d3c450a', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:43.415+02', 'provisioner', 'debug', 'Detecting ephemeral resources', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d49391ed-7b60-459e-8fe5-4dd4783f6629', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:43.45+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('25f8cf7f-4cee-4891-9f14-4e32002e6661', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.179+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ca6ad386-5647-49e3-9b05-0f4cdfd35b01', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.184+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('aece424c-595e-4ad8-a1cc-0938d037a3cb', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.187+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_provisioner.me: Refresh complete after 0s [id=ccf0bb8b-fca5-408f-b4a6-93434185c25a]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f345b9bd-429e-468f-aea4-8c11709991da', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.19+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_workspace.me: Refresh complete after 0s [id=5e7ca627-de78-4542-a964-acda3f038ced]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('31b0cf2d-4a8b-4860-86d7-ec9fc5e58abc', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.208+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('602d137a-eaad-4b34-9a5a-e2169adf1b17', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.212+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d5cc54cf-0390-4fa7-9ff5-3bcd15cbe351', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.216+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('a2f6c1a8-05ff-45a8-ac0e-490f7dc0b7b1', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:45.056+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2dbe9812-907b-4344-b602-14c680fd460c', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.222+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'Plan: 3 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('377e167e-4133-40d8-b62b-e33369c5ed55', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.437+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6d970dad-4051-42d8-847c-07320fb9b110', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.525+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('501f4bab-6eb3-4278-9192-7ae725943418', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.528+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Initializing the backend...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('80e17591-51fe-4fda-aa90-18caa59d1b7d', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.533+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bdd4192d-9f9d-4300-8b4e-f1f12a8479a8', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.536+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b46e6002-1277-4d2b-88a6-97f2f250766e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.542+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b251b96c-368b-4be2-982e-621372dd1f0e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.072+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('294bdb11-4229-46a0-a926-34590373e945', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.155+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('70eab552-5fe7-491a-a275-22059e959ccb', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.212+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c32727e1-ca44-405a-9419-038107c4bc96', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.285+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9e3ae09c-09bc-4a27-b271-9446198fb60e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.293+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9d2d3770-f969-4796-9d65-6cc903593d52', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.299+02', 'provisioner', 'debug', 'Detecting persistent resources', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4fdc748e-938c-4809-9125-caae1c44fd74', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.302+02', 'provisioner', 'debug', 'Detecting persistent resources', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('57b70064-827e-4763-b8a0-473f99797b48', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.306+02', 'provisioner', 'debug', 'Detecting persistent resources', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('781a7128-f401-48e3-a752-4f5d3c636ead', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.309+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('71dbe7ee-7537-4cf3-8f33-8c309ebe8b7e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.313+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('360f1a19-7904-440a-a539-6f035169d093', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.317+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('df6ba6d2-fada-4b68-a8a6-978fdc7b55e2', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.32+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bfc96ab3-fa05-416f-b21e-c58a3ab123f2', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.323+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('44a12cb2-da79-469f-b971-c4bf32fe406e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.327+02', 'provisioner', 'debug', 'Detecting persistent resources', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('299f3930-0423-4e75-ae4a-526e37f481c5', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.33+02', 'provisioner', 'debug', 'Detecting persistent resources', ' - coder/coder') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b6bffd24-8365-4a00-98ed-1e1f978aca49', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.333+02', 'provisioner', 'debug', 'Detecting persistent resources', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0422f763-da4f-4552-9562-144927bec94d', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.336+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('aab83530-7dcd-49bd-b981-5c29cd9674d3', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.339+02', 'provisioner', 'debug', 'Detecting persistent resources', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3089607c-329c-4ee7-88dc-e9f48318e52f', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.342+02', 'provisioner', 'debug', 'Detecting persistent resources', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('a20e18c7-daa9-4e40-9ed9-9c11a2ccf5b1', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.346+02', 'provisioner', 'debug', 'Detecting persistent resources', 'providers.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('582dbda6-f79c-43d5-b215-2da3b13bd406', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.351+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b6cf795f-35e7-45ee-98d0-8be04d7ad6fe', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.356+02', 'provisioner', 'debug', 'Detecting persistent resources', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f36be7f7-a6c2-40d1-9722-ae8e4d920a08', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.359+02', 'provisioner', 'debug', 'Detecting persistent resources', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3c03dd84-79e1-4abf-9900-05613cc82664', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.363+02', 'provisioner', 'debug', 'Detecting persistent resources', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4cbcab55-cb60-4982-90f7-be05ffac7566', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.366+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3d553c50-de3b-4770-8bed-2792c6981362', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.369+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1137030a-bfda-4391-8b17-6a1c1a5e2749', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.373+02', 'provisioner', 'info', 'Detecting persistent resources', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bc462919-aa4b-452c-b945-35965cf138c5', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.157+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c76057c5-2191-4336-b7fb-f104d3c4054b', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.162+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5a1771fc-0028-4edb-9bdb-86dcd4c58687', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.165+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_workspace.me: Refresh complete after 0s [id=13dbb32a-5197-458d-8092-6f00a7df5d19]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f29c2e5d-3392-43c3-ae80-1deeea71f11a', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.169+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_provisioner.me: Refresh complete after 0s [id=2ab63b5c-7d85-43db-ae90-cdcdd27ca4d6]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f3eb65ac-2be8-4b88-9bb8-618bfd70561b', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.201+02', 'provisioner', 'info', 'Detecting persistent resources', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('739ec94c-4700-4562-80f7-7f254aa8b129', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.205+02', 'provisioner', 'info', 'Detecting persistent resources', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f01666b7-4de4-451f-8710-8aa63d32aa8e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.209+02', 'provisioner', 'info', 'Detecting persistent resources', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('556f3f57-5a46-49da-aac4-66ed5d101e7f', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.213+02', 'provisioner', 'info', 'Detecting persistent resources', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('55d5e4f7-e80a-4622-9168-7e412651b91e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.216+02', 'provisioner', 'info', 'Detecting persistent resources', 'Plan: 4 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('664fe6c8-1fec-410c-93d8-385e8b65e7d4', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:14.076+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1af37305-2e9f-445d-b3fb-dea1dece403b', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.436+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5da10ab9-a23c-44ea-98bb-865dc706eae2', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.44+02', 'provisioner_daemon', 'info', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7d8d1b95-2660-4724-a2e5-8c0439eff2e7', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.534+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('89f7bb3f-c495-4567-a188-25c5b41f6e5d', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.538+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('050649c2-fa47-407a-b871-b9f9a8a48b49', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.543+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7d7e19cb-238c-4293-a488-5f61fe83d9d9', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.548+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('daf99de4-753e-4a40-a412-385a97ffe655', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.551+02', 'provisioner', 'debug', 'Starting workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b10105f1-a4ca-473e-a33d-a6755709ac7d', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.776+02', 'provisioner', 'debug', 'Starting workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d974959e-0c77-4c38-8302-dbdea5f8b702', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.966+02', 'provisioner', 'debug', 'Starting workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3d0f0030-8fdb-4193-9f71-e7648c0b00ca', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.023+02', 'provisioner', 'debug', 'Starting workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5e93f614-bd73-4fd7-bf0d-85c61e8294dd', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.095+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('160b7134-0cd1-4c28-b05a-ac7fa6d5d05e', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.098+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7b4b1ca6-fe48-4f1b-bd9b-b6ab54b3cccd', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.086+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8a234d62-5d70-450d-ae9e-fd46186a7717', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.102+02', 'provisioner', 'debug', 'Starting workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3872d0e7-f5fd-45f9-9027-ec41eacdbc8f', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.106+02', 'provisioner', 'debug', 'Starting workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('02eb7d16-8513-4ac8-af9b-bd096faccbe0', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.109+02', 'provisioner', 'debug', 'Starting workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('afefbc98-b47e-4c32-99cc-a2cbe7066d43', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.112+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('785eef1d-66ce-406e-bf12-5e2e95ffdf4a', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.115+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('cb782ff3-5ef8-4312-9431-8a44aed28d9d', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.118+02', 'provisioner', 'debug', 'Starting workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('02003fe3-ac96-4cdc-8683-5042aff7ad81', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.122+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2f203901-94d5-4e9d-8988-d06224e9fa0b', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.125+02', 'provisioner', 'debug', 'Starting workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9d53f365-961a-43df-8a0e-b974649183aa', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.129+02', 'provisioner', 'debug', 'Starting workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3dd1ed46-1de4-4362-bfff-784182b44471', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.132+02', 'provisioner', 'debug', 'Starting workspace', ' - coder/coder') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c03469d0-08a9-4378-b538-50d6d1d50c79', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.135+02', 'provisioner', 'debug', 'Starting workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c6166453-3033-4d69-a5f0-4856776c76e4', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.138+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('fa7a0d2f-1bd2-46e9-9f25-8bd45860dd98', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.141+02', 'provisioner', 'debug', 'Starting workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('a1fd6072-dde6-420c-bf8e-9ebc4d537866', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.145+02', 'provisioner', 'debug', 'Starting workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('cae1268e-b797-4e97-83cb-95ff016a5a63', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.147+02', 'provisioner', 'debug', 'Starting workspace', 'providers.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('938f0d99-4dc6-42a1-b6b9-c9e70b450cd5', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.15+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3d9d2115-f6ea-40c2-9f56-5075cada0c34', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.152+02', 'provisioner', 'debug', 'Starting workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('46e2af39-78f2-4a37-824a-0b4f2dc54764', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.157+02', 'provisioner', 'debug', 'Starting workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1ae80356-6b65-4ff6-8230-7cab8a8cfaf9', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.161+02', 'provisioner', 'debug', 'Starting workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('386b59ce-c30e-4f7d-addc-178e223b1d87', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.164+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b18f40d1-f649-4f34-b332-feb6690482da', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.168+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0747c356-f2ea-49ab-b7ba-292ed9abff7a', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.171+02', 'provisioner', 'info', 'Starting workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('da54beff-b031-436d-93f0-67f2ff53be96', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.94+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('78007aad-5614-4d0e-9fa9-ae1307b6240f', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.946+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3159380d-141a-4d2c-9a69-cbc90c5fe71b', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.951+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=3a9a1feb-e89d-457c-9d53-ac751b198ebe]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6e6a0df4-feb4-4339-87ce-ccf48ff873db', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.956+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=17cc17af-8bbb-4761-ad39-da62ad512c23]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('88cae505-15e1-4ca8-bcf7-8bd1c3af8030', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.975+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('463ef456-2b86-4c67-bc2b-23079a793142', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.979+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e29a0563-0299-42df-8f3e-38fdb851d743', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.982+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e0756af8-2cce-4246-a589-a4741eee2359', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.985+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7e6f0608-02b9-4523-bdef-5a8cdf63d65b', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.987+02', 'provisioner', 'info', 'Starting workspace', 'Plan: 4 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1fb337be-0749-4e98-8513-62164696b42e', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.166+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6d785a3a-7864-4cbf-9612-af13c4347f66', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.172+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Creation complete after 0s [id=0907dab9-1909-4d03-bffd-936a2a05a22a]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9e3f0f0d-3269-48c8-aa1c-f633360809cc', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.185+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d6bc2b50-2e74-414a-bf13-a43189e86abb', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.192+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0e090bdb-12b2-4959-9230-9b014af2b7ca', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.195+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Creation complete after 0s [id=5f1c2ba3-6b26-409c-9790-df56b62efcbc]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4bfa811d-9368-4548-be3e-3f7884d2ba5d', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.198+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Creation complete after 0s [id=coder-admin-my-workspace-root]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('00ef0ca8-439c-4b3f-94a4-cd086d15e9d2', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.21+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('53d6c9b1-8d43-4f0d-963b-8967ee2a3409', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.74+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creation complete after 1s [id=a1ff536472e9efdd5ed90d6aafc28669799668007ab93823567ac08066822244]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f29082e9-aad2-46f9-8d4a-bacf3d3618dd', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.749+02', 'provisioner', 'info', 'Starting workspace', 'Apply complete! Resources: 4 added, 0 changed, 0 destroyed.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6a3690dc-6e1f-400b-b47f-af64bd007a55', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.755+02', 'provisioner', 'info', 'Starting workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('473429ac-5ede-48cd-b754-9b3e498aa60c', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:22.83+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f5303749-4d64-4f3d-ac0e-81e629355e86', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.44+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0812fe54-1b04-4b52-9f43-049fa80467e8', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.444+02', 'provisioner_daemon', 'info', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('31f9020f-3378-4209-a421-7399c86ea76d', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.54+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1110403f-1865-4dca-97a7-4ecb14a97134', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.545+02', 'provisioner', 'debug', 'Stopping workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('955f1a84-1718-4aa7-8022-3ea41d2ba1ba', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.549+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ea27eb37-4934-4bad-bbeb-8a7f0bec0498', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.553+02', 'provisioner', 'debug', 'Stopping workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3525b9d7-69c2-4537-9f47-6290966b4f3d', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.555+02', 'provisioner', 'debug', 'Stopping workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0b83bb69-0b72-4e11-8c13-2b68b90217a9', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.832+02', 'provisioner', 'debug', 'Stopping workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bff36240-532f-44d0-ae0f-2c2faa22ebf3', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.941+02', 'provisioner', 'debug', 'Stopping workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('755ab7f1-8af7-4e73-bdf4-e7d42250f102', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.997+02', 'provisioner', 'debug', 'Stopping workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('41af9b10-8823-40ef-9acc-0c7c0aac73aa', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.066+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4eb9c01c-e77c-4509-b1d3-f394383e00dc', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.072+02', 'provisioner', 'debug', 'Stopping workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6baa0b42-517a-4154-804f-50b900a17cb0', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.077+02', 'provisioner', 'debug', 'Stopping workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5ed6a9d9-ce78-41f2-850b-c54354ba85fc', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.081+02', 'provisioner', 'debug', 'Stopping workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f674b8d8-1aca-475d-84d1-74e68caad7c2', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.084+02', 'provisioner', 'debug', 'Stopping workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bf8e0403-bc1f-4200-93cd-d459c5d4eea5', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.089+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2a26f80a-f534-4a57-8f60-61b0245b2a28', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.092+02', 'provisioner', 'debug', 'Stopping workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d687a831-e9cf-47f7-8fee-2b971e07110e', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.095+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5f1ea8a9-583f-4613-9cf9-2ae1dada1013', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.098+02', 'provisioner', 'debug', 'Stopping workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('63a97f16-df0c-4064-abb1-fa94edff116b', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.1+02', 'provisioner', 'debug', 'Stopping workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0659c1b7-ba0a-4153-a121-1b707d6dba43', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.103+02', 'provisioner', 'debug', 'Stopping workspace', ' - coder/coder') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5026c663-c180-4880-b27e-831ea7b915e6', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.106+02', 'provisioner', 'debug', 'Stopping workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('11bc6add-90ee-4ef1-8270-2c4b75149666', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.108+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('a4ed025f-5228-40fd-97dc-3f581423760c', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.11+02', 'provisioner', 'debug', 'Stopping workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f87eb959-7fa4-4574-b57f-0d25eddb5780', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.113+02', 'provisioner', 'debug', 'Stopping workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('190ce772-4a3e-48d1-82fc-6d2c4406376b', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.115+02', 'provisioner', 'debug', 'Stopping workspace', 'providers.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('807aa93f-0413-4158-9706-aad0f2bd3472', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.118+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('224ba0e5-1c27-44d9-8dd9-d04843eed695', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.12+02', 'provisioner', 'debug', 'Stopping workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('744e3d76-8b8d-40c2-a3d3-2b2707720a2b', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.123+02', 'provisioner', 'debug', 'Stopping workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('53594c81-a9e6-4ab3-babd-5491157c4de1', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.13+02', 'provisioner', 'debug', 'Stopping workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('86f24df0-9053-4aee-a55e-39186d23dd9c', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.133+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('710b2445-edfe-4b38-956f-f3ee83affebc', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.136+02', 'provisioner', 'debug', 'Stopping workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8e29a006-dbbc-4cc2-8856-70aaddc70627', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.139+02', 'provisioner', 'info', 'Stopping workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bbf4cec8-2edc-47e1-9f18-71c0d0f3229e', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.88+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ad23bec6-86a9-4831-9adc-3ee4d979e49c', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.887+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0a0c2ddc-94f4-4d9a-a403-c0229939ad38', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.89+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=69d8c247-a4cc-4de6-86c0-ce073255316a]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('992e7b85-8c39-4c79-82c8-d9e616baf3b3', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.894+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=3a9a1feb-e89d-457c-9d53-ac751b198ebe]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5abaec85-0b9a-49a0-a547-e40f68123865', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.898+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Refreshing state... [id=0907dab9-1909-4d03-bffd-936a2a05a22a]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('927353ca-a7f2-4604-b7a2-0b1e3c5bd1da', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.902+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Refresh complete [id=0907dab9-1909-4d03-bffd-936a2a05a22a]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('94933b2b-bf01-45c8-a0e9-6c82700cd4d4', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.91+02', 'provisioner', 'info', 'Stopping workspace', 'coder_app.code-server: Refreshing state... [id=5f1c2ba3-6b26-409c-9790-df56b62efcbc]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5b8fcf47-22e5-4ced-a3b7-aaaef79f8452', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.913+02', 'provisioner', 'info', 'Stopping workspace', 'coder_app.code-server: Refresh complete [id=5f1c2ba3-6b26-409c-9790-df56b62efcbc]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2c9cb430-1dc6-4896-a0c8-469947399523', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.917+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Refreshing state... [id=coder-admin-my-workspace-root]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9cbe3787-1861-4007-8803-f071482fa2fa', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.921+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Refresh complete [id=coder-admin-my-workspace-root]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d8b2a8a8-c653-412e-a70d-dc65a14c2af6', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.925+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Refreshing state... [id=a1ff536472e9efdd5ed90d6aafc28669799668007ab93823567ac08066822244]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6b22e12c-75e1-4f5a-a571-add29e687bd8', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.043+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Refresh complete [id=a1ff536472e9efdd5ed90d6aafc28669799668007ab93823567ac08066822244]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('db7d2cd9-0331-4e1c-9ae2-0cc18cfb1d5e', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.057+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Drift detected (update)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('71455c3a-c6ec-472c-9b6e-e174697c3e93', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.061+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Drift detected (update)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5580b627-218d-43eb-b619-b269c796b158', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.066+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Drift detected (update)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9499f481-527c-46a8-9a9f-f84ab5caf603', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.069+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Plan to delete') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c245b280-6683-4aea-ab6b-55dd8ccc02dd', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.073+02', 'provisioner', 'info', 'Stopping workspace', 'Plan: 0 to add, 0 to change, 1 to destroy.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0aa11a57-2d0c-40f9-84e2-793d47db516f', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.174+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Destroying... [id=a1ff536472e9efdd5ed90d6aafc28669799668007ab93823567ac08066822244]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('354d0fe5-252d-49eb-a432-3672bd77f421', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.472+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Destruction complete after 0s') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e4cf8c6b-2e24-4db8-bb71-d57c5c88157b', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.485+02', 'provisioner', 'info', 'Stopping workspace', 'Apply complete! Resources: 0 added, 0 changed, 1 destroyed.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('651e7bf8-eeb1-41fc-acca-d00036fd1dbe', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.49+02', 'provisioner', 'info', 'Stopping workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('15a9676b-718b-4461-9e62-81fbd550a897', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:51.51+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('109203b0-c680-45fe-b470-ab383e7322f7', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.438+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('25618f60-4596-4bb5-9ba4-daa75e885289', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.442+02', 'provisioner_daemon', 'info', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('53042802-9b70-4933-91ab-b2c0bc444693', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.545+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c9ad724a-5481-44f7-b8a1-b450dc5051f6', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.549+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('71bc590a-a0e5-4268-89a7-d6178304d91c', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.552+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('aa7529a0-8020-4268-a106-bfa9d7b8db45', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.555+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4ebfc500-c4ff-4247-a4e8-14e6814d9fac', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.557+02', 'provisioner', 'debug', 'Starting workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('541853de-e473-486d-b6e1-3b6e78518788', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.82+02', 'provisioner', 'debug', 'Starting workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2778a009-7045-4773-beb6-4fb7ca574116', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.993+02', 'provisioner', 'debug', 'Starting workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ed060a03-3f5a-4695-97e3-22d7deab8d38', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.053+02', 'provisioner', 'debug', 'Starting workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3564ae4e-b903-4431-96d9-17d7bac188f7', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.128+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('683bd5c5-0397-4d70-8e5f-7d5995f1f38a', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.136+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('55d3ea8e-5bc0-4aac-aa19-4d4c74146503', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.14+02', 'provisioner', 'debug', 'Starting workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('cbff931a-8b68-4dfa-83a2-40809e7add5d', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.143+02', 'provisioner', 'debug', 'Starting workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3fff2b02-6fc5-421d-8c2a-1ab8ffbc9fcc', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.146+02', 'provisioner', 'debug', 'Starting workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9ac3b08f-bb36-4e21-828f-4ae47d2b9719', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.149+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('74d08fc2-27a1-403b-85f8-24ec862dd288', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.152+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d2511df7-1992-4c8d-b5ca-bb60bc638ce4', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.156+02', 'provisioner', 'debug', 'Starting workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('681814dc-5855-4e4b-8907-c9de0d29b119', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.16+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bbbb300a-9a20-4e88-8f12-4de90b82594e', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.163+02', 'provisioner', 'debug', 'Starting workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8bc2492b-fd16-4098-92e9-7d8ece563d3d', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.166+02', 'provisioner', 'debug', 'Starting workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('dcdf65fb-c226-4554-8fec-edbb04f1414e', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.17+02', 'provisioner', 'debug', 'Starting workspace', ' - coder/coder') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8820392e-9d9e-4988-9623-ee1af0059d11', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.173+02', 'provisioner', 'debug', 'Starting workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9c64a617-539b-4e57-a750-355c66cf15cb', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.176+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bffd2b77-7f42-4aef-ac19-586998a6f614', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.179+02', 'provisioner', 'debug', 'Starting workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d7fcb4d1-6c30-4777-83c6-015b0f48a970', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.182+02', 'provisioner', 'debug', 'Starting workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('cdcb62d9-f347-4654-b5b2-28fd6960a509', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.185+02', 'provisioner', 'debug', 'Starting workspace', 'providers.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('95ea3beb-90e7-4787-901c-f570df3bd801', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.188+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9797523c-35ee-4ed3-a69f-af357fafc42c', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.19+02', 'provisioner', 'debug', 'Starting workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bb8189ba-82a1-48f1-bf1c-e000659f8697', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.193+02', 'provisioner', 'debug', 'Starting workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7d86f4fa-e8e9-42f7-b1f6-f27822c67292', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.195+02', 'provisioner', 'debug', 'Starting workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('17e19806-b748-4837-9166-0bcae6592ffe', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.198+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('07392a8e-31b3-4e7d-ade1-a09d4c2ee2b6', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.2+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('524bf497-10f8-41d3-960d-ed67df4fa9ec', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.204+02', 'provisioner', 'info', 'Starting workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('326c0c94-5c0a-45f8-8a26-1e8639553502', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.954+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8a6b7cd1-f0f5-4972-9364-cdc5eeb61f0c', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.958+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('61b04b23-eca5-4c15-91bb-bb88f00a2021', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.962+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=b65a767f-719a-4883-a186-aeb1ac05cb25]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('73e70e26-c6b7-4d8e-a9cc-1acc06022279', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.967+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=3a9a1feb-e89d-457c-9d53-ac751b198ebe]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('74683648-d94f-4d9e-9e6d-bf5fc05fd706', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.972+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Refreshing state... [id=0907dab9-1909-4d03-bffd-936a2a05a22a]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0c241c2a-7607-438c-8af2-c3641959f485', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.976+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Refreshing state... [id=coder-admin-my-workspace-root]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('944880c1-ef75-45b8-a60f-f17800a83023', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.979+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Refresh complete [id=0907dab9-1909-4d03-bffd-936a2a05a22a]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7a466591-e523-4fe0-9df3-564a35161f49', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.982+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Refresh complete [id=coder-admin-my-workspace-root]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c4e5c961-e812-40e3-b0a4-bc5bb781751d', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.986+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Refreshing state... [id=5f1c2ba3-6b26-409c-9790-df56b62efcbc]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0b2bdd2e-4c9f-4514-8f42-0650a0be805a', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.989+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Refresh complete [id=5f1c2ba3-6b26-409c-9790-df56b62efcbc]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d3b5a8c6-f038-4147-a074-4603945ed27b', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.997+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Drift detected (update)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4f2c80f8-d06b-4670-976e-3b58b5f8200d', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:57.002+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('755914cd-d41e-447e-9343-284fe4c0a9a5', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:57.006+02', 'provisioner', 'info', 'Starting workspace', 'Plan: 1 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('dc059e5c-18aa-4d95-b50d-9861ee068be7', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:57.229+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ccbb8238-0a62-4d0d-8269-3709ce02f47a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.964+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e8fd1570-dfb7-4bdf-9d13-fd1dbf3cb124', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.967+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('76167fa7-9670-430f-9536-176cdc4a9f5c', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.97+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9d1e67b3-6bd8-435e-96b5-c7b012b42604', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.972+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4a8a8443-c6cf-435d-8527-1a782b74b838', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.975+02', 'provisioner', 'info', 'Starting workspace', 'Plan: 4 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('45c669a4-88fd-47f0-98a3-e0875d352d7a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.16+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c48882a8-d865-44ee-b8d0-6fa60a255848', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.165+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('dda5b5fe-027d-402a-8852-a4ce2bfe4d71', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.17+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Creation complete after 0s [id=6137599d-4ad6-4e58-b0fa-a3d1a88fa5a9]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ea5f28e9-38b6-4d6a-8ffb-9e48657632ea', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.174+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Creation complete after 0s [id=coder-oauthuser1-test1-root]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('53224434-ead2-4aa3-8c86-177bc4c8fc3b', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.181+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('71ddfedf-d129-495e-bb80-ecf16f789be0', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.186+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f2f59832-212f-466c-92f3-ede2f34fc731', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.189+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Creation complete after 0s [id=3a585bc7-75e6-408e-b0bc-dc64e4d3be0a]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b10e5078-18f2-4ccc-875f-134d5724667a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.857+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creation complete after 1s [id=eed96dcc159829b9a91ee13a2106b3f8598896f99d7142765276914e628e8109]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('566e4cb4-2828-43f1-af4c-5797a7fd3504', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.868+02', 'provisioner', 'info', 'Starting workspace', 'Apply complete! Resources: 4 added, 0 changed, 0 destroyed.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ad17b455-6adc-4f4a-851e-c8fa568c20c7', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.871+02', 'provisioner', 'info', 'Starting workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bfe7a100-7f42-471f-a210-b687bda0fc24', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:57.739+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creation complete after 1s [id=db4aecde657b126939e8bad412b953314cd1316fe7b3f3d061203d0a71e037e8]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e896bbfe-86a0-4f7b-bf6e-4b848d945cbf', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:57.743+02', 'provisioner', 'info', 'Starting workspace', 'Apply complete! Resources: 1 added, 0 changed, 0 destroyed.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('250130a2-19fd-4cce-874f-c74d55aa2f14', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:57.748+02', 'provisioner', 'info', 'Starting workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('91616345-1c98-40ba-a4d3-23514865abb8', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:58.769+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('35036434-b0d7-4d8b-9b2e-2a55fb045bd7', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.44+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('80f1e6fd-8e57-46a9-bcc0-436d9ff34cc2', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.444+02', 'provisioner_daemon', 'info', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d4aa27c1-8b51-455f-ad37-ef3031e9959d', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.535+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ef4ffdad-1b7c-42e9-9ca4-03a878e8efce', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.54+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('caae1027-94c4-4ce9-a58a-c7629632c763', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.545+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1d890d4d-1b83-4cbc-b4b9-904150e970ae', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.549+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ebe2cf5f-bd32-44e9-95a2-e92fd68e51f4', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.552+02', 'provisioner', 'debug', 'Starting workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('849e5476-f4d6-42aa-9331-a80a27a12107', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.916+02', 'provisioner', 'debug', 'Starting workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0c142d37-a1d7-4ba1-b3e3-a2544952099a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.984+02', 'provisioner', 'debug', 'Starting workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0654054e-9f59-40b2-b899-0b8a8ec3f33a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.044+02', 'provisioner', 'debug', 'Starting workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('edc50f19-4bbe-46bf-8f13-292384708095', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.114+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ce618bdf-d2b0-4857-98d4-842d33f0154b', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.12+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('794ea457-7da4-42c0-a960-bf0895541c9c', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.124+02', 'provisioner', 'debug', 'Starting workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('47d5a9e6-4b36-4129-a746-5611767673cb', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.128+02', 'provisioner', 'debug', 'Starting workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b23c35f8-a06c-4716-9d6d-1ab79dda11bf', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.131+02', 'provisioner', 'debug', 'Starting workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('75fc0daa-c636-49da-b635-09bb34b69a20', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.134+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('56702227-afc1-4413-a939-36f521e80d4b', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.137+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('becc9242-efa6-4ed9-a69e-8f4c589529d0', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.141+02', 'provisioner', 'debug', 'Starting workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('37205ac5-6eb1-4773-9def-cfd48cb8ce11', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.146+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b85b9b97-4fad-442f-8662-af1660d73f3f', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.15+02', 'provisioner', 'debug', 'Starting workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('80c19b36-2754-4b72-9b5e-59518b293241', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.153+02', 'provisioner', 'debug', 'Starting workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2dc14d18-0cac-4a18-b7c1-1f3b858db5a3', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.156+02', 'provisioner', 'debug', 'Starting workspace', ' - coder/coder') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d0d6d612-755f-418b-8c2c-d3509a42abd0', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.159+02', 'provisioner', 'debug', 'Starting workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1f466eae-c2b8-4942-a189-07d80f8cd34c', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.162+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('cf37bd97-4ae3-404b-8f0a-94a7f2bf9af1', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.165+02', 'provisioner', 'debug', 'Starting workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8000ff14-2939-472d-ae75-ff323d5c02df', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.168+02', 'provisioner', 'debug', 'Starting workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('116ed6f5-9486-45c8-a460-de9491854550', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.171+02', 'provisioner', 'debug', 'Starting workspace', 'providers.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3e3ba08f-2010-4e24-bbc9-97a0f17ccae5', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.174+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0ebddff5-9fdf-4026-885e-5bfbe81cf837', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.177+02', 'provisioner', 'debug', 'Starting workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1e283699-cc08-47c6-accc-69c1d1579540', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.179+02', 'provisioner', 'debug', 'Starting workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('08c60987-9653-4c60-8d72-60c930581bac', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.182+02', 'provisioner', 'debug', 'Starting workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ad734b46-eb2b-4566-b2d2-b8f5c6a65192', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.185+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ea094a42-52ef-4974-a81d-db49d6dca17a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.188+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d962bb9b-9ffc-4131-b9e2-4b540a025d20', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.191+02', 'provisioner', 'info', 'Starting workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8edcc52f-4242-4c78-b375-eb343d439e2d', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.916+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('33f37088-1e7c-4cfb-b150-5d5e735f6da5', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.924+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('123192aa-9f05-4924-b724-c44928a6034d', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.928+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=fe418e81-b8a0-4f69-87ea-a0a597745e29]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('15a7ed53-89f0-43da-af80-9b95649e0697', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.931+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=b90547be-8870-4d68-8184-e8b2242b7c01]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3109c750-9158-463e-b1c6-7e8a9d562a54', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:43.883+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('451eda7a-d6a8-464c-893e-78796ef328bd', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:03.941+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6cd03ef3-ca74-4898-b014-3823feef5014', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:03.945+02', 'provisioner_daemon', 'info', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('be374e41-cf6f-493b-a799-e2f548f940f5', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:04.047+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('351f9dbc-1936-4890-b145-d1644c4a346a', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:04.05+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('040204a2-cdf4-4ee1-9704-7c8ec1fa0374', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:04.053+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('09c75767-3d08-4673-b4f6-eca8fac2058f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:04.056+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c986858c-c26e-49ea-a011-38e07510dc51', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:04.059+02', 'provisioner', 'debug', 'Starting workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1e352ba9-9fda-4a5e-9bc6-38baac1ac124', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.453+02', 'provisioner', 'debug', 'Starting workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('778be809-e832-4518-b63c-b4d4ea9bb24f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.62+02', 'provisioner', 'debug', 'Starting workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d2302004-ed2b-4071-bec5-ce9f16446aaa', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.695+02', 'provisioner', 'debug', 'Starting workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4055c54c-f128-46a1-86f9-ccec28d9cb9f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.75+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('05cdffde-f37a-4899-97ad-b3954c99fe20', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.754+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('20215593-28a0-4a44-8d23-f990171d6e25', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.758+02', 'provisioner', 'debug', 'Starting workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('74c22124-ad8c-4c8b-9c38-e074c0a6690b', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.761+02', 'provisioner', 'debug', 'Starting workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f5df23a6-6461-4b64-bb02-16b4c8a8558c', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.764+02', 'provisioner', 'debug', 'Starting workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('89564128-9d95-4dbf-bfc9-3e0a3e080f6a', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.766+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e9c32ce3-71f1-4b4c-a269-c8f26b70d2cf', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.769+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9ea0b406-aed4-4dde-978d-0aa184d905ab', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.772+02', 'provisioner', 'debug', 'Starting workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e2c45f9e-f037-41fc-b1a0-3415309a4bc7', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.775+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e70b1f1f-a226-481b-99a7-84ea9df1aeee', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.778+02', 'provisioner', 'debug', 'Starting workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f2724cf1-bbda-40ba-825f-64333171092f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.78+02', 'provisioner', 'debug', 'Starting workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4727c32b-90e1-4d27-9c52-e782f00aec3c', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.783+02', 'provisioner', 'debug', 'Starting workspace', ' - coder/coder') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('56d15d39-2f3a-4128-9462-109a7c35d3e4', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.785+02', 'provisioner', 'debug', 'Starting workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d2837d78-e7d6-4c6e-a7cf-4083f774a060', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.79+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('fd344fbd-5269-4d53-9d24-2f0a3e07b3f5', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.792+02', 'provisioner', 'debug', 'Starting workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('eb228344-c8f8-4ba8-a2f4-b486ff59d8fe', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.796+02', 'provisioner', 'debug', 'Starting workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('192a9ac1-af63-4aca-9dbf-3861ab360ee6', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.804+02', 'provisioner', 'debug', 'Starting workspace', 'providers.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b6517635-07cc-439a-bca9-a738bae94c53', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.807+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('cd3c921e-7929-49e4-b853-7713f7da68aa', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.809+02', 'provisioner', 'debug', 'Starting workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c98500c6-8ce5-4e15-9403-aa97f618141a', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.812+02', 'provisioner', 'debug', 'Starting workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('754568bf-a947-459e-abc5-03e37cb53b5f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.814+02', 'provisioner', 'debug', 'Starting workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bc0becf0-9184-4fb3-853a-d5e3d6be25f4', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.817+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0b62b0a7-de63-44b1-9a7f-a08210fa8283', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.82+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f3a49bd8-51e1-4a88-8a21-58cec8640e50', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.823+02', 'provisioner', 'info', 'Starting workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0863b4d2-84aa-43b1-8d52-a13e4ed91902', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.584+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8739d789-e629-4047-9e3a-499eae4c3d8a', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.588+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('790ba241-04b6-4dbc-a703-3aae9f28eae9', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.592+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=10c01a50-bec1-446c-b395-5d184fba369d]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1e2e80ff-f1d5-4285-9a4c-6dc918f024a2', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.595+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=2d72d32e-3021-4843-b582-d962fee897e2]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c8c6b94f-4a28-4e85-8c84-df60494cf98b', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.618+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f83f42ae-5671-49b4-9bb0-0fa535fc4d47', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.622+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9725ca37-c56a-4e25-ba87-3eda93cab7cc', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.624+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('73c7ed3c-7753-4823-b7ef-6826edfb7905', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.627+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ed4512d2-c3b6-4368-9291-0be933767557', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.632+02', 'provisioner', 'info', 'Starting workspace', 'Plan: 4 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('fc3e96db-5178-411a-8282-b8ee8e5db38f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.836+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b7a50c72-1b48-4c37-833d-76560b60bf4f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.841+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e87d2c28-3bd0-43ce-8b8a-6aa58264d7ab', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.844+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Creation complete after 0s [id=coder-oauthuser1-scheduled-root]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3741adca-df5c-41fb-9031-7c2e9e8a35bd', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.847+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Creation complete after 0s [id=8841874d-0c5c-49ab-ab10-2c6b8e509433]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1e2bf69c-236a-430d-8824-16b04daaee9b', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.853+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3f6087ff-301b-44c0-a892-88e5523048dc', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.858+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Creation complete after 0s [id=86ac90e2-cc81-41dc-a16e-eeca54c8a3c7]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('03e0d14e-d50e-4a91-a845-dd5140cea0b0', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.862+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0f701ad8-b85b-4515-aa10-426a9dd532a9', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:07.559+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creation complete after 1s [id=031ada2ead4766de08331f05ed2e5f0d674166517757edf1b924886b1359cab5]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7dcd63a2-1e80-4927-adca-1b19e6e3f7d4', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:07.57+02', 'provisioner', 'info', 'Starting workspace', 'Apply complete! Resources: 4 added, 0 changed, 0 destroyed.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('32909e4c-0dd2-43c2-b231-01314ae83bd8', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:07.578+02', 'provisioner', 'info', 'Starting workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ca4907f9-2158-4254-9a70-7366407b0789', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:08.667+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ec5ec4b3-49a7-4400-9085-3dadca04e656', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:52.934+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('97069c13-0085-41f7-b515-144630e3d10c', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:52.945+02', 'provisioner_daemon', 'info', 'Adding README.md...', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bc2dc6d3-e103-454e-a0ea-28e3ee516811', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:52.951+02', 'provisioner_daemon', 'info', 'Parse parameters', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('14bc3811-9fca-427e-8bca-bdf4124c8c42', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:52.965+02', 'provisioner_daemon', 'info', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f38628b1-2b19-4221-9a82-721b4064245a', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.058+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bae7a730-6853-41bf-aec3-df57021ef685', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.062+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Initializing the backend...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2cd32c99-b560-4029-9cfe-eddbb7b169b6', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.066+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('618f2905-ded6-4342-826b-230e3f1b07ac', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.069+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('fcd5133c-02a9-45d5-98f8-343c28c192b8', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.072+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('edc4a28a-1a05-4181-b523-3b7866ed47d0', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.214+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3abbd207-79d1-472e-b311-5d12cfa2adf0', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.311+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('055d02e9-70a8-458a-b63f-c9bf055f61b4', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.371+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0d385c88-0c06-4cd1-b69b-23690fa3c6dc', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.443+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('be40524d-3b27-402b-9253-9eb7afd8b3a7', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.447+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('186eacc1-237e-43ea-bf17-747f20277e80', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.45+02', 'provisioner', 'debug', 'Detecting persistent resources', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('72fea3ac-e12c-4222-9a65-23b303fbde51', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.453+02', 'provisioner', 'debug', 'Detecting persistent resources', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('a51ebdff-8f37-4a2a-9715-139db970054a', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.457+02', 'provisioner', 'debug', 'Detecting persistent resources', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2ed720c7-8872-46f1-aff2-d637b8ba8f47', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.462+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('72d48510-300f-4155-8ba7-7abeda40e411', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.466+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('234761af-f52a-4b87-9c91-80708bfb57df', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.469+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c1f61684-7229-459f-87d3-52f548675d1e', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.471+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4f76df82-b848-4225-82c1-5e8787258b24', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.475+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('20510fda-a6a1-4999-95db-cc3fb4458f5a', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.478+02', 'provisioner', 'debug', 'Detecting persistent resources', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('577517b0-be37-4fac-b9c8-a91840bfdf13', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.481+02', 'provisioner', 'debug', 'Detecting persistent resources', ' - coder/coder') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('eda778eb-66b5-436a-a576-e6793f41b04b', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.484+02', 'provisioner', 'debug', 'Detecting persistent resources', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b6ba43a9-188a-479c-8200-9dc1771558d3', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.486+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7341e996-a19b-4a5a-a969-f12137c06c99', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.49+02', 'provisioner', 'debug', 'Detecting persistent resources', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3618eb39-3cf5-421c-89be-5f5d8fb18050', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.493+02', 'provisioner', 'debug', 'Detecting persistent resources', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0c493997-4fea-4e34-9427-66a80b29713f', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.498+02', 'provisioner', 'debug', 'Detecting persistent resources', 'providers.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8f0eedec-76e5-45a7-96d6-0f6053dd2b7a', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.501+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3bcac134-e265-4cb4-b222-42239602b230', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.504+02', 'provisioner', 'debug', 'Detecting persistent resources', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5baf397f-d719-49e7-9c64-5868ab6867d3', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.507+02', 'provisioner', 'debug', 'Detecting persistent resources', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('11f29f58-7699-4825-9f19-044991e2bd72', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.509+02', 'provisioner', 'debug', 'Detecting persistent resources', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4290e889-0524-4a44-a863-50ca5c995dd6', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.512+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8673bef9-01b6-4012-85f0-405abd2bc018', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.514+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e61d6c01-774f-466f-80c5-d48d9d650659', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.517+02', 'provisioner', 'info', 'Detecting persistent resources', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5f1cb4d0-6333-40c9-a4f6-35a33707850e', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.242+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9349299d-5e9f-4392-a0a9-bf57b81a486e', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.247+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('03d69f7f-1ba0-4ff3-b165-57644ae74ea0', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.253+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_provisioner.me: Refresh complete after 0s [id=0c2a9b56-6788-426a-b51f-d1020b2f946c]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2b4f8569-69f4-484a-a0ba-274e7c508460', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.257+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_workspace.me: Refresh complete after 0s [id=f029c2d7-f293-4d65-95e5-fbbec8069da5]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2d426394-18e9-4eff-9b3f-7f8ca8212a0e', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.284+02', 'provisioner', 'info', 'Detecting persistent resources', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4f281678-1ff4-4a32-83bd-0357f02279dc', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.29+02', 'provisioner', 'info', 'Detecting persistent resources', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e0dde379-7983-41fc-ae3f-125adbb7e0a4', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.294+02', 'provisioner', 'info', 'Detecting persistent resources', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1359a82f-17d0-46c4-908b-a6623aec532a', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.299+02', 'provisioner', 'info', 'Detecting persistent resources', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('46b1948d-b07b-480f-9cb5-94dc56c76414', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.303+02', 'provisioner', 'info', 'Detecting persistent resources', 'Plan: 4 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c4a04254-ac4e-49b1-9922-18f00beb81f3', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.086+02', 'provisioner_daemon', 'info', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8ac6dfa1-dd6e-48c4-95c9-9d43a3f81fd1', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.169+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e833f596-ecf0-42aa-a4ab-544becc93b2f', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.173+02', 'provisioner', 'debug', 'Detecting ephemeral resources', 'Initializing the backend...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('53d42646-4d25-4f0a-a90a-2e7344952e04', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.306+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e58bd9b1-03dd-4650-a530-7264f1b77256', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.311+02', 'provisioner', 'debug', 'Detecting ephemeral resources', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('13f5bbd4-c399-4896-ad60-3a180a814ee9', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.314+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Reusing previous version of coder/coder from the dependency lock file') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4d5547c9-22e3-4dc2-a13d-ce958cad5d05', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.55+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Reusing previous version of kreuzwerker/docker from the dependency lock file') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('fc1acf12-6058-44c9-a7a3-18ec67538272', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.77+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Using previously-installed coder/coder v0.5.0') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('04728b40-6184-41fc-833c-e1b1299ce625', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.854+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Using previously-installed kreuzwerker/docker v2.20.3') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('501c8bae-4726-4365-9b94-0e20063d94c5', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.858+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e420f075-e982-4515-b45a-1b6c5aaf79cd', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.863+02', 'provisioner', 'debug', 'Detecting ephemeral resources', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6af54ded-f9ed-4bcd-ab31-f5b7d451cbc7', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.924+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6c0ffa8a-47ab-4d83-bcc3-63bd9dff7c51', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.669+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('aa2b789b-f85e-4131-8a31-a8125e4ebaf7', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.675+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bf3681f0-adaf-4466-ae0a-4251769947fc', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.678+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_workspace.me: Refresh complete after 0s [id=a56ece0a-5fd0-4377-beb4-b863a7b6f643]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('867acd6d-6459-4be3-8b7a-09c912f9a229', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.681+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_provisioner.me: Refresh complete after 0s [id=799aeb19-6a6c-4127-bc37-02bd4766cf5f]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5cd3d492-6611-4944-b0b4-87e27dab9376', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.704+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('bd8ba462-89c9-4a1d-af5a-35d245fe8fc1', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.708+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f50d9387-c97e-47a3-9958-2330462d7534', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.712+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('44b9aa93-fa16-47f6-92c5-a3e8f6ae2d5b', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.716+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'Plan: 3 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e267601b-0bab-4dcd-8cf2-01315983e35a', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:57.534+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c2f685fa-b49e-46b0-8b49-3499a6c86c54', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.441+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f0b836ba-dd49-42d6-9d2f-c765f676cef7', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.445+02', 'provisioner_daemon', 'info', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('365e78dd-d03f-413b-9ec2-c254106fc5d4', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.535+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('90b6d5a8-e690-4475-b05e-2c7d599bdb90', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.539+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f7024b37-d4dd-4c4a-9c64-aecff11a47ef', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.544+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('74bf20aa-8f5f-475a-991a-4baa61ac894a', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.547+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c9ef259a-f267-4afb-b401-c247ac3dd3f5', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.55+02', 'provisioner', 'debug', 'Starting workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6c6de8e1-0bed-4636-8966-502b7adcf530', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.705+02', 'provisioner', 'debug', 'Starting workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('a017a4e0-cbbf-4ea4-bf91-9936ead5d649', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.771+02', 'provisioner', 'debug', 'Starting workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7f2577aa-cab9-4e9c-82b1-e2b68f1de520', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.845+02', 'provisioner', 'debug', 'Starting workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5bbb9cf4-69a1-4e9f-87fb-7ddd6bf41323', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.901+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('49732ed2-1526-410a-844a-27fd51afa8b8', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.906+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3ca5f41e-936e-41cf-8ec6-9a322d73102d', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.91+02', 'provisioner', 'debug', 'Starting workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('a11b20a0-5b00-4323-a949-356013d91119', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.913+02', 'provisioner', 'debug', 'Starting workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8a2e242c-d977-4677-8f6c-e0d264d834e8', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.915+02', 'provisioner', 'debug', 'Starting workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ad719a38-a50b-4623-a0e1-a47e04d7827f', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.918+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2916ea33-9304-42c9-ae7d-2b965c202eb0', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.923+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('776a479c-8cfa-419d-87c6-9c3ad8a3bd09', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.926+02', 'provisioner', 'debug', 'Starting workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b3a16e4b-86a2-4d4c-8cf4-dc3488c636b9', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.929+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1756b015-3863-46a0-8ce1-756a3db0ab0d', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.932+02', 'provisioner', 'debug', 'Starting workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('dfff1eea-4022-4685-8fe9-1dedf12b1bfa', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.935+02', 'provisioner', 'debug', 'Starting workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8bfdd0f9-59f8-452b-a5f0-94cf72ee53dd', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.938+02', 'provisioner', 'debug', 'Starting workspace', ' - coder/coder') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('81e3e47b-bcdc-42f8-9c23-4e3224657961', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.941+02', 'provisioner', 'debug', 'Starting workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0dd5456e-727f-4820-801d-a73196a99db5', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.944+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d1017c48-e1e1-4f6b-85b7-c432ec0c848c', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.947+02', 'provisioner', 'debug', 'Starting workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('32f30546-bb7a-4b25-893a-7740d5028720', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.95+02', 'provisioner', 'debug', 'Starting workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0a9cbe99-0bfc-45a2-a714-91c0fab6959f', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.953+02', 'provisioner', 'debug', 'Starting workspace', 'providers.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('884b86f8-d32c-4961-a6d2-b74cecc9ef94', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.956+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('07a49cf9-2088-4977-930e-d7019c7fadd8', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.959+02', 'provisioner', 'debug', 'Starting workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0104d747-0c30-40ea-bc37-99d1c5d828b1', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.962+02', 'provisioner', 'debug', 'Starting workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('14d0e06a-920e-48f7-862f-21b26533c2d4', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.964+02', 'provisioner', 'debug', 'Starting workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9327e992-19d6-485f-a783-f069195b39bb', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.966+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e566f0e1-5000-4328-bb26-503933a6cf58', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.969+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('a622c794-e3de-4d05-ad21-7d15206a8134', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.972+02', 'provisioner', 'info', 'Starting workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2c3e911a-0120-4ef9-8bf1-ae38b335f4c7', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.685+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1700402d-473b-4545-ac26-e8c2474ae61e', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.69+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('677e7e30-e7b0-414d-950c-691ece713410', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.698+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=ad27e546-76e2-44a1-befc-04e736c7d136]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('de33d989-b4a0-4aca-bdcc-2f709c89badf', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.702+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=b90547be-8870-4d68-8184-e8b2242b7c01]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ae71133b-83ea-4594-938a-619b65a72dbc', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.708+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Refreshing state... [id=6137599d-4ad6-4e58-b0fa-a3d1a88fa5a9]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2c4d3f77-57ab-444e-bcf2-47c0d334071d', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.713+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Refresh complete [id=6137599d-4ad6-4e58-b0fa-a3d1a88fa5a9]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('08e41e5a-92cc-4593-9a76-fe7aedb89136', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.716+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Refreshing state... [id=3a585bc7-75e6-408e-b0bc-dc64e4d3be0a]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b6e2db47-d7ba-421b-bb19-8efb34e15041', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.718+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Refresh complete [id=3a585bc7-75e6-408e-b0bc-dc64e4d3be0a]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('da66adc5-7955-4efa-ae4e-921d89a0a905', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.721+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Refreshing state... [id=coder-oauthuser1-test1-root]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('86b81dc3-345e-4032-91ca-e179d2f68426', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.724+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Refresh complete [id=coder-oauthuser1-test1-root]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c16e6dd4-8590-4267-8d86-86469c58bd38', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.727+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Refreshing state... [id=eed96dcc159829b9a91ee13a2106b3f8598896f99d7142765276914e628e8109]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f029d62b-fa15-41dd-a55b-56de56aa9d2d', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.845+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Refresh complete [id=eed96dcc159829b9a91ee13a2106b3f8598896f99d7142765276914e628e8109]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d420d06e-a470-491f-b77a-76e9efcfd885', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.88+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Drift detected (update)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f29b2f5a-5b30-4450-808d-57633ec9eee1', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.884+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Drift detected (update)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('dd638547-02bd-4f94-8e3a-706d8c4bbb4d', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.887+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Drift detected (update)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9733a541-f554-45fc-b9e6-8449d0a7268d', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.891+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Plan to replace') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1442786e-4ac2-4a31-bf01-62f71ee1c58f', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.895+02', 'provisioner', 'info', 'Starting workspace', 'Plan: 1 to add, 0 to change, 1 to destroy.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b73e9a9c-2a80-4edc-b9c9-caed1f8fb76f', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:09.092+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Destroying... [id=eed96dcc159829b9a91ee13a2106b3f8598896f99d7142765276914e628e8109]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('26bb15bd-55f5-40af-bb5f-68276cdc547c', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:09.315+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Destruction complete after 0s') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e8dd9645-1e6c-4620-a2b4-db13e3591672', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:09.334+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creating...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('27a857d3-fae3-4e7b-92f3-ba66651415fd', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:09.838+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creation complete after 1s [id=86d859b12b6519160d53b99d4f0155bf6aeeefbd33d88fe93e2eecef22eb65de]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6b755e4e-7e2e-407d-874e-2b9d94636d1f', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:09.846+02', 'provisioner', 'info', 'Starting workspace', 'Apply complete! Resources: 1 added, 0 changed, 1 destroyed.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d7858aaa-6fa7-4673-9ff2-1b2347088c64', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:09.853+02', 'provisioner', 'info', 'Starting workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('43b1ff4e-b1d2-45bf-aced-0d209ae20ae6', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:10.807+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('567776bf-1821-4c52-80ed-3fce709d0c85', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:51.94+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('36b8be4a-a30c-4de0-8896-14406d6cfdb6', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:51.949+02', 'provisioner_daemon', 'info', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('80f21094-54a2-4756-a837-25df3ddc10bd', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.042+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e4a9be07-5e4a-402c-a32c-5d2be759063f', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.047+02', 'provisioner', 'debug', 'Stopping workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9b30655a-31d0-4cb5-a2d1-c55de3a4f7db', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.051+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b66c4596-da28-4f0c-857d-d22ec01a659e', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.054+02', 'provisioner', 'debug', 'Stopping workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f78aef35-c5b0-4ca1-bba3-1ed73c00d2f5', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.057+02', 'provisioner', 'debug', 'Stopping workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5128de3f-c94a-498d-aac0-690fb3a3256c', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.203+02', 'provisioner', 'debug', 'Stopping workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('cb707f76-a1ce-4411-998f-492ab315f9de', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.276+02', 'provisioner', 'debug', 'Stopping workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b6ba27e3-22c5-4585-ab89-9a122daa2ed2', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.352+02', 'provisioner', 'debug', 'Stopping workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('155e3a4d-e37a-4443-9609-94ea25793182', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.407+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('803c0af2-6bee-4dd5-bca7-088ccef48d0d', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.411+02', 'provisioner', 'debug', 'Stopping workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b045cf9c-4a31-42df-a157-36799cc1505e', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.415+02', 'provisioner', 'debug', 'Stopping workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('96af3253-66fd-4e6f-8a02-9aaa2ed840e1', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.419+02', 'provisioner', 'debug', 'Stopping workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('a577318f-cd80-46e2-9dc2-e96ddb958fb6', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.423+02', 'provisioner', 'debug', 'Stopping workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('918e8d10-6929-43ff-a417-e835a05593b5', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.426+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('82a34d3a-ff5d-42a4-9734-c369e56ce5c8', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.428+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f7cb10b8-9c51-4f23-afbe-33b20842feef', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.434+02', 'provisioner', 'debug', 'Stopping workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('77119d73-aa08-4d6a-b2a9-5a876cc38f10', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.438+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('77ba3339-32b2-4c0c-9dab-347a75457e1b', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.44+02', 'provisioner', 'debug', 'Stopping workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('378e1d98-148e-4ba4-861e-9eba8d7f1f79', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.444+02', 'provisioner', 'debug', 'Stopping workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('305ed411-809f-47e8-b5fc-36ab80d51862', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.446+02', 'provisioner', 'debug', 'Stopping workspace', ' - coder/coder') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('b2b3030f-f923-4c11-a0b5-3f88673f174c', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.45+02', 'provisioner', 'debug', 'Stopping workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('72495cac-fb10-4a5b-ba56-e5720fdcbe80', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.452+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('aa63292e-da72-4ad2-837b-4b7d09ca8352', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.455+02', 'provisioner', 'debug', 'Stopping workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7d402a33-e901-481a-a4b0-7975bc710884', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.457+02', 'provisioner', 'debug', 'Stopping workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('26f48c94-98cd-4ab4-a7c9-92b51c559e50', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.46+02', 'provisioner', 'debug', 'Stopping workspace', 'providers.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('425de025-c374-4359-bb84-36d08b1f045f', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.463+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f0a06046-0ed0-4d43-8608-8ede7c923a9f', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.466+02', 'provisioner', 'debug', 'Stopping workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c8eae175-6b93-44d7-86d9-acd10e5d9257', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.469+02', 'provisioner', 'debug', 'Stopping workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('a186dc9b-c58c-48ed-90e8-5ff3d4049dac', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.471+02', 'provisioner', 'debug', 'Stopping workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f47f6907-04cd-4ada-b7df-7bb27032d429', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.474+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('237bad3c-59c9-4d7e-9860-922c85106edb', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.476+02', 'provisioner', 'debug', 'Stopping workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0aeca511-4a7b-4151-a806-525e03e842db', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.479+02', 'provisioner', 'info', 'Stopping workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('66abfda2-7009-4d13-bb84-24ab857b3404', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.226+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e1073aaf-e53a-4e28-a771-66a273731128', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.231+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d388445e-b43e-4f8f-a866-66639edcb087', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.236+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=b90547be-8870-4d68-8184-e8b2242b7c01]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1726c68b-dad7-4cbc-bf24-6a6578f50edd', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.244+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=58ea2d09-993b-4754-8c7d-0c0ec944e2ee]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0331dcb4-8497-4b49-b823-be547e2fb19e', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.248+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Refreshing state... [id=6137599d-4ad6-4e58-b0fa-a3d1a88fa5a9]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('668a90d0-12e9-4aea-af6f-5c50324ebb1d', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.252+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Refreshing state... [id=coder-oauthuser1-test1-root]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('656ab85b-4184-494e-8a57-891b3343bacb', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.254+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Refresh complete [id=6137599d-4ad6-4e58-b0fa-a3d1a88fa5a9]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e521fb3e-c3d3-4e07-b7d5-d23a14735f3d', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.257+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Refresh complete [id=coder-oauthuser1-test1-root]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8b6e20ee-cd76-44f0-a361-24e3b91ca966', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.261+02', 'provisioner', 'info', 'Stopping workspace', 'coder_app.code-server: Refreshing state... [id=3a585bc7-75e6-408e-b0bc-dc64e4d3be0a]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7a5cb85d-85d5-4e4d-9bac-6b71b17a31a7', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.263+02', 'provisioner', 'info', 'Stopping workspace', 'coder_app.code-server: Refresh complete [id=3a585bc7-75e6-408e-b0bc-dc64e4d3be0a]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('61a50656-1299-4920-8cfe-a3d496fb7601', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.266+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Refreshing state... [id=86d859b12b6519160d53b99d4f0155bf6aeeefbd33d88fe93e2eecef22eb65de]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c748eb20-3d36-4dd1-9d67-94f187845e60', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.377+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Refresh complete [id=86d859b12b6519160d53b99d4f0155bf6aeeefbd33d88fe93e2eecef22eb65de]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('11300863-799e-4a8d-9d4e-e816fe59f8d0', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.388+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Drift detected (update)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('148f401b-202f-495d-a00d-3edaf2d98b54', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.392+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Drift detected (update)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('518cc0ce-b6fa-46af-be96-7eb0745832c9', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.395+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Plan to delete') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('abdc6fea-3ecd-444c-b0b9-a0c5c7b04bd5', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.399+02', 'provisioner', 'info', 'Stopping workspace', 'Plan: 0 to add, 0 to change, 1 to destroy.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('098266ee-37b3-423b-a915-1eebed4a9382', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.51+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Destroying... [id=86d859b12b6519160d53b99d4f0155bf6aeeefbd33d88fe93e2eecef22eb65de]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1dd3c1b5-aee2-4c3c-a43b-98abf577daee', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.752+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Destruction complete after 0s') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('785540dd-ba37-4111-900a-e63fe74c80c3', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.763+02', 'provisioner', 'info', 'Stopping workspace', 'Apply complete! Resources: 0 added, 0 changed, 1 destroyed.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('1dd9d93f-3497-4fd4-acd5-676807a332a6', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.769+02', 'provisioner', 'info', 'Stopping workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ae1db4e6-6f61-4e96-86a4-90869cdfd4eb', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:54.754+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('5ff779e3-9dd5-48a0-a5f3-a7a80d029807', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.44+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('59a25c28-c558-4571-b853-bee95d5fb22b', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.444+02', 'provisioner_daemon', 'info', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('9a497a9b-63aa-4424-bcc8-92ffc1dd1259', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.534+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('672df058-dbd9-4eb2-ab8e-21033b6104c7', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.538+02', 'provisioner', 'debug', 'Stopping workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2c9dccb2-41d0-40bf-bcc4-04d77075474f', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.542+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('4168300f-5094-4235-91b4-6b3b4777f3ef', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.546+02', 'provisioner', 'debug', 'Stopping workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('12c537a5-d863-4fe3-8b04-9ba93d749a64', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.55+02', 'provisioner', 'debug', 'Stopping workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e9be6d15-d900-47a1-a2af-9f1f51b3644c', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.156+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0a588339-f93c-4a54-9655-59e4fd30f49e', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.821+02', 'provisioner', 'debug', 'Stopping workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0b21faa5-29a9-4b78-8a5b-2afd05d250eb', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.027+02', 'provisioner', 'debug', 'Stopping workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('47176b70-e029-4b88-9ba7-4b19781da315', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.086+02', 'provisioner', 'debug', 'Stopping workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('eb7b92a8-fdcc-475b-88da-8b2e1c4c7a57', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.16+02', 'provisioner', 'debug', 'Stopping workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c7b928c0-f6fb-4d4d-836a-8ff389bf8aad', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.163+02', 'provisioner', 'debug', 'Stopping workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('ed8116ca-5692-48ba-bc64-359bec1f54a6', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.166+02', 'provisioner', 'debug', 'Stopping workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('19e91c08-0342-4c21-8a23-8690b567a3bd', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.168+02', 'provisioner', 'debug', 'Stopping workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c1144133-37fa-4715-9e1d-4301287ef9e7', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.171+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f454b157-5e4d-45ac-83a6-aaceb242af1c', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.173+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('78ef1dfb-8de7-4c09-9775-9ac2b0c8fccb', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.176+02', 'provisioner', 'debug', 'Stopping workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e17bda0e-e339-4f28-87ad-af15ff5103c3', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.179+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6ae8b6b5-7817-414d-bf79-dd6f78a9db26', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.182+02', 'provisioner', 'debug', 'Stopping workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e3e90d48-336f-4d41-a90b-cc569633e5d4', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.185+02', 'provisioner', 'debug', 'Stopping workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('615327b8-8a02-4afc-a6be-a4e2d0758ebf', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.188+02', 'provisioner', 'debug', 'Stopping workspace', ' - coder/coder') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c30bc373-2667-42e7-aed5-bac2907c1813', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.19+02', 'provisioner', 'debug', 'Stopping workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c7a73bc4-1d99-4230-89c5-be15c70db5e3', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.195+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('fa365813-ac49-4cb7-aaeb-a9344156b73a', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.198+02', 'provisioner', 'debug', 'Stopping workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('e501ee90-28bd-4d20-ad99-12a09fe8287e', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.201+02', 'provisioner', 'debug', 'Stopping workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('f4708ad7-013b-4149-ba57-8d30f8198142', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.203+02', 'provisioner', 'debug', 'Stopping workspace', 'providers.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('fa4f0229-187c-4654-aff8-6cee50c6720e', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.206+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0ccf37bd-877e-464c-9931-97564752f445', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.21+02', 'provisioner', 'debug', 'Stopping workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('64242d19-9374-45e3-a46c-17833470e20d', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.212+02', 'provisioner', 'debug', 'Stopping workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('027cb177-34fd-4137-91a8-f0b63c0ae8f1', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.216+02', 'provisioner', 'debug', 'Stopping workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('fd28c560-d002-4f4d-947e-75a9dd98ae71', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.218+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('35ad66c7-fd53-49a8-8a08-993abb9dc471', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.22+02', 'provisioner', 'debug', 'Stopping workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('2d4d5e8c-2eea-42ba-8aea-2f250af13f8f', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.222+02', 'provisioner', 'info', 'Stopping workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('cf9cb3ab-8af9-4b34-8500-02979c138c4e', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.952+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('8537a20e-b447-423e-8f68-24b53582c88c', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.957+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('989116f5-b114-4df4-9b4f-62417b12cdd5', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.962+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=2d72d32e-3021-4843-b582-d962fee897e2]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('33440676-d562-4284-8496-e9be767976de', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.965+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=a9bb9c27-a53b-4524-821b-64d3bf2da47c]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('3ea2e134-018b-4291-b9ca-d7db05a6a127', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.969+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Refreshing state... [id=coder-oauthuser1-scheduled-root]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('45d2a1e5-73a5-423f-a0bb-43a4fc49f457', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.972+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Refreshing state... [id=8841874d-0c5c-49ab-ab10-2c6b8e509433]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('71910cc2-83f8-4507-856b-7a95de0ec6f8', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.975+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Refresh complete [id=coder-oauthuser1-scheduled-root]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('96f27f81-0b5c-49b0-9b69-18ab9bd71d74', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.978+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Refresh complete [id=8841874d-0c5c-49ab-ab10-2c6b8e509433]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('6f7ec6ad-1f57-44b9-b1d3-d23e26ceca6e', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.982+02', 'provisioner', 'info', 'Stopping workspace', 'coder_app.code-server: Refreshing state... [id=86ac90e2-cc81-41dc-a16e-eeca54c8a3c7]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('13d86e02-e7ab-4390-a80b-dadee7fa8146', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.985+02', 'provisioner', 'info', 'Stopping workspace', 'coder_app.code-server: Refresh complete [id=86ac90e2-cc81-41dc-a16e-eeca54c8a3c7]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('214603da-529f-42e5-964d-77e91c92940f', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.99+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Refreshing state... [id=031ada2ead4766de08331f05ed2e5f0d674166517757edf1b924886b1359cab5]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('52409cc8-c6ce-4d8d-8c53-dc3e3734ae17', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.11+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Refresh complete [id=031ada2ead4766de08331f05ed2e5f0d674166517757edf1b924886b1359cab5]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('957979e1-7611-49f0-8756-d7e2a4eaffee', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.125+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Drift detected (update)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('d364a7e6-249c-4fd9-a041-3854b03388f9', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.13+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Drift detected (update)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('27daf314-7f00-435d-8fad-f037882392bb', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.134+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Drift detected (update)') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('736b42be-7b2a-4f91-8676-37e12f2ca6f3', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.137+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Plan to delete') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('0bcd3cbc-916b-417e-96c2-afb5e83c9f3c', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.14+02', 'provisioner', 'info', 'Stopping workspace', 'Plan: 0 to add, 0 to change, 1 to destroy.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('deba29f1-462d-4e70-a6e7-ca2cb8ac5b70', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.257+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Destroying... [id=031ada2ead4766de08331f05ed2e5f0d674166517757edf1b924886b1359cab5]') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('118af4d6-67f3-4906-9585-4b4f1cdefc57', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.579+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Destruction complete after 1s') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('c257a4c2-83f7-45b4-ae42-652a93547b51', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.592+02', 'provisioner', 'info', 'Stopping workspace', 'Apply complete! Resources: 0 added, 0 changed, 1 destroyed.') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('825b93e0-a4fb-4807-9570-2190eec03ba7', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.595+02', 'provisioner', 'info', 'Stopping workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; -INSERT INTO public.provisioner_job_logs VALUES ('7ebd786a-4c6b-4d81-b66e-c70912ac2036', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:45.612+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d09b6083-e482-41ac-ad06-3aa731ec4fc6', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:31.936+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('fe61a03b-d5b2-4ba4-a437-253ea724d149', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:31.943+02', 'provisioner_daemon', 'info', 'Adding README.md...', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1badda17-67b2-4795-9351-6a2cee04c22a', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:31.949+02', 'provisioner_daemon', 'info', 'Parse parameters', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('80b05e59-1fdb-48cf-99ed-27ed0aba8ecc', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:31.956+02', 'provisioner_daemon', 'info', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4a5c2ec6-c2e2-4136-9423-14cdff1d6f13', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:32.046+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7da3c668-9812-47b7-9d22-43138800a307', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:32.05+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Initializing the backend...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2f752172-65f0-4a43-9378-c5d0997eca76', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:32.054+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f6c293ae-a496-40c2-ab8e-b7d64aca4bdb', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:32.057+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('15948e16-e69b-47c2-821e-6c0b375dccd8', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:32.059+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('62d872e4-9a8c-4cc5-9f21-083bfcb6a03c', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:32.28+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bce64698-1b64-4ef6-94b7-b5fd071b31e8', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:33.826+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Installing coder/coder v0.5.0...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('35c436dd-3beb-4c5d-9f8a-16198d2fbcb7', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:36.186+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Installed coder/coder v0.5.0 (self-signed, key ID 93C75807601AA0EC)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('aa80ec91-8b8f-4268-88a4-111e5b368bf9', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:37.718+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Installing kreuzwerker/docker v2.20.3...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e322b727-fddf-4d31-b0ad-fe9fb0532586', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:40.978+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Installed kreuzwerker/docker v2.20.3 (self-signed, key ID BD080C4571C6104C)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('26cac587-a5c7-4691-bab4-0fa1436724c3', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:40.984+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b192bac6-94a0-4e89-8d88-eba6c1028abe', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:40.988+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Partner and community providers are signed by their developers.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('57162911-66a9-4597-a289-643724057e2d', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:40.992+02', 'provisioner', 'debug', 'Detecting persistent resources', 'If you''d like to know more about provider signing, you can read about it here:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('19227832-c872-4d8d-b610-c649da56984e', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:40.996+02', 'provisioner', 'debug', 'Detecting persistent resources', 'https://www.terraform.io/docs/cli/plugins/signing.html') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('dce2aa32-5dbd-4c04-af85-c7ef585dca6a', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6e220907-0654-46dc-bb56-212153032d5e', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.003+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8da2943d-7295-43a6-b560-e946c127b0a0', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.006+02', 'provisioner', 'debug', 'Detecting persistent resources', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('79b02372-6db4-4cd6-8c69-c94e9ef2fb5b', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.009+02', 'provisioner', 'debug', 'Detecting persistent resources', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4b810053-fde0-4579-b2fa-f3a80d9f3f5f', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.012+02', 'provisioner', 'debug', 'Detecting persistent resources', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('76a7934d-b410-4ccc-a57e-1329aefe0203', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.015+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('133aa0b6-4da8-48e4-9428-62f6b7635510', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.018+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('a0ed73d5-e07a-4b3a-8046-8fdaefc1dacb', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.027+02', 'provisioner', 'info', 'Detecting persistent resources', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8b67db19-e012-461d-a538-d1dea4f91ef4', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.781+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('60637ec5-5231-4407-b62d-13e44074eb7f', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.784+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c3ed554e-b395-4adf-8642-f7596a71c4cc', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.787+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_provisioner.me: Refresh complete after 0s [id=20990c34-2afd-4e54-bfa3-9b7e5beea09e]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8b988379-c1e4-442b-9232-65fd926eecff', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.791+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_workspace.me: Refresh complete after 0s [id=fc8ca501-3820-427d-92c5-d8ca4fd45362]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9a4e58db-512e-42db-9349-29ebef3c750f', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.816+02', 'provisioner', 'info', 'Detecting persistent resources', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('45f82833-a53c-49ca-be8e-1545917d7062', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.82+02', 'provisioner', 'info', 'Detecting persistent resources', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5c0eb2ac-825d-4ad8-bed8-63d59266030e', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.823+02', 'provisioner', 'info', 'Detecting persistent resources', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9fd0dc4c-e75d-47ac-9676-2e4182da7edd', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.826+02', 'provisioner', 'info', 'Detecting persistent resources', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b9a391a5-117f-45e0-9a2c-89b13c32d201', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:41.829+02', 'provisioner', 'info', 'Detecting persistent resources', 'Plan: 4 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7470c318-e457-4b2c-b61d-4fbab6f84fc3', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:42.617+02', 'provisioner_daemon', 'info', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('975d23d1-aa43-4efc-b533-55a07e8cc3bf', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:42.705+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f4cad918-2fb0-46a2-b64a-daa899b659fe', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:42.708+02', 'provisioner', 'debug', 'Detecting ephemeral resources', 'Initializing the backend...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('dc6331d7-41a8-4f65-8a89-da54638b5aa0', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:42.846+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ca307075-af47-4fa5-9d3b-4744acc7a0ac', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:42.851+02', 'provisioner', 'debug', 'Detecting ephemeral resources', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('61df6634-b81b-4fde-b7cb-13ed3f97b210', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:42.855+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Reusing previous version of coder/coder from the dependency lock file') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9f227da4-3fa0-4d7d-a65e-38ccf06bba2f', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:43.06+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Reusing previous version of kreuzwerker/docker from the dependency lock file') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e79c1b3c-5db1-48fd-ac61-3fb667e3fc97', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:43.319+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Using previously-installed coder/coder v0.5.0') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('28ec8811-7aaa-460c-b6e2-a9b85eb46b4b', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:43.405+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Using previously-installed kreuzwerker/docker v2.20.3') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('cef26c13-8a06-4ae6-946f-216db5cc0996', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:43.411+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('fe2bc9eb-b7ea-4d20-8d7a-b2be1d3c450a', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:43.415+02', 'provisioner', 'debug', 'Detecting ephemeral resources', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d49391ed-7b60-459e-8fe5-4dd4783f6629', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:43.45+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('25f8cf7f-4cee-4891-9f14-4e32002e6661', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.179+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ca6ad386-5647-49e3-9b05-0f4cdfd35b01', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.184+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('aece424c-595e-4ad8-a1cc-0938d037a3cb', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.187+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_provisioner.me: Refresh complete after 0s [id=ccf0bb8b-fca5-408f-b4a6-93434185c25a]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f345b9bd-429e-468f-aea4-8c11709991da', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.19+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_workspace.me: Refresh complete after 0s [id=5e7ca627-de78-4542-a964-acda3f038ced]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('31b0cf2d-4a8b-4860-86d7-ec9fc5e58abc', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.208+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('602d137a-eaad-4b34-9a5a-e2169adf1b17', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.212+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d5cc54cf-0390-4fa7-9ff5-3bcd15cbe351', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.216+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('a2f6c1a8-05ff-45a8-ac0e-490f7dc0b7b1', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:45.056+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2dbe9812-907b-4344-b602-14c680fd460c', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', '2022-11-02 13:03:44.222+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'Plan: 3 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('377e167e-4133-40d8-b62b-e33369c5ed55', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.437+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6d970dad-4051-42d8-847c-07320fb9b110', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.525+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('501f4bab-6eb3-4278-9192-7ae725943418', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.528+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Initializing the backend...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('80e17591-51fe-4fda-aa90-18caa59d1b7d', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.533+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bdd4192d-9f9d-4300-8b4e-f1f12a8479a8', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.536+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b46e6002-1277-4d2b-88a6-97f2f250766e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:11.542+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b251b96c-368b-4be2-982e-621372dd1f0e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.072+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('294bdb11-4229-46a0-a926-34590373e945', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.155+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('70eab552-5fe7-491a-a275-22059e959ccb', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.212+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c32727e1-ca44-405a-9419-038107c4bc96', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.285+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9e3ae09c-09bc-4a27-b271-9446198fb60e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.293+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9d2d3770-f969-4796-9d65-6cc903593d52', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.299+02', 'provisioner', 'debug', 'Detecting persistent resources', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4fdc748e-938c-4809-9125-caae1c44fd74', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.302+02', 'provisioner', 'debug', 'Detecting persistent resources', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('57b70064-827e-4763-b8a0-473f99797b48', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.306+02', 'provisioner', 'debug', 'Detecting persistent resources', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('781a7128-f401-48e3-a752-4f5d3c636ead', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.309+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('71dbe7ee-7537-4cf3-8f33-8c309ebe8b7e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.313+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('360f1a19-7904-440a-a539-6f035169d093', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.317+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('df6ba6d2-fada-4b68-a8a6-978fdc7b55e2', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.32+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bfc96ab3-fa05-416f-b21e-c58a3ab123f2', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.323+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('44a12cb2-da79-469f-b971-c4bf32fe406e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.327+02', 'provisioner', 'debug', 'Detecting persistent resources', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('299f3930-0423-4e75-ae4a-526e37f481c5', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.33+02', 'provisioner', 'debug', 'Detecting persistent resources', ' - coder/coder') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b6bffd24-8365-4a00-98ed-1e1f978aca49', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.333+02', 'provisioner', 'debug', 'Detecting persistent resources', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0422f763-da4f-4552-9562-144927bec94d', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.336+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('aab83530-7dcd-49bd-b981-5c29cd9674d3', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.339+02', 'provisioner', 'debug', 'Detecting persistent resources', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3089607c-329c-4ee7-88dc-e9f48318e52f', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.342+02', 'provisioner', 'debug', 'Detecting persistent resources', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('a20e18c7-daa9-4e40-9ed9-9c11a2ccf5b1', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.346+02', 'provisioner', 'debug', 'Detecting persistent resources', 'providers.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('582dbda6-f79c-43d5-b215-2da3b13bd406', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.351+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b6cf795f-35e7-45ee-98d0-8be04d7ad6fe', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.356+02', 'provisioner', 'debug', 'Detecting persistent resources', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f36be7f7-a6c2-40d1-9722-ae8e4d920a08', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.359+02', 'provisioner', 'debug', 'Detecting persistent resources', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3c03dd84-79e1-4abf-9900-05613cc82664', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.363+02', 'provisioner', 'debug', 'Detecting persistent resources', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4cbcab55-cb60-4982-90f7-be05ffac7566', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.366+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3d553c50-de3b-4770-8bed-2792c6981362', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.369+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1137030a-bfda-4391-8b17-6a1c1a5e2749', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:12.373+02', 'provisioner', 'info', 'Detecting persistent resources', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bc462919-aa4b-452c-b945-35965cf138c5', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.157+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c76057c5-2191-4336-b7fb-f104d3c4054b', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.162+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5a1771fc-0028-4edb-9bdb-86dcd4c58687', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.165+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_workspace.me: Refresh complete after 0s [id=13dbb32a-5197-458d-8092-6f00a7df5d19]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f29c2e5d-3392-43c3-ae80-1deeea71f11a', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.169+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_provisioner.me: Refresh complete after 0s [id=2ab63b5c-7d85-43db-ae90-cdcdd27ca4d6]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f3eb65ac-2be8-4b88-9bb8-618bfd70561b', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.201+02', 'provisioner', 'info', 'Detecting persistent resources', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('739ec94c-4700-4562-80f7-7f254aa8b129', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.205+02', 'provisioner', 'info', 'Detecting persistent resources', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f01666b7-4de4-451f-8710-8aa63d32aa8e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.209+02', 'provisioner', 'info', 'Detecting persistent resources', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('556f3f57-5a46-49da-aac4-66ed5d101e7f', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.213+02', 'provisioner', 'info', 'Detecting persistent resources', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('55d5e4f7-e80a-4622-9168-7e412651b91e', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:13.216+02', 'provisioner', 'info', 'Detecting persistent resources', 'Plan: 4 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('664fe6c8-1fec-410c-93d8-385e8b65e7d4', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', '2022-11-02 13:04:14.076+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1af37305-2e9f-445d-b3fb-dea1dece403b', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.436+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5da10ab9-a23c-44ea-98bb-865dc706eae2', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.44+02', 'provisioner_daemon', 'info', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7d8d1b95-2660-4724-a2e5-8c0439eff2e7', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.534+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('89f7bb3f-c495-4567-a188-25c5b41f6e5d', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.538+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('050649c2-fa47-407a-b871-b9f9a8a48b49', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.543+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7d7e19cb-238c-4293-a488-5f61fe83d9d9', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.548+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('daf99de4-753e-4a40-a412-385a97ffe655', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.551+02', 'provisioner', 'debug', 'Starting workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b10105f1-a4ca-473e-a33d-a6755709ac7d', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.776+02', 'provisioner', 'debug', 'Starting workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d974959e-0c77-4c38-8302-dbdea5f8b702', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:19.966+02', 'provisioner', 'debug', 'Starting workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3d0f0030-8fdb-4193-9f71-e7648c0b00ca', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.023+02', 'provisioner', 'debug', 'Starting workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5e93f614-bd73-4fd7-bf0d-85c61e8294dd', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.095+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('160b7134-0cd1-4c28-b05a-ac7fa6d5d05e', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.098+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7b4b1ca6-fe48-4f1b-bd9b-b6ab54b3cccd', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.086+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8a234d62-5d70-450d-ae9e-fd46186a7717', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.102+02', 'provisioner', 'debug', 'Starting workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3872d0e7-f5fd-45f9-9027-ec41eacdbc8f', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.106+02', 'provisioner', 'debug', 'Starting workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('02eb7d16-8513-4ac8-af9b-bd096faccbe0', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.109+02', 'provisioner', 'debug', 'Starting workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('afefbc98-b47e-4c32-99cc-a2cbe7066d43', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.112+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('785eef1d-66ce-406e-bf12-5e2e95ffdf4a', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.115+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('cb782ff3-5ef8-4312-9431-8a44aed28d9d', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.118+02', 'provisioner', 'debug', 'Starting workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('02003fe3-ac96-4cdc-8683-5042aff7ad81', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.122+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2f203901-94d5-4e9d-8988-d06224e9fa0b', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.125+02', 'provisioner', 'debug', 'Starting workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9d53f365-961a-43df-8a0e-b974649183aa', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.129+02', 'provisioner', 'debug', 'Starting workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3dd1ed46-1de4-4362-bfff-784182b44471', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.132+02', 'provisioner', 'debug', 'Starting workspace', ' - coder/coder') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c03469d0-08a9-4378-b538-50d6d1d50c79', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.135+02', 'provisioner', 'debug', 'Starting workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c6166453-3033-4d69-a5f0-4856776c76e4', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.138+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('fa7a0d2f-1bd2-46e9-9f25-8bd45860dd98', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.141+02', 'provisioner', 'debug', 'Starting workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('a1fd6072-dde6-420c-bf8e-9ebc4d537866', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.145+02', 'provisioner', 'debug', 'Starting workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('cae1268e-b797-4e97-83cb-95ff016a5a63', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.147+02', 'provisioner', 'debug', 'Starting workspace', 'providers.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('938f0d99-4dc6-42a1-b6b9-c9e70b450cd5', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.15+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3d9d2115-f6ea-40c2-9f56-5075cada0c34', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.152+02', 'provisioner', 'debug', 'Starting workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('46e2af39-78f2-4a37-824a-0b4f2dc54764', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.157+02', 'provisioner', 'debug', 'Starting workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1ae80356-6b65-4ff6-8230-7cab8a8cfaf9', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.161+02', 'provisioner', 'debug', 'Starting workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('386b59ce-c30e-4f7d-addc-178e223b1d87', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.164+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b18f40d1-f649-4f34-b332-feb6690482da', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.168+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0747c356-f2ea-49ab-b7ba-292ed9abff7a', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.171+02', 'provisioner', 'info', 'Starting workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('da54beff-b031-436d-93f0-67f2ff53be96', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.94+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('78007aad-5614-4d0e-9fa9-ae1307b6240f', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.946+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3159380d-141a-4d2c-9a69-cbc90c5fe71b', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.951+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=3a9a1feb-e89d-457c-9d53-ac751b198ebe]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6e6a0df4-feb4-4339-87ce-ccf48ff873db', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.956+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=17cc17af-8bbb-4761-ad39-da62ad512c23]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('88cae505-15e1-4ca8-bcf7-8bd1c3af8030', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.975+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('463ef456-2b86-4c67-bc2b-23079a793142', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.979+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e29a0563-0299-42df-8f3e-38fdb851d743', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.982+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e0756af8-2cce-4246-a589-a4741eee2359', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.985+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7e6f0608-02b9-4523-bdef-5a8cdf63d65b', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:20.987+02', 'provisioner', 'info', 'Starting workspace', 'Plan: 4 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1fb337be-0749-4e98-8513-62164696b42e', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.166+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6d785a3a-7864-4cbf-9612-af13c4347f66', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.172+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Creation complete after 0s [id=0907dab9-1909-4d03-bffd-936a2a05a22a]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9e3f0f0d-3269-48c8-aa1c-f633360809cc', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.185+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d6bc2b50-2e74-414a-bf13-a43189e86abb', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.192+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0e090bdb-12b2-4959-9230-9b014af2b7ca', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.195+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Creation complete after 0s [id=5f1c2ba3-6b26-409c-9790-df56b62efcbc]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4bfa811d-9368-4548-be3e-3f7884d2ba5d', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.198+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Creation complete after 0s [id=coder-admin-my-workspace-root]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('00ef0ca8-439c-4b3f-94a4-cd086d15e9d2', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.21+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('53d6c9b1-8d43-4f0d-963b-8967ee2a3409', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.74+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creation complete after 1s [id=a1ff536472e9efdd5ed90d6aafc28669799668007ab93823567ac08066822244]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f29082e9-aad2-46f9-8d4a-bacf3d3618dd', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.749+02', 'provisioner', 'info', 'Starting workspace', 'Apply complete! Resources: 4 added, 0 changed, 0 destroyed.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6a3690dc-6e1f-400b-b47f-af64bd007a55', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:21.755+02', 'provisioner', 'info', 'Starting workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('473429ac-5ede-48cd-b754-9b3e498aa60c', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 13:04:22.83+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f5303749-4d64-4f3d-ac0e-81e629355e86', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.44+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0812fe54-1b04-4b52-9f43-049fa80467e8', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.444+02', 'provisioner_daemon', 'info', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('31f9020f-3378-4209-a421-7399c86ea76d', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.54+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1110403f-1865-4dca-97a7-4ecb14a97134', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.545+02', 'provisioner', 'debug', 'Stopping workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('955f1a84-1718-4aa7-8022-3ea41d2ba1ba', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.549+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ea27eb37-4934-4bad-bbeb-8a7f0bec0498', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.553+02', 'provisioner', 'debug', 'Stopping workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3525b9d7-69c2-4537-9f47-6290966b4f3d', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.555+02', 'provisioner', 'debug', 'Stopping workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0b83bb69-0b72-4e11-8c13-2b68b90217a9', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.832+02', 'provisioner', 'debug', 'Stopping workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bff36240-532f-44d0-ae0f-2c2faa22ebf3', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.941+02', 'provisioner', 'debug', 'Stopping workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('755ab7f1-8af7-4e73-bdf4-e7d42250f102', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:48.997+02', 'provisioner', 'debug', 'Stopping workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('41af9b10-8823-40ef-9acc-0c7c0aac73aa', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.066+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4eb9c01c-e77c-4509-b1d3-f394383e00dc', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.072+02', 'provisioner', 'debug', 'Stopping workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6baa0b42-517a-4154-804f-50b900a17cb0', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.077+02', 'provisioner', 'debug', 'Stopping workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5ed6a9d9-ce78-41f2-850b-c54354ba85fc', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.081+02', 'provisioner', 'debug', 'Stopping workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f674b8d8-1aca-475d-84d1-74e68caad7c2', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.084+02', 'provisioner', 'debug', 'Stopping workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bf8e0403-bc1f-4200-93cd-d459c5d4eea5', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.089+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2a26f80a-f534-4a57-8f60-61b0245b2a28', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.092+02', 'provisioner', 'debug', 'Stopping workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d687a831-e9cf-47f7-8fee-2b971e07110e', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.095+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5f1ea8a9-583f-4613-9cf9-2ae1dada1013', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.098+02', 'provisioner', 'debug', 'Stopping workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('63a97f16-df0c-4064-abb1-fa94edff116b', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.1+02', 'provisioner', 'debug', 'Stopping workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0659c1b7-ba0a-4153-a121-1b707d6dba43', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.103+02', 'provisioner', 'debug', 'Stopping workspace', ' - coder/coder') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5026c663-c180-4880-b27e-831ea7b915e6', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.106+02', 'provisioner', 'debug', 'Stopping workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('11bc6add-90ee-4ef1-8270-2c4b75149666', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.108+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('a4ed025f-5228-40fd-97dc-3f581423760c', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.11+02', 'provisioner', 'debug', 'Stopping workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f87eb959-7fa4-4574-b57f-0d25eddb5780', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.113+02', 'provisioner', 'debug', 'Stopping workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('190ce772-4a3e-48d1-82fc-6d2c4406376b', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.115+02', 'provisioner', 'debug', 'Stopping workspace', 'providers.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('807aa93f-0413-4158-9706-aad0f2bd3472', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.118+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('224ba0e5-1c27-44d9-8dd9-d04843eed695', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.12+02', 'provisioner', 'debug', 'Stopping workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('744e3d76-8b8d-40c2-a3d3-2b2707720a2b', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.123+02', 'provisioner', 'debug', 'Stopping workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('53594c81-a9e6-4ab3-babd-5491157c4de1', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.13+02', 'provisioner', 'debug', 'Stopping workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('86f24df0-9053-4aee-a55e-39186d23dd9c', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.133+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('710b2445-edfe-4b38-956f-f3ee83affebc', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.136+02', 'provisioner', 'debug', 'Stopping workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8e29a006-dbbc-4cc2-8856-70aaddc70627', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.139+02', 'provisioner', 'info', 'Stopping workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bbf4cec8-2edc-47e1-9f18-71c0d0f3229e', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.88+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ad23bec6-86a9-4831-9adc-3ee4d979e49c', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.887+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0a0c2ddc-94f4-4d9a-a403-c0229939ad38', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.89+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=69d8c247-a4cc-4de6-86c0-ce073255316a]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('992e7b85-8c39-4c79-82c8-d9e616baf3b3', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.894+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=3a9a1feb-e89d-457c-9d53-ac751b198ebe]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5abaec85-0b9a-49a0-a547-e40f68123865', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.898+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Refreshing state... [id=0907dab9-1909-4d03-bffd-936a2a05a22a]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('927353ca-a7f2-4604-b7a2-0b1e3c5bd1da', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.902+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Refresh complete [id=0907dab9-1909-4d03-bffd-936a2a05a22a]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('94933b2b-bf01-45c8-a0e9-6c82700cd4d4', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.91+02', 'provisioner', 'info', 'Stopping workspace', 'coder_app.code-server: Refreshing state... [id=5f1c2ba3-6b26-409c-9790-df56b62efcbc]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5b8fcf47-22e5-4ced-a3b7-aaaef79f8452', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.913+02', 'provisioner', 'info', 'Stopping workspace', 'coder_app.code-server: Refresh complete [id=5f1c2ba3-6b26-409c-9790-df56b62efcbc]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2c9cb430-1dc6-4896-a0c8-469947399523', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.917+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Refreshing state... [id=coder-admin-my-workspace-root]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9cbe3787-1861-4007-8803-f071482fa2fa', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.921+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Refresh complete [id=coder-admin-my-workspace-root]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d8b2a8a8-c653-412e-a70d-dc65a14c2af6', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:49.925+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Refreshing state... [id=a1ff536472e9efdd5ed90d6aafc28669799668007ab93823567ac08066822244]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6b22e12c-75e1-4f5a-a571-add29e687bd8', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.043+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Refresh complete [id=a1ff536472e9efdd5ed90d6aafc28669799668007ab93823567ac08066822244]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('db7d2cd9-0331-4e1c-9ae2-0cc18cfb1d5e', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.057+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Drift detected (update)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('71455c3a-c6ec-472c-9b6e-e174697c3e93', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.061+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Drift detected (update)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5580b627-218d-43eb-b619-b269c796b158', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.066+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Drift detected (update)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9499f481-527c-46a8-9a9f-f84ab5caf603', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.069+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Plan to delete') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c245b280-6683-4aea-ab6b-55dd8ccc02dd', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.073+02', 'provisioner', 'info', 'Stopping workspace', 'Plan: 0 to add, 0 to change, 1 to destroy.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0aa11a57-2d0c-40f9-84e2-793d47db516f', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.174+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Destroying... [id=a1ff536472e9efdd5ed90d6aafc28669799668007ab93823567ac08066822244]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('354d0fe5-252d-49eb-a432-3672bd77f421', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.472+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Destruction complete after 0s') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e4cf8c6b-2e24-4db8-bb71-d57c5c88157b', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.485+02', 'provisioner', 'info', 'Stopping workspace', 'Apply complete! Resources: 0 added, 0 changed, 1 destroyed.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('651e7bf8-eeb1-41fc-acca-d00036fd1dbe', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:50.49+02', 'provisioner', 'info', 'Stopping workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('15a9676b-718b-4461-9e62-81fbd550a897', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 13:04:51.51+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('109203b0-c680-45fe-b470-ab383e7322f7', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.438+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('25618f60-4596-4bb5-9ba4-daa75e885289', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.442+02', 'provisioner_daemon', 'info', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('53042802-9b70-4933-91ab-b2c0bc444693', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.545+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c9ad724a-5481-44f7-b8a1-b450dc5051f6', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.549+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('71bc590a-a0e5-4268-89a7-d6178304d91c', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.552+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('aa7529a0-8020-4268-a106-bfa9d7b8db45', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.555+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4ebfc500-c4ff-4247-a4e8-14e6814d9fac', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.557+02', 'provisioner', 'debug', 'Starting workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('541853de-e473-486d-b6e1-3b6e78518788', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.82+02', 'provisioner', 'debug', 'Starting workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2778a009-7045-4773-beb6-4fb7ca574116', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:55.993+02', 'provisioner', 'debug', 'Starting workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ed060a03-3f5a-4695-97e3-22d7deab8d38', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.053+02', 'provisioner', 'debug', 'Starting workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3564ae4e-b903-4431-96d9-17d7bac188f7', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.128+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('683bd5c5-0397-4d70-8e5f-7d5995f1f38a', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.136+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('55d3ea8e-5bc0-4aac-aa19-4d4c74146503', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.14+02', 'provisioner', 'debug', 'Starting workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('cbff931a-8b68-4dfa-83a2-40809e7add5d', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.143+02', 'provisioner', 'debug', 'Starting workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3fff2b02-6fc5-421d-8c2a-1ab8ffbc9fcc', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.146+02', 'provisioner', 'debug', 'Starting workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9ac3b08f-bb36-4e21-828f-4ae47d2b9719', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.149+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('74d08fc2-27a1-403b-85f8-24ec862dd288', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.152+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d2511df7-1992-4c8d-b5ca-bb60bc638ce4', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.156+02', 'provisioner', 'debug', 'Starting workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('681814dc-5855-4e4b-8907-c9de0d29b119', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.16+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bbbb300a-9a20-4e88-8f12-4de90b82594e', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.163+02', 'provisioner', 'debug', 'Starting workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8bc2492b-fd16-4098-92e9-7d8ece563d3d', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.166+02', 'provisioner', 'debug', 'Starting workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('dcdf65fb-c226-4554-8fec-edbb04f1414e', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.17+02', 'provisioner', 'debug', 'Starting workspace', ' - coder/coder') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8820392e-9d9e-4988-9623-ee1af0059d11', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.173+02', 'provisioner', 'debug', 'Starting workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9c64a617-539b-4e57-a750-355c66cf15cb', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.176+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bffd2b77-7f42-4aef-ac19-586998a6f614', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.179+02', 'provisioner', 'debug', 'Starting workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d7fcb4d1-6c30-4777-83c6-015b0f48a970', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.182+02', 'provisioner', 'debug', 'Starting workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('cdcb62d9-f347-4654-b5b2-28fd6960a509', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.185+02', 'provisioner', 'debug', 'Starting workspace', 'providers.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('95ea3beb-90e7-4787-901c-f570df3bd801', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.188+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9797523c-35ee-4ed3-a69f-af357fafc42c', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.19+02', 'provisioner', 'debug', 'Starting workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bb8189ba-82a1-48f1-bf1c-e000659f8697', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.193+02', 'provisioner', 'debug', 'Starting workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7d86f4fa-e8e9-42f7-b1f6-f27822c67292', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.195+02', 'provisioner', 'debug', 'Starting workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('17e19806-b748-4837-9166-0bcae6592ffe', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.198+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('07392a8e-31b3-4e7d-ade1-a09d4c2ee2b6', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.2+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('524bf497-10f8-41d3-960d-ed67df4fa9ec', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.204+02', 'provisioner', 'info', 'Starting workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('326c0c94-5c0a-45f8-8a26-1e8639553502', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.954+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8a6b7cd1-f0f5-4972-9364-cdc5eeb61f0c', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.958+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('61b04b23-eca5-4c15-91bb-bb88f00a2021', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.962+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=b65a767f-719a-4883-a186-aeb1ac05cb25]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('73e70e26-c6b7-4d8e-a9cc-1acc06022279', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.967+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=3a9a1feb-e89d-457c-9d53-ac751b198ebe]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('74683648-d94f-4d9e-9e6d-bf5fc05fd706', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.972+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Refreshing state... [id=0907dab9-1909-4d03-bffd-936a2a05a22a]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0c241c2a-7607-438c-8af2-c3641959f485', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.976+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Refreshing state... [id=coder-admin-my-workspace-root]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('944880c1-ef75-45b8-a60f-f17800a83023', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.979+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Refresh complete [id=0907dab9-1909-4d03-bffd-936a2a05a22a]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7a466591-e523-4fe0-9df3-564a35161f49', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.982+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Refresh complete [id=coder-admin-my-workspace-root]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c4e5c961-e812-40e3-b0a4-bc5bb781751d', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.986+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Refreshing state... [id=5f1c2ba3-6b26-409c-9790-df56b62efcbc]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0b2bdd2e-4c9f-4514-8f42-0650a0be805a', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.989+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Refresh complete [id=5f1c2ba3-6b26-409c-9790-df56b62efcbc]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d3b5a8c6-f038-4147-a074-4603945ed27b', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:56.997+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Drift detected (update)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4f2c80f8-d06b-4670-976e-3b58b5f8200d', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:57.002+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('755914cd-d41e-447e-9343-284fe4c0a9a5', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:57.006+02', 'provisioner', 'info', 'Starting workspace', 'Plan: 1 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('dc059e5c-18aa-4d95-b50d-9861ee068be7', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:57.229+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ccbb8238-0a62-4d0d-8269-3709ce02f47a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.964+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e8fd1570-dfb7-4bdf-9d13-fd1dbf3cb124', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.967+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('76167fa7-9670-430f-9536-176cdc4a9f5c', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.97+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9d1e67b3-6bd8-435e-96b5-c7b012b42604', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.972+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4a8a8443-c6cf-435d-8527-1a782b74b838', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.975+02', 'provisioner', 'info', 'Starting workspace', 'Plan: 4 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('45c669a4-88fd-47f0-98a3-e0875d352d7a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.16+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c48882a8-d865-44ee-b8d0-6fa60a255848', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.165+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('dda5b5fe-027d-402a-8852-a4ce2bfe4d71', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.17+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Creation complete after 0s [id=6137599d-4ad6-4e58-b0fa-a3d1a88fa5a9]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ea5f28e9-38b6-4d6a-8ffb-9e48657632ea', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.174+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Creation complete after 0s [id=coder-oauthuser1-test1-root]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('53224434-ead2-4aa3-8c86-177bc4c8fc3b', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.181+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('71ddfedf-d129-495e-bb80-ecf16f789be0', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.186+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f2f59832-212f-466c-92f3-ede2f34fc731', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.189+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Creation complete after 0s [id=3a585bc7-75e6-408e-b0bc-dc64e4d3be0a]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b10e5078-18f2-4ccc-875f-134d5724667a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.857+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creation complete after 1s [id=eed96dcc159829b9a91ee13a2106b3f8598896f99d7142765276914e628e8109]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('566e4cb4-2828-43f1-af4c-5797a7fd3504', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.868+02', 'provisioner', 'info', 'Starting workspace', 'Apply complete! Resources: 4 added, 0 changed, 0 destroyed.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ad17b455-6adc-4f4a-851e-c8fa568c20c7', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:42.871+02', 'provisioner', 'info', 'Starting workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bfe7a100-7f42-471f-a210-b687bda0fc24', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:57.739+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creation complete after 1s [id=db4aecde657b126939e8bad412b953314cd1316fe7b3f3d061203d0a71e037e8]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e896bbfe-86a0-4f7b-bf6e-4b848d945cbf', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:57.743+02', 'provisioner', 'info', 'Starting workspace', 'Apply complete! Resources: 1 added, 0 changed, 0 destroyed.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('250130a2-19fd-4cce-874f-c74d55aa2f14', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:57.748+02', 'provisioner', 'info', 'Starting workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('91616345-1c98-40ba-a4d3-23514865abb8', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 13:04:58.769+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('35036434-b0d7-4d8b-9b2e-2a55fb045bd7', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.44+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('80f1e6fd-8e57-46a9-bcc0-436d9ff34cc2', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.444+02', 'provisioner_daemon', 'info', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d4aa27c1-8b51-455f-ad37-ef3031e9959d', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.535+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ef4ffdad-1b7c-42e9-9ca4-03a878e8efce', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.54+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('caae1027-94c4-4ce9-a58a-c7629632c763', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.545+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1d890d4d-1b83-4cbc-b4b9-904150e970ae', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.549+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ebe2cf5f-bd32-44e9-95a2-e92fd68e51f4', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.552+02', 'provisioner', 'debug', 'Starting workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('849e5476-f4d6-42aa-9331-a80a27a12107', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.916+02', 'provisioner', 'debug', 'Starting workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0c142d37-a1d7-4ba1-b3e3-a2544952099a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:40.984+02', 'provisioner', 'debug', 'Starting workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0654054e-9f59-40b2-b899-0b8a8ec3f33a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.044+02', 'provisioner', 'debug', 'Starting workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('edc50f19-4bbe-46bf-8f13-292384708095', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.114+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ce618bdf-d2b0-4857-98d4-842d33f0154b', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.12+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('794ea457-7da4-42c0-a960-bf0895541c9c', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.124+02', 'provisioner', 'debug', 'Starting workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('47d5a9e6-4b36-4129-a746-5611767673cb', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.128+02', 'provisioner', 'debug', 'Starting workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b23c35f8-a06c-4716-9d6d-1ab79dda11bf', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.131+02', 'provisioner', 'debug', 'Starting workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('75fc0daa-c636-49da-b635-09bb34b69a20', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.134+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('56702227-afc1-4413-a939-36f521e80d4b', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.137+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('becc9242-efa6-4ed9-a69e-8f4c589529d0', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.141+02', 'provisioner', 'debug', 'Starting workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('37205ac5-6eb1-4773-9def-cfd48cb8ce11', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.146+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b85b9b97-4fad-442f-8662-af1660d73f3f', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.15+02', 'provisioner', 'debug', 'Starting workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('80c19b36-2754-4b72-9b5e-59518b293241', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.153+02', 'provisioner', 'debug', 'Starting workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2dc14d18-0cac-4a18-b7c1-1f3b858db5a3', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.156+02', 'provisioner', 'debug', 'Starting workspace', ' - coder/coder') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d0d6d612-755f-418b-8c2c-d3509a42abd0', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.159+02', 'provisioner', 'debug', 'Starting workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1f466eae-c2b8-4942-a189-07d80f8cd34c', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.162+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('cf37bd97-4ae3-404b-8f0a-94a7f2bf9af1', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.165+02', 'provisioner', 'debug', 'Starting workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8000ff14-2939-472d-ae75-ff323d5c02df', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.168+02', 'provisioner', 'debug', 'Starting workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('116ed6f5-9486-45c8-a460-de9491854550', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.171+02', 'provisioner', 'debug', 'Starting workspace', 'providers.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3e3ba08f-2010-4e24-bbc9-97a0f17ccae5', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.174+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0ebddff5-9fdf-4026-885e-5bfbe81cf837', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.177+02', 'provisioner', 'debug', 'Starting workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1e283699-cc08-47c6-accc-69c1d1579540', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.179+02', 'provisioner', 'debug', 'Starting workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('08c60987-9653-4c60-8d72-60c930581bac', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.182+02', 'provisioner', 'debug', 'Starting workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ad734b46-eb2b-4566-b2d2-b8f5c6a65192', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.185+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ea094a42-52ef-4974-a81d-db49d6dca17a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.188+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d962bb9b-9ffc-4131-b9e2-4b540a025d20', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.191+02', 'provisioner', 'info', 'Starting workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8edcc52f-4242-4c78-b375-eb343d439e2d', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.916+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('33f37088-1e7c-4cfb-b150-5d5e735f6da5', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.924+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('123192aa-9f05-4924-b724-c44928a6034d', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.928+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=fe418e81-b8a0-4f69-87ea-a0a597745e29]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('15a7ed53-89f0-43da-af80-9b95649e0697', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:41.931+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=b90547be-8870-4d68-8184-e8b2242b7c01]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3109c750-9158-463e-b1c6-7e8a9d562a54', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-02 13:05:43.883+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('451eda7a-d6a8-464c-893e-78796ef328bd', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:03.941+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6cd03ef3-ca74-4898-b014-3823feef5014', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:03.945+02', 'provisioner_daemon', 'info', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('be374e41-cf6f-493b-a799-e2f548f940f5', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:04.047+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('351f9dbc-1936-4890-b145-d1644c4a346a', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:04.05+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('040204a2-cdf4-4ee1-9704-7c8ec1fa0374', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:04.053+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('09c75767-3d08-4673-b4f6-eca8fac2058f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:04.056+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c986858c-c26e-49ea-a011-38e07510dc51', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:04.059+02', 'provisioner', 'debug', 'Starting workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1e352ba9-9fda-4a5e-9bc6-38baac1ac124', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.453+02', 'provisioner', 'debug', 'Starting workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('778be809-e832-4518-b63c-b4d4ea9bb24f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.62+02', 'provisioner', 'debug', 'Starting workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d2302004-ed2b-4071-bec5-ce9f16446aaa', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.695+02', 'provisioner', 'debug', 'Starting workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4055c54c-f128-46a1-86f9-ccec28d9cb9f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.75+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('05cdffde-f37a-4899-97ad-b3954c99fe20', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.754+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('20215593-28a0-4a44-8d23-f990171d6e25', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.758+02', 'provisioner', 'debug', 'Starting workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('74c22124-ad8c-4c8b-9c38-e074c0a6690b', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.761+02', 'provisioner', 'debug', 'Starting workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f5df23a6-6461-4b64-bb02-16b4c8a8558c', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.764+02', 'provisioner', 'debug', 'Starting workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('89564128-9d95-4dbf-bfc9-3e0a3e080f6a', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.766+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e9c32ce3-71f1-4b4c-a269-c8f26b70d2cf', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.769+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9ea0b406-aed4-4dde-978d-0aa184d905ab', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.772+02', 'provisioner', 'debug', 'Starting workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e2c45f9e-f037-41fc-b1a0-3415309a4bc7', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.775+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e70b1f1f-a226-481b-99a7-84ea9df1aeee', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.778+02', 'provisioner', 'debug', 'Starting workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f2724cf1-bbda-40ba-825f-64333171092f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.78+02', 'provisioner', 'debug', 'Starting workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4727c32b-90e1-4d27-9c52-e782f00aec3c', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.783+02', 'provisioner', 'debug', 'Starting workspace', ' - coder/coder') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('56d15d39-2f3a-4128-9462-109a7c35d3e4', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.785+02', 'provisioner', 'debug', 'Starting workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d2837d78-e7d6-4c6e-a7cf-4083f774a060', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.79+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('fd344fbd-5269-4d53-9d24-2f0a3e07b3f5', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.792+02', 'provisioner', 'debug', 'Starting workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('eb228344-c8f8-4ba8-a2f4-b486ff59d8fe', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.796+02', 'provisioner', 'debug', 'Starting workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('192a9ac1-af63-4aca-9dbf-3861ab360ee6', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.804+02', 'provisioner', 'debug', 'Starting workspace', 'providers.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b6517635-07cc-439a-bca9-a738bae94c53', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.807+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('cd3c921e-7929-49e4-b853-7713f7da68aa', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.809+02', 'provisioner', 'debug', 'Starting workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c98500c6-8ce5-4e15-9403-aa97f618141a', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.812+02', 'provisioner', 'debug', 'Starting workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('754568bf-a947-459e-abc5-03e37cb53b5f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.814+02', 'provisioner', 'debug', 'Starting workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bc0becf0-9184-4fb3-853a-d5e3d6be25f4', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.817+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0b62b0a7-de63-44b1-9a7f-a08210fa8283', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.82+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f3a49bd8-51e1-4a88-8a21-58cec8640e50', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:05.823+02', 'provisioner', 'info', 'Starting workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0863b4d2-84aa-43b1-8d52-a13e4ed91902', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.584+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8739d789-e629-4047-9e3a-499eae4c3d8a', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.588+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('790ba241-04b6-4dbc-a703-3aae9f28eae9', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.592+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=10c01a50-bec1-446c-b395-5d184fba369d]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1e2e80ff-f1d5-4285-9a4c-6dc918f024a2', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.595+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=2d72d32e-3021-4843-b582-d962fee897e2]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c8c6b94f-4a28-4e85-8c84-df60494cf98b', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.618+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f83f42ae-5671-49b4-9bb0-0fa535fc4d47', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.622+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9725ca37-c56a-4e25-ba87-3eda93cab7cc', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.624+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('73c7ed3c-7753-4823-b7ef-6826edfb7905', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.627+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ed4512d2-c3b6-4368-9291-0be933767557', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.632+02', 'provisioner', 'info', 'Starting workspace', 'Plan: 4 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('fc3e96db-5178-411a-8282-b8ee8e5db38f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.836+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b7a50c72-1b48-4c37-833d-76560b60bf4f', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.841+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e87d2c28-3bd0-43ce-8b8a-6aa58264d7ab', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.844+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Creation complete after 0s [id=coder-oauthuser1-scheduled-root]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3741adca-df5c-41fb-9031-7c2e9e8a35bd', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.847+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Creation complete after 0s [id=8841874d-0c5c-49ab-ab10-2c6b8e509433]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1e2bf69c-236a-430d-8824-16b04daaee9b', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.853+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3f6087ff-301b-44c0-a892-88e5523048dc', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.858+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Creation complete after 0s [id=86ac90e2-cc81-41dc-a16e-eeca54c8a3c7]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('03e0d14e-d50e-4a91-a845-dd5140cea0b0', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:06.862+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0f701ad8-b85b-4515-aa10-426a9dd532a9', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:07.559+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creation complete after 1s [id=031ada2ead4766de08331f05ed2e5f0d674166517757edf1b924886b1359cab5]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7dcd63a2-1e80-4927-adca-1b19e6e3f7d4', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:07.57+02', 'provisioner', 'info', 'Starting workspace', 'Apply complete! Resources: 4 added, 0 changed, 0 destroyed.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('32909e4c-0dd2-43c2-b231-01314ae83bd8', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:07.578+02', 'provisioner', 'info', 'Starting workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ca4907f9-2158-4254-9a70-7366407b0789', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-02 13:06:08.667+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ec5ec4b3-49a7-4400-9085-3dadca04e656', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:52.934+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('97069c13-0085-41f7-b515-144630e3d10c', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:52.945+02', 'provisioner_daemon', 'info', 'Adding README.md...', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bc2dc6d3-e103-454e-a0ea-28e3ee516811', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:52.951+02', 'provisioner_daemon', 'info', 'Parse parameters', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('14bc3811-9fca-427e-8bca-bdf4124c8c42', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:52.965+02', 'provisioner_daemon', 'info', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f38628b1-2b19-4221-9a82-721b4064245a', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.058+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bae7a730-6853-41bf-aec3-df57021ef685', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.062+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Initializing the backend...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2cd32c99-b560-4029-9cfe-eddbb7b169b6', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.066+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('618f2905-ded6-4342-826b-230e3f1b07ac', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.069+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('fcd5133c-02a9-45d5-98f8-343c28c192b8', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.072+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('edc4a28a-1a05-4181-b523-3b7866ed47d0', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.214+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3abbd207-79d1-472e-b311-5d12cfa2adf0', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.311+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('055d02e9-70a8-458a-b63f-c9bf055f61b4', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.371+02', 'provisioner', 'debug', 'Detecting persistent resources', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0d385c88-0c06-4cd1-b69b-23690fa3c6dc', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.443+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('be40524d-3b27-402b-9253-9eb7afd8b3a7', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.447+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('186eacc1-237e-43ea-bf17-747f20277e80', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.45+02', 'provisioner', 'debug', 'Detecting persistent resources', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('72fea3ac-e12c-4222-9a65-23b303fbde51', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.453+02', 'provisioner', 'debug', 'Detecting persistent resources', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('a51ebdff-8f37-4a2a-9715-139db970054a', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.457+02', 'provisioner', 'debug', 'Detecting persistent resources', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2ed720c7-8872-46f1-aff2-d637b8ba8f47', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.462+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('72d48510-300f-4155-8ba7-7abeda40e411', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.466+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('234761af-f52a-4b87-9c91-80708bfb57df', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.469+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c1f61684-7229-459f-87d3-52f548675d1e', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.471+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4f76df82-b848-4225-82c1-5e8787258b24', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.475+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('20510fda-a6a1-4999-95db-cc3fb4458f5a', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.478+02', 'provisioner', 'debug', 'Detecting persistent resources', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('577517b0-be37-4fac-b9c8-a91840bfdf13', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.481+02', 'provisioner', 'debug', 'Detecting persistent resources', ' - coder/coder') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('eda778eb-66b5-436a-a576-e6793f41b04b', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.484+02', 'provisioner', 'debug', 'Detecting persistent resources', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b6ba43a9-188a-479c-8200-9dc1771558d3', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.486+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7341e996-a19b-4a5a-a969-f12137c06c99', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.49+02', 'provisioner', 'debug', 'Detecting persistent resources', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3618eb39-3cf5-421c-89be-5f5d8fb18050', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.493+02', 'provisioner', 'debug', 'Detecting persistent resources', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0c493997-4fea-4e34-9427-66a80b29713f', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.498+02', 'provisioner', 'debug', 'Detecting persistent resources', 'providers.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8f0eedec-76e5-45a7-96d6-0f6053dd2b7a', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.501+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3bcac134-e265-4cb4-b222-42239602b230', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.504+02', 'provisioner', 'debug', 'Detecting persistent resources', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5baf397f-d719-49e7-9c64-5868ab6867d3', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.507+02', 'provisioner', 'debug', 'Detecting persistent resources', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('11f29f58-7699-4825-9f19-044991e2bd72', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.509+02', 'provisioner', 'debug', 'Detecting persistent resources', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4290e889-0524-4a44-a863-50ca5c995dd6', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.512+02', 'provisioner', 'debug', 'Detecting persistent resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8673bef9-01b6-4012-85f0-405abd2bc018', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.514+02', 'provisioner', 'debug', 'Detecting persistent resources', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e61d6c01-774f-466f-80c5-d48d9d650659', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:53.517+02', 'provisioner', 'info', 'Detecting persistent resources', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5f1cb4d0-6333-40c9-a4f6-35a33707850e', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.242+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9349299d-5e9f-4392-a0a9-bf57b81a486e', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.247+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('03d69f7f-1ba0-4ff3-b165-57644ae74ea0', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.253+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_provisioner.me: Refresh complete after 0s [id=0c2a9b56-6788-426a-b51f-d1020b2f946c]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2b4f8569-69f4-484a-a0ba-274e7c508460', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.257+02', 'provisioner', 'info', 'Detecting persistent resources', 'data.coder_workspace.me: Refresh complete after 0s [id=f029c2d7-f293-4d65-95e5-fbbec8069da5]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2d426394-18e9-4eff-9b3f-7f8ca8212a0e', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.284+02', 'provisioner', 'info', 'Detecting persistent resources', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4f281678-1ff4-4a32-83bd-0357f02279dc', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.29+02', 'provisioner', 'info', 'Detecting persistent resources', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e0dde379-7983-41fc-ae3f-125adbb7e0a4', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.294+02', 'provisioner', 'info', 'Detecting persistent resources', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1359a82f-17d0-46c4-908b-a6623aec532a', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.299+02', 'provisioner', 'info', 'Detecting persistent resources', 'docker_container.workspace[0]: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('46b1948d-b07b-480f-9cb5-94dc56c76414', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:54.303+02', 'provisioner', 'info', 'Detecting persistent resources', 'Plan: 4 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c4a04254-ac4e-49b1-9922-18f00beb81f3', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.086+02', 'provisioner_daemon', 'info', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8ac6dfa1-dd6e-48c4-95c9-9d43a3f81fd1', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.169+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e833f596-ecf0-42aa-a4ab-544becc93b2f', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.173+02', 'provisioner', 'debug', 'Detecting ephemeral resources', 'Initializing the backend...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('53d42646-4d25-4f0a-a90a-2e7344952e04', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.306+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e58bd9b1-03dd-4650-a530-7264f1b77256', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.311+02', 'provisioner', 'debug', 'Detecting ephemeral resources', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('13f5bbd4-c399-4896-ad60-3a180a814ee9', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.314+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Reusing previous version of coder/coder from the dependency lock file') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4d5547c9-22e3-4dc2-a13d-ce958cad5d05', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.55+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Reusing previous version of kreuzwerker/docker from the dependency lock file') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('fc1acf12-6058-44c9-a7a3-18ec67538272', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.77+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Using previously-installed coder/coder v0.5.0') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('04728b40-6184-41fc-833c-e1b1299ce625', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.854+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '- Using previously-installed kreuzwerker/docker v2.20.3') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('501c8bae-4726-4365-9b94-0e20063d94c5', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.858+02', 'provisioner', 'debug', 'Detecting ephemeral resources', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e420f075-e982-4515-b45a-1b6c5aaf79cd', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.863+02', 'provisioner', 'debug', 'Detecting ephemeral resources', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6af54ded-f9ed-4bcd-ab31-f5b7d451cbc7', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:55.924+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6c0ffa8a-47ab-4d83-bcc3-63bd9dff7c51', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.669+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('aa2b789b-f85e-4131-8a31-a8125e4ebaf7', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.675+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bf3681f0-adaf-4466-ae0a-4251769947fc', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.678+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_workspace.me: Refresh complete after 0s [id=a56ece0a-5fd0-4377-beb4-b863a7b6f643]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('867acd6d-6459-4be3-8b7a-09c912f9a229', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.681+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'data.coder_provisioner.me: Refresh complete after 0s [id=799aeb19-6a6c-4127-bc37-02bd4766cf5f]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5cd3d492-6611-4944-b0b4-87e27dab9376', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.704+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'coder_agent.main: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('bd8ba462-89c9-4a1d-af5a-35d245fe8fc1', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.708+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'coder_app.code-server: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f50d9387-c97e-47a3-9958-2330462d7534', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.712+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'docker_volume.home_volume: Plan to create') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('44b9aa93-fa16-47f6-92c5-a3e8f6ae2d5b', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:56.716+02', 'provisioner', 'info', 'Detecting ephemeral resources', 'Plan: 3 to add, 0 to change, 0 to destroy.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e267601b-0bab-4dcd-8cf2-01315983e35a', 'f042af17-e3a2-4194-af67-e416302bc860', '2022-11-02 13:07:57.534+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c2f685fa-b49e-46b0-8b49-3499a6c86c54', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.441+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f0b836ba-dd49-42d6-9d2f-c765f676cef7', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.445+02', 'provisioner_daemon', 'info', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('365e78dd-d03f-413b-9ec2-c254106fc5d4', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.535+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('90b6d5a8-e690-4475-b05e-2c7d599bdb90', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.539+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f7024b37-d4dd-4c4a-9c64-aecff11a47ef', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.544+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('74bf20aa-8f5f-475a-991a-4baa61ac894a', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.547+02', 'provisioner', 'debug', 'Starting workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c9ef259a-f267-4afb-b401-c247ac3dd3f5', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.55+02', 'provisioner', 'debug', 'Starting workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6c6de8e1-0bed-4636-8966-502b7adcf530', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.705+02', 'provisioner', 'debug', 'Starting workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('a017a4e0-cbbf-4ea4-bf91-9936ead5d649', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.771+02', 'provisioner', 'debug', 'Starting workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7f2577aa-cab9-4e9c-82b1-e2b68f1de520', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.845+02', 'provisioner', 'debug', 'Starting workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5bbb9cf4-69a1-4e9f-87fb-7ddd6bf41323', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.901+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('49732ed2-1526-410a-844a-27fd51afa8b8', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.906+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3ca5f41e-936e-41cf-8ec6-9a322d73102d', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.91+02', 'provisioner', 'debug', 'Starting workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('a11b20a0-5b00-4323-a949-356013d91119', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.913+02', 'provisioner', 'debug', 'Starting workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8a2e242c-d977-4677-8f6c-e0d264d834e8', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.915+02', 'provisioner', 'debug', 'Starting workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ad719a38-a50b-4623-a0e1-a47e04d7827f', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.918+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2916ea33-9304-42c9-ae7d-2b965c202eb0', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.923+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('776a479c-8cfa-419d-87c6-9c3ad8a3bd09', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.926+02', 'provisioner', 'debug', 'Starting workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b3a16e4b-86a2-4d4c-8cf4-dc3488c636b9', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.929+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1756b015-3863-46a0-8ce1-756a3db0ab0d', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.932+02', 'provisioner', 'debug', 'Starting workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('dfff1eea-4022-4685-8fe9-1dedf12b1bfa', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.935+02', 'provisioner', 'debug', 'Starting workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8bfdd0f9-59f8-452b-a5f0-94cf72ee53dd', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.938+02', 'provisioner', 'debug', 'Starting workspace', ' - coder/coder') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('81e3e47b-bcdc-42f8-9c23-4e3224657961', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.941+02', 'provisioner', 'debug', 'Starting workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0dd5456e-727f-4820-801d-a73196a99db5', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.944+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d1017c48-e1e1-4f6b-85b7-c432ec0c848c', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.947+02', 'provisioner', 'debug', 'Starting workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('32f30546-bb7a-4b25-893a-7740d5028720', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.95+02', 'provisioner', 'debug', 'Starting workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0a9cbe99-0bfc-45a2-a714-91c0fab6959f', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.953+02', 'provisioner', 'debug', 'Starting workspace', 'providers.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('884b86f8-d32c-4961-a6d2-b74cecc9ef94', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.956+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('07a49cf9-2088-4977-930e-d7019c7fadd8', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.959+02', 'provisioner', 'debug', 'Starting workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0104d747-0c30-40ea-bc37-99d1c5d828b1', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.962+02', 'provisioner', 'debug', 'Starting workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('14d0e06a-920e-48f7-862f-21b26533c2d4', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.964+02', 'provisioner', 'debug', 'Starting workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9327e992-19d6-485f-a783-f069195b39bb', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.966+02', 'provisioner', 'debug', 'Starting workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e566f0e1-5000-4328-bb26-503933a6cf58', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.969+02', 'provisioner', 'debug', 'Starting workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('a622c794-e3de-4d05-ad21-7d15206a8134', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:07.972+02', 'provisioner', 'info', 'Starting workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2c3e911a-0120-4ef9-8bf1-ae38b335f4c7', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.685+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1700402d-473b-4545-ac26-e8c2474ae61e', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.69+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('677e7e30-e7b0-414d-950c-691ece713410', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.698+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=ad27e546-76e2-44a1-befc-04e736c7d136]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('de33d989-b4a0-4aca-bdcc-2f709c89badf', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.702+02', 'provisioner', 'info', 'Starting workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=b90547be-8870-4d68-8184-e8b2242b7c01]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ae71133b-83ea-4594-938a-619b65a72dbc', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.708+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Refreshing state... [id=6137599d-4ad6-4e58-b0fa-a3d1a88fa5a9]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2c4d3f77-57ab-444e-bcf2-47c0d334071d', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.713+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Refresh complete [id=6137599d-4ad6-4e58-b0fa-a3d1a88fa5a9]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('08e41e5a-92cc-4593-9a76-fe7aedb89136', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.716+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Refreshing state... [id=3a585bc7-75e6-408e-b0bc-dc64e4d3be0a]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b6e2db47-d7ba-421b-bb19-8efb34e15041', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.718+02', 'provisioner', 'info', 'Starting workspace', 'coder_app.code-server: Refresh complete [id=3a585bc7-75e6-408e-b0bc-dc64e4d3be0a]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('da66adc5-7955-4efa-ae4e-921d89a0a905', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.721+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Refreshing state... [id=coder-oauthuser1-test1-root]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('86b81dc3-345e-4032-91ca-e179d2f68426', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.724+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Refresh complete [id=coder-oauthuser1-test1-root]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c16e6dd4-8590-4267-8d86-86469c58bd38', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.727+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Refreshing state... [id=eed96dcc159829b9a91ee13a2106b3f8598896f99d7142765276914e628e8109]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f029d62b-fa15-41dd-a55b-56de56aa9d2d', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.845+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Refresh complete [id=eed96dcc159829b9a91ee13a2106b3f8598896f99d7142765276914e628e8109]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d420d06e-a470-491f-b77a-76e9efcfd885', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.88+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Drift detected (update)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f29b2f5a-5b30-4450-808d-57633ec9eee1', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.884+02', 'provisioner', 'info', 'Starting workspace', 'docker_volume.home_volume: Drift detected (update)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('dd638547-02bd-4f94-8e3a-706d8c4bbb4d', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.887+02', 'provisioner', 'info', 'Starting workspace', 'coder_agent.main: Drift detected (update)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9733a541-f554-45fc-b9e6-8449d0a7268d', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.891+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Plan to replace') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1442786e-4ac2-4a31-bf01-62f71ee1c58f', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:08.895+02', 'provisioner', 'info', 'Starting workspace', 'Plan: 1 to add, 0 to change, 1 to destroy.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b73e9a9c-2a80-4edc-b9c9-caed1f8fb76f', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:09.092+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Destroying... [id=eed96dcc159829b9a91ee13a2106b3f8598896f99d7142765276914e628e8109]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('26bb15bd-55f5-40af-bb5f-68276cdc547c', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:09.315+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Destruction complete after 0s') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e8dd9645-1e6c-4620-a2b4-db13e3591672', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:09.334+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creating...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('27a857d3-fae3-4e7b-92f3-ba66651415fd', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:09.838+02', 'provisioner', 'info', 'Starting workspace', 'docker_container.workspace[0]: Creation complete after 1s [id=86d859b12b6519160d53b99d4f0155bf6aeeefbd33d88fe93e2eecef22eb65de]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6b755e4e-7e2e-407d-874e-2b9d94636d1f', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:09.846+02', 'provisioner', 'info', 'Starting workspace', 'Apply complete! Resources: 1 added, 0 changed, 1 destroyed.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d7858aaa-6fa7-4673-9ff2-1b2347088c64', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:09.853+02', 'provisioner', 'info', 'Starting workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('43b1ff4e-b1d2-45bf-aced-0d209ae20ae6', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-02 13:08:10.807+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('567776bf-1821-4c52-80ed-3fce709d0c85', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:51.94+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('36b8be4a-a30c-4de0-8896-14406d6cfdb6', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:51.949+02', 'provisioner_daemon', 'info', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('80f21094-54a2-4756-a837-25df3ddc10bd', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.042+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e4a9be07-5e4a-402c-a32c-5d2be759063f', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.047+02', 'provisioner', 'debug', 'Stopping workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9b30655a-31d0-4cb5-a2d1-c55de3a4f7db', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.051+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b66c4596-da28-4f0c-857d-d22ec01a659e', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.054+02', 'provisioner', 'debug', 'Stopping workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f78aef35-c5b0-4ca1-bba3-1ed73c00d2f5', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.057+02', 'provisioner', 'debug', 'Stopping workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5128de3f-c94a-498d-aac0-690fb3a3256c', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.203+02', 'provisioner', 'debug', 'Stopping workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('cb707f76-a1ce-4411-998f-492ab315f9de', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.276+02', 'provisioner', 'debug', 'Stopping workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b6ba27e3-22c5-4585-ab89-9a122daa2ed2', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.352+02', 'provisioner', 'debug', 'Stopping workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('155e3a4d-e37a-4443-9609-94ea25793182', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.407+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('803c0af2-6bee-4dd5-bca7-088ccef48d0d', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.411+02', 'provisioner', 'debug', 'Stopping workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b045cf9c-4a31-42df-a157-36799cc1505e', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.415+02', 'provisioner', 'debug', 'Stopping workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('96af3253-66fd-4e6f-8a02-9aaa2ed840e1', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.419+02', 'provisioner', 'debug', 'Stopping workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('a577318f-cd80-46e2-9dc2-e96ddb958fb6', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.423+02', 'provisioner', 'debug', 'Stopping workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('918e8d10-6929-43ff-a417-e835a05593b5', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.426+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('82a34d3a-ff5d-42a4-9734-c369e56ce5c8', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.428+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f7cb10b8-9c51-4f23-afbe-33b20842feef', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.434+02', 'provisioner', 'debug', 'Stopping workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('77119d73-aa08-4d6a-b2a9-5a876cc38f10', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.438+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('77ba3339-32b2-4c0c-9dab-347a75457e1b', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.44+02', 'provisioner', 'debug', 'Stopping workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('378e1d98-148e-4ba4-861e-9eba8d7f1f79', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.444+02', 'provisioner', 'debug', 'Stopping workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('305ed411-809f-47e8-b5fc-36ab80d51862', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.446+02', 'provisioner', 'debug', 'Stopping workspace', ' - coder/coder') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('b2b3030f-f923-4c11-a0b5-3f88673f174c', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.45+02', 'provisioner', 'debug', 'Stopping workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('72495cac-fb10-4a5b-ba56-e5720fdcbe80', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.452+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('aa63292e-da72-4ad2-837b-4b7d09ca8352', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.455+02', 'provisioner', 'debug', 'Stopping workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7d402a33-e901-481a-a4b0-7975bc710884', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.457+02', 'provisioner', 'debug', 'Stopping workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('26f48c94-98cd-4ab4-a7c9-92b51c559e50', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.46+02', 'provisioner', 'debug', 'Stopping workspace', 'providers.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('425de025-c374-4359-bb84-36d08b1f045f', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.463+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f0a06046-0ed0-4d43-8608-8ede7c923a9f', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.466+02', 'provisioner', 'debug', 'Stopping workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c8eae175-6b93-44d7-86d9-acd10e5d9257', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.469+02', 'provisioner', 'debug', 'Stopping workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('a186dc9b-c58c-48ed-90e8-5ff3d4049dac', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.471+02', 'provisioner', 'debug', 'Stopping workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f47f6907-04cd-4ada-b7df-7bb27032d429', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.474+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('237bad3c-59c9-4d7e-9860-922c85106edb', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.476+02', 'provisioner', 'debug', 'Stopping workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0aeca511-4a7b-4151-a806-525e03e842db', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:52.479+02', 'provisioner', 'info', 'Stopping workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('66abfda2-7009-4d13-bb84-24ab857b3404', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.226+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e1073aaf-e53a-4e28-a771-66a273731128', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.231+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d388445e-b43e-4f8f-a866-66639edcb087', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.236+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=b90547be-8870-4d68-8184-e8b2242b7c01]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1726c68b-dad7-4cbc-bf24-6a6578f50edd', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.244+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=58ea2d09-993b-4754-8c7d-0c0ec944e2ee]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0331dcb4-8497-4b49-b823-be547e2fb19e', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.248+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Refreshing state... [id=6137599d-4ad6-4e58-b0fa-a3d1a88fa5a9]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('668a90d0-12e9-4aea-af6f-5c50324ebb1d', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.252+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Refreshing state... [id=coder-oauthuser1-test1-root]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('656ab85b-4184-494e-8a57-891b3343bacb', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.254+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Refresh complete [id=6137599d-4ad6-4e58-b0fa-a3d1a88fa5a9]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e521fb3e-c3d3-4e07-b7d5-d23a14735f3d', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.257+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Refresh complete [id=coder-oauthuser1-test1-root]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8b6e20ee-cd76-44f0-a361-24e3b91ca966', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.261+02', 'provisioner', 'info', 'Stopping workspace', 'coder_app.code-server: Refreshing state... [id=3a585bc7-75e6-408e-b0bc-dc64e4d3be0a]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7a5cb85d-85d5-4e4d-9bac-6b71b17a31a7', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.263+02', 'provisioner', 'info', 'Stopping workspace', 'coder_app.code-server: Refresh complete [id=3a585bc7-75e6-408e-b0bc-dc64e4d3be0a]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('61a50656-1299-4920-8cfe-a3d496fb7601', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.266+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Refreshing state... [id=86d859b12b6519160d53b99d4f0155bf6aeeefbd33d88fe93e2eecef22eb65de]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c748eb20-3d36-4dd1-9d67-94f187845e60', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.377+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Refresh complete [id=86d859b12b6519160d53b99d4f0155bf6aeeefbd33d88fe93e2eecef22eb65de]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('11300863-799e-4a8d-9d4e-e816fe59f8d0', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.388+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Drift detected (update)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('148f401b-202f-495d-a00d-3edaf2d98b54', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.392+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Drift detected (update)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('518cc0ce-b6fa-46af-be96-7eb0745832c9', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.395+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Plan to delete') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('abdc6fea-3ecd-444c-b0b9-a0c5c7b04bd5', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.399+02', 'provisioner', 'info', 'Stopping workspace', 'Plan: 0 to add, 0 to change, 1 to destroy.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('098266ee-37b3-423b-a915-1eebed4a9382', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.51+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Destroying... [id=86d859b12b6519160d53b99d4f0155bf6aeeefbd33d88fe93e2eecef22eb65de]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1dd3c1b5-aee2-4c3c-a43b-98abf577daee', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.752+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Destruction complete after 0s') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('785540dd-ba37-4111-900a-e63fe74c80c3', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.763+02', 'provisioner', 'info', 'Stopping workspace', 'Apply complete! Resources: 0 added, 0 changed, 1 destroyed.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('1dd9d93f-3497-4fd4-acd5-676807a332a6', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:53.769+02', 'provisioner', 'info', 'Stopping workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ae1db4e6-6f61-4e96-86a4-90869cdfd4eb', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-02 13:09:54.754+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('5ff779e3-9dd5-48a0-a5f3-a7a80d029807', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.44+02', 'provisioner_daemon', 'info', 'Setting up', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('59a25c28-c558-4571-b853-bee95d5fb22b', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.444+02', 'provisioner_daemon', 'info', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('9a497a9b-63aa-4424-bcc8-92ffc1dd1259', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.534+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('672df058-dbd9-4eb2-ab8e-21033b6104c7', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.538+02', 'provisioner', 'debug', 'Stopping workspace', 'Initializing the backend...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2c9dccb2-41d0-40bf-bcc4-04d77075474f', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.542+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('4168300f-5094-4235-91b4-6b3b4777f3ef', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.546+02', 'provisioner', 'debug', 'Stopping workspace', 'Initializing provider plugins...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('12c537a5-d863-4fe3-8b04-9ba93d749a64', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.55+02', 'provisioner', 'debug', 'Stopping workspace', '- Finding coder/coder versions matching "0.5.0"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e9be6d15-d900-47a1-a2af-9f1f51b3644c', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.156+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0a588339-f93c-4a54-9655-59e4fd30f49e', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:42.821+02', 'provisioner', 'debug', 'Stopping workspace', '- Finding kreuzwerker/docker versions matching "~> 2.20.2"...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0b21faa5-29a9-4b78-8a5b-2afd05d250eb', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.027+02', 'provisioner', 'debug', 'Stopping workspace', '- Using coder/coder v0.5.0 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('47176b70-e029-4b88-9ba7-4b19781da315', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.086+02', 'provisioner', 'debug', 'Stopping workspace', '- Using kreuzwerker/docker v2.20.3 from the shared cache directory') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('eb7b92a8-fdcc-475b-88da-8b2e1c4c7a57', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.16+02', 'provisioner', 'debug', 'Stopping workspace', 'Terraform has created a lock file .terraform.lock.hcl to record the provider') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c7b928c0-f6fb-4d4d-836a-8ff389bf8aad', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.163+02', 'provisioner', 'debug', 'Stopping workspace', 'selections it made above. Include this file in your version control repository') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('ed8116ca-5692-48ba-bc64-359bec1f54a6', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.166+02', 'provisioner', 'debug', 'Stopping workspace', 'so that Terraform can guarantee to make the same selections by default when') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('19e91c08-0342-4c21-8a23-8690b567a3bd', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.168+02', 'provisioner', 'debug', 'Stopping workspace', 'you run "terraform init" in the future.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c1144133-37fa-4715-9e1d-4301287ef9e7', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.171+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f454b157-5e4d-45ac-83a6-aaceb242af1c', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.173+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('78ef1dfb-8de7-4c09-9775-9ac2b0c8fccb', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.176+02', 'provisioner', 'debug', 'Stopping workspace', 'Warning: Incomplete lock file information for providers') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e17bda0e-e339-4f28-87ad-af15ff5103c3', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.179+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6ae8b6b5-7817-414d-bf79-dd6f78a9db26', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.182+02', 'provisioner', 'debug', 'Stopping workspace', 'Due to your customized provider installation methods, Terraform was forced to') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e3e90d48-336f-4d41-a90b-cc569633e5d4', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.185+02', 'provisioner', 'debug', 'Stopping workspace', 'calculate lock file checksums locally for the following providers:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('615327b8-8a02-4afc-a6be-a4e2d0758ebf', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.188+02', 'provisioner', 'debug', 'Stopping workspace', ' - coder/coder') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c30bc373-2667-42e7-aed5-bac2907c1813', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.19+02', 'provisioner', 'debug', 'Stopping workspace', ' - kreuzwerker/docker') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c7a73bc4-1d99-4230-89c5-be15c70db5e3', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.195+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('fa365813-ac49-4cb7-aaeb-a9344156b73a', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.198+02', 'provisioner', 'debug', 'Stopping workspace', 'The current .terraform.lock.hcl file only includes checksums for linux_amd64,') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('e501ee90-28bd-4d20-ad99-12a09fe8287e', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.201+02', 'provisioner', 'debug', 'Stopping workspace', 'so Terraform running on another platform will fail to install these') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('f4708ad7-013b-4149-ba57-8d30f8198142', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.203+02', 'provisioner', 'debug', 'Stopping workspace', 'providers.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('fa4f0229-187c-4654-aff8-6cee50c6720e', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.206+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0ccf37bd-877e-464c-9931-97564752f445', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.21+02', 'provisioner', 'debug', 'Stopping workspace', 'To calculate additional checksums for another platform, run:') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('64242d19-9374-45e3-a46c-17833470e20d', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.212+02', 'provisioner', 'debug', 'Stopping workspace', ' terraform providers lock -platform=linux_amd64') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('027cb177-34fd-4137-91a8-f0b63c0ae8f1', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.216+02', 'provisioner', 'debug', 'Stopping workspace', '(where linux_amd64 is the platform to generate)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('fd28c560-d002-4f4d-947e-75a9dd98ae71', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.218+02', 'provisioner', 'debug', 'Stopping workspace', '') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('35ad66c7-fd53-49a8-8a08-993abb9dc471', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.22+02', 'provisioner', 'debug', 'Stopping workspace', 'Terraform has been successfully initialized!') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('2d4d5e8c-2eea-42ba-8aea-2f250af13f8f', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.222+02', 'provisioner', 'info', 'Stopping workspace', 'Terraform 1.2.8') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('cf9cb3ab-8af9-4b34-8500-02979c138c4e', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.952+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_workspace.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('8537a20e-b447-423e-8f68-24b53582c88c', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.957+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_provisioner.me: Refreshing...') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('989116f5-b114-4df4-9b4f-62417b12cdd5', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.962+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_workspace.me: Refresh complete after 0s [id=2d72d32e-3021-4843-b582-d962fee897e2]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('33440676-d562-4284-8496-e9be767976de', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.965+02', 'provisioner', 'info', 'Stopping workspace', 'data.coder_provisioner.me: Refresh complete after 0s [id=a9bb9c27-a53b-4524-821b-64d3bf2da47c]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('3ea2e134-018b-4291-b9ca-d7db05a6a127', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.969+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Refreshing state... [id=coder-oauthuser1-scheduled-root]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('45d2a1e5-73a5-423f-a0bb-43a4fc49f457', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.972+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Refreshing state... [id=8841874d-0c5c-49ab-ab10-2c6b8e509433]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('71910cc2-83f8-4507-856b-7a95de0ec6f8', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.975+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Refresh complete [id=coder-oauthuser1-scheduled-root]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('96f27f81-0b5c-49b0-9b69-18ab9bd71d74', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.978+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Refresh complete [id=8841874d-0c5c-49ab-ab10-2c6b8e509433]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('6f7ec6ad-1f57-44b9-b1d3-d23e26ceca6e', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.982+02', 'provisioner', 'info', 'Stopping workspace', 'coder_app.code-server: Refreshing state... [id=86ac90e2-cc81-41dc-a16e-eeca54c8a3c7]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('13d86e02-e7ab-4390-a80b-dadee7fa8146', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.985+02', 'provisioner', 'info', 'Stopping workspace', 'coder_app.code-server: Refresh complete [id=86ac90e2-cc81-41dc-a16e-eeca54c8a3c7]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('214603da-529f-42e5-964d-77e91c92940f', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:43.99+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Refreshing state... [id=031ada2ead4766de08331f05ed2e5f0d674166517757edf1b924886b1359cab5]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('52409cc8-c6ce-4d8d-8c53-dc3e3734ae17', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.11+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Refresh complete [id=031ada2ead4766de08331f05ed2e5f0d674166517757edf1b924886b1359cab5]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('957979e1-7611-49f0-8756-d7e2a4eaffee', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.125+02', 'provisioner', 'info', 'Stopping workspace', 'coder_agent.main: Drift detected (update)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('d364a7e6-249c-4fd9-a041-3854b03388f9', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.13+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Drift detected (update)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('27daf314-7f00-435d-8fad-f037882392bb', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.134+02', 'provisioner', 'info', 'Stopping workspace', 'docker_volume.home_volume: Drift detected (update)') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('736b42be-7b2a-4f91-8676-37e12f2ca6f3', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.137+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Plan to delete') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('0bcd3cbc-916b-417e-96c2-afb5e83c9f3c', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.14+02', 'provisioner', 'info', 'Stopping workspace', 'Plan: 0 to add, 0 to change, 1 to destroy.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('deba29f1-462d-4e70-a6e7-ca2cb8ac5b70', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.257+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Destroying... [id=031ada2ead4766de08331f05ed2e5f0d674166517757edf1b924886b1359cab5]') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('118af4d6-67f3-4906-9585-4b4f1cdefc57', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.579+02', 'provisioner', 'info', 'Stopping workspace', 'docker_container.workspace[0]: Destruction complete after 1s') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('c257a4c2-83f7-45b4-ae42-652a93547b51', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.592+02', 'provisioner', 'info', 'Stopping workspace', 'Apply complete! Resources: 0 added, 0 changed, 1 destroyed.') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('825b93e0-a4fb-4807-9570-2190eec03ba7', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:44.595+02', 'provisioner', 'info', 'Stopping workspace', 'Outputs: 0') ON CONFLICT DO NOTHING; +INSERT INTO provisioner_job_logs VALUES ('7ebd786a-4c6b-4d81-b66e-c70912ac2036', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-02 13:10:45.612+02', 'provisioner_daemon', 'info', 'Cleaning Up', '') ON CONFLICT DO NOTHING; -- -- Data for Name: schema_migrations; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.schema_migrations VALUES (22, false) ON CONFLICT DO NOTHING; +INSERT INTO schema_migrations VALUES (22, false) ON CONFLICT DO NOTHING; -- -- Data for Name: templates; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.templates VALUES ('4cc1f466-f326-477e-8762-9d0c6781fc56', '2022-11-02 13:03:56.718393+02', '2022-11-02 13:03:56.718393+02', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', false, 'docker-code-server', 'terraform', '4e681a60-83da-42c2-902e-6535376ebb77', '', 604800000000000, 3600000000000, '30095c71-380b-457a-8995-97b8ee6e5307') ON CONFLICT DO NOTHING; +INSERT INTO templates VALUES ('4cc1f466-f326-477e-8762-9d0c6781fc56', '2022-11-02 13:03:56.718393+02', '2022-11-02 13:03:56.718393+02', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', false, 'docker-code-server', 'terraform', '4e681a60-83da-42c2-902e-6535376ebb77', '', 604800000000000, 3600000000000, '30095c71-380b-457a-8995-97b8ee6e5307') ON CONFLICT DO NOTHING; -- -- Data for Name: template_versions; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.template_versions VALUES ('920baba5-4c64-4686-8b7d-d1bef5683eae', '4cc1f466-f326-477e-8762-9d0c6781fc56', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:03:31.676657+02', '0001-01-01 01:39:49+01:39:49', 'trusting_lamport1', '--- +INSERT INTO template_versions VALUES ('920baba5-4c64-4686-8b7d-d1bef5683eae', '4cc1f466-f326-477e-8762-9d0c6781fc56', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:03:31.676657+02', '0001-01-01 01:39:49+01:39:49', 'trusting_lamport1', '--- name: Develop code-server in Docker description: Run code-server in a Docker development environment tags: [local, docker] @@ -792,7 +792,7 @@ This template has the following predefined parameters: - `docker_arch`: Architecture of the host running Docker. This can be `amd64`, `arm64`, or `armv7`. ', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0') ON CONFLICT DO NOTHING; -INSERT INTO public.template_versions VALUES ('4e681a60-83da-42c2-902e-6535376ebb77', '4cc1f466-f326-477e-8762-9d0c6781fc56', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:07:52.490041+02', '2022-11-02 13:07:52.950734+02', 'thirsty_williamson6', '--- +INSERT INTO template_versions VALUES ('4e681a60-83da-42c2-902e-6535376ebb77', '4cc1f466-f326-477e-8762-9d0c6781fc56', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:07:52.490041+02', '2022-11-02 13:07:52.950734+02', 'thirsty_williamson6', '--- name: Develop code-server in Docker description: Run code-server in a Docker development environment tags: [local, docker] @@ -824,85 +824,85 @@ This template has the following predefined parameters: -- Data for Name: workspace_resources; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.workspace_resources VALUES ('0ff953c0-92a6-4fe6-a415-eb0139a36ad1', '2022-11-02 13:03:45.043403+02', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('82d290df-0aa7-4514-9db8-e4e573442c6e', '2022-11-02 13:03:45.050988+02', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('1188c596-990b-4754-9e36-aaa013b9ea9a', '2022-11-02 13:03:45.053839+02', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', 'stop', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('de5e9eeb-f2d1-4fae-a0f4-3966f1a15554', '2022-11-02 13:04:14.068501+02', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('46b830e7-0b13-44f4-90ef-0e29e8f948e3', '2022-11-02 13:04:14.074875+02', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('e1dde2c9-bfff-485b-9b85-9ce53a59b3ee', '2022-11-02 13:04:22.824216+02', '52a90399-a53d-4644-be3c-47ee18a5716e', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('9f9b17b8-a7eb-491b-adee-4224f1dca047', '2022-11-02 13:04:22.826459+02', '52a90399-a53d-4644-be3c-47ee18a5716e', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('d14ad764-3385-4276-8201-0d70935e4e50', '2022-11-02 13:04:51.507424+02', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', 'stop', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('578765bb-bf7c-49b6-9d3e-5d089d68180d', '2022-11-02 13:04:58.764535+02', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('c192783d-bbdf-4a52-bd30-3da3321da46b', '2022-11-02 13:04:58.76647+02', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('93609df1-39ab-46e8-8fba-4d207cdbe461', '2022-11-02 13:05:43.879156+02', '4de9c41c-eb10-435a-8742-e8725e926a9d', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('03e8a6e1-4ec2-4115-9595-b0b6a0cbba82', '2022-11-02 13:05:43.881237+02', '4de9c41c-eb10-435a-8742-e8725e926a9d', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('e670ea5b-8a4e-4e5f-92e7-a46901474f26', '2022-11-02 13:06:08.661894+02', '424a58cb-61d6-4627-9907-613c396c4a38', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('4888064d-263c-4022-abdb-8816ac433c36', '2022-11-02 13:06:08.662461+02', '424a58cb-61d6-4627-9907-613c396c4a38', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('f8e41551-2add-4243-b174-ba5525ef3468', '2022-11-02 13:07:57.523213+02', 'f042af17-e3a2-4194-af67-e416302bc860', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('0405ee7f-ac1f-4ef3-839b-56476f9fc724', '2022-11-02 13:07:57.529989+02', 'f042af17-e3a2-4194-af67-e416302bc860', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('566883fd-d61f-427a-bbc7-16566923e613', '2022-11-02 13:07:57.531917+02', 'f042af17-e3a2-4194-af67-e416302bc860', 'stop', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('35a7cc19-0816-40bd-930b-1afe28dfc656', '2022-11-02 13:08:10.803507+02', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('63d5f2c3-232f-4b83-b7c1-c148b773df23', '2022-11-02 13:08:10.80546+02', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('82efa58b-dafc-4cbd-b3d5-47229e151720', '2022-11-02 13:09:54.752163+02', '52874f66-89cc-4e6b-8066-80ba21ad9e57', 'stop', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_resources VALUES ('8090fa9e-bc58-4dc4-ab96-053ea4e6fb62', '2022-11-02 13:10:45.608721+02', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', 'stop', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('0ff953c0-92a6-4fe6-a415-eb0139a36ad1', '2022-11-02 13:03:45.043403+02', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('82d290df-0aa7-4514-9db8-e4e573442c6e', '2022-11-02 13:03:45.050988+02', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('1188c596-990b-4754-9e36-aaa013b9ea9a', '2022-11-02 13:03:45.053839+02', 'f1392ef5-2502-4474-9f0c-98b5cad6edf0', 'stop', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('de5e9eeb-f2d1-4fae-a0f4-3966f1a15554', '2022-11-02 13:04:14.068501+02', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('46b830e7-0b13-44f4-90ef-0e29e8f948e3', '2022-11-02 13:04:14.074875+02', '3013ee6d-3c8f-4dcf-8271-01fd1e88aba6', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('e1dde2c9-bfff-485b-9b85-9ce53a59b3ee', '2022-11-02 13:04:22.824216+02', '52a90399-a53d-4644-be3c-47ee18a5716e', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('9f9b17b8-a7eb-491b-adee-4224f1dca047', '2022-11-02 13:04:22.826459+02', '52a90399-a53d-4644-be3c-47ee18a5716e', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('d14ad764-3385-4276-8201-0d70935e4e50', '2022-11-02 13:04:51.507424+02', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', 'stop', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('578765bb-bf7c-49b6-9d3e-5d089d68180d', '2022-11-02 13:04:58.764535+02', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('c192783d-bbdf-4a52-bd30-3da3321da46b', '2022-11-02 13:04:58.76647+02', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('93609df1-39ab-46e8-8fba-4d207cdbe461', '2022-11-02 13:05:43.879156+02', '4de9c41c-eb10-435a-8742-e8725e926a9d', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('03e8a6e1-4ec2-4115-9595-b0b6a0cbba82', '2022-11-02 13:05:43.881237+02', '4de9c41c-eb10-435a-8742-e8725e926a9d', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('e670ea5b-8a4e-4e5f-92e7-a46901474f26', '2022-11-02 13:06:08.661894+02', '424a58cb-61d6-4627-9907-613c396c4a38', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('4888064d-263c-4022-abdb-8816ac433c36', '2022-11-02 13:06:08.662461+02', '424a58cb-61d6-4627-9907-613c396c4a38', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('f8e41551-2add-4243-b174-ba5525ef3468', '2022-11-02 13:07:57.523213+02', 'f042af17-e3a2-4194-af67-e416302bc860', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('0405ee7f-ac1f-4ef3-839b-56476f9fc724', '2022-11-02 13:07:57.529989+02', 'f042af17-e3a2-4194-af67-e416302bc860', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('566883fd-d61f-427a-bbc7-16566923e613', '2022-11-02 13:07:57.531917+02', 'f042af17-e3a2-4194-af67-e416302bc860', 'stop', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('35a7cc19-0816-40bd-930b-1afe28dfc656', '2022-11-02 13:08:10.803507+02', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', 'start', 'docker_container', 'workspace') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('63d5f2c3-232f-4b83-b7c1-c148b773df23', '2022-11-02 13:08:10.80546+02', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', 'start', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('82efa58b-dafc-4cbd-b3d5-47229e151720', '2022-11-02 13:09:54.752163+02', '52874f66-89cc-4e6b-8066-80ba21ad9e57', 'stop', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; +INSERT INTO workspace_resources VALUES ('8090fa9e-bc58-4dc4-ab96-053ea4e6fb62', '2022-11-02 13:10:45.608721+02', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', 'stop', 'docker_volume', 'home_volume') ON CONFLICT DO NOTHING; -- -- Data for Name: workspace_agents; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.workspace_agents VALUES ('45e89705-e09d-4850-bcec-f9a937f5d78d', '2022-11-02 13:03:45.046432+02', '2022-11-02 13:03:45.046432+02', 'main', NULL, NULL, NULL, '0ff953c0-92a6-4fe6-a415-eb0139a36ad1', 'ffc107ef-7ded-4d80-b1a9-0c1d0bf7ccbf', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "default", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "default", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_agents VALUES ('a90934b3-34da-400a-a583-fa4dcb3b4668', '2022-11-02 13:04:14.070745+02', '2022-11-02 13:04:14.070745+02', 'main', NULL, NULL, NULL, 'de5e9eeb-f2d1-4fae-a0f4-3966f1a15554', '7703f4f1-210d-4c8f-a298-5803661cd7a7', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "admin", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "admin", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_agents VALUES ('5f8e48e4-1304-45bd-b91a-ab12c8bfc20f', '2022-11-02 13:04:58.765211+02', '2022-11-02 13:04:58.765211+02', 'main', '2022-11-02 13:04:58.931953+02', '2022-11-02 13:11:08.372146+02', NULL, '578765bb-bf7c-49b6-9d3e-5d089d68180d', 'ea71c16a-cf93-4c45-a1fd-100fc034528f', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "admin", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "admin", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_agents VALUES ('8fa17bbd-c48c-44c7-91ae-d4acbc755fad', '2022-11-02 13:04:22.825169+02', '2022-11-02 13:04:22.825169+02', 'main', '2022-11-02 13:04:23.19467+02', '2022-11-02 13:04:50.199604+02', NULL, 'e1dde2c9-bfff-485b-9b85-9ce53a59b3ee', '769edba1-bfcf-46bf-a5e8-0f2533d54f71', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "admin", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "admin", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_agents VALUES ('ef095280-d76a-422d-812f-0991ad06f2de', '2022-11-02 13:07:57.525832+02', '2022-11-02 13:07:57.525832+02', 'main', NULL, NULL, NULL, 'f8e41551-2add-4243-b174-ba5525ef3468', '73b3a1d3-7f78-43d6-8c97-4ccb70bb5cc2', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "default", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "default", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_agents VALUES ('a000d66f-e24e-4d40-9d2c-ec3809da3a55', '2022-11-02 13:06:08.662927+02', '2022-11-02 13:06:08.662927+02', 'main', '2022-11-02 13:06:08.700926+02', '2022-11-02 13:10:41.705193+02', NULL, '4888064d-263c-4022-abdb-8816ac433c36', '90cc130e-9f39-410a-a574-741e397877c1', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "oauthuser1", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "oauthuser1", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_agents VALUES ('7bb646eb-7baa-4300-a580-addb3fe89529', '2022-11-02 13:08:10.804142+02', '2022-11-02 13:08:10.804142+02', 'main', '2022-11-02 13:08:10.813078+02', '2022-11-02 13:09:52.819083+02', NULL, '35a7cc19-0816-40bd-930b-1afe28dfc656', 'e82e7cf1-9cfc-4c47-b591-22439f6c00cc', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "oauthuser1", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "oauthuser1", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_agents VALUES ('7a1ce5f8-8d00-431c-ad1b-97a846512804', '2022-11-02 13:05:43.879791+02', '2022-11-02 13:05:43.879791+02', 'main', '2022-11-02 13:05:43.891545+02', '2022-11-02 13:08:07.898132+02', NULL, '93609df1-39ab-46e8-8fba-4d207cdbe461', 'c427d112-23d8-4634-994e-d65a79e718fc', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "oauthuser1", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "oauthuser1", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; +INSERT INTO workspace_agents VALUES ('45e89705-e09d-4850-bcec-f9a937f5d78d', '2022-11-02 13:03:45.046432+02', '2022-11-02 13:03:45.046432+02', 'main', NULL, NULL, NULL, '0ff953c0-92a6-4fe6-a415-eb0139a36ad1', 'ffc107ef-7ded-4d80-b1a9-0c1d0bf7ccbf', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "default", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "default", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; +INSERT INTO workspace_agents VALUES ('a90934b3-34da-400a-a583-fa4dcb3b4668', '2022-11-02 13:04:14.070745+02', '2022-11-02 13:04:14.070745+02', 'main', NULL, NULL, NULL, 'de5e9eeb-f2d1-4fae-a0f4-3966f1a15554', '7703f4f1-210d-4c8f-a298-5803661cd7a7', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "admin", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "admin", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; +INSERT INTO workspace_agents VALUES ('5f8e48e4-1304-45bd-b91a-ab12c8bfc20f', '2022-11-02 13:04:58.765211+02', '2022-11-02 13:04:58.765211+02', 'main', '2022-11-02 13:04:58.931953+02', '2022-11-02 13:11:08.372146+02', NULL, '578765bb-bf7c-49b6-9d3e-5d089d68180d', 'ea71c16a-cf93-4c45-a1fd-100fc034528f', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "admin", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "admin", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; +INSERT INTO workspace_agents VALUES ('8fa17bbd-c48c-44c7-91ae-d4acbc755fad', '2022-11-02 13:04:22.825169+02', '2022-11-02 13:04:22.825169+02', 'main', '2022-11-02 13:04:23.19467+02', '2022-11-02 13:04:50.199604+02', NULL, 'e1dde2c9-bfff-485b-9b85-9ce53a59b3ee', '769edba1-bfcf-46bf-a5e8-0f2533d54f71', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "admin", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "admin", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; +INSERT INTO workspace_agents VALUES ('ef095280-d76a-422d-812f-0991ad06f2de', '2022-11-02 13:07:57.525832+02', '2022-11-02 13:07:57.525832+02', 'main', NULL, NULL, NULL, 'f8e41551-2add-4243-b174-ba5525ef3468', '73b3a1d3-7f78-43d6-8c97-4ccb70bb5cc2', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "default", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "default", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; +INSERT INTO workspace_agents VALUES ('a000d66f-e24e-4d40-9d2c-ec3809da3a55', '2022-11-02 13:06:08.662927+02', '2022-11-02 13:06:08.662927+02', 'main', '2022-11-02 13:06:08.700926+02', '2022-11-02 13:10:41.705193+02', NULL, '4888064d-263c-4022-abdb-8816ac433c36', '90cc130e-9f39-410a-a574-741e397877c1', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "oauthuser1", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "oauthuser1", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; +INSERT INTO workspace_agents VALUES ('7bb646eb-7baa-4300-a580-addb3fe89529', '2022-11-02 13:08:10.804142+02', '2022-11-02 13:08:10.804142+02', 'main', '2022-11-02 13:08:10.813078+02', '2022-11-02 13:09:52.819083+02', NULL, '35a7cc19-0816-40bd-930b-1afe28dfc656', 'e82e7cf1-9cfc-4c47-b591-22439f6c00cc', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "oauthuser1", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "oauthuser1", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; +INSERT INTO workspace_agents VALUES ('7a1ce5f8-8d00-431c-ad1b-97a846512804', '2022-11-02 13:05:43.879791+02', '2022-11-02 13:05:43.879791+02', 'main', '2022-11-02 13:05:43.891545+02', '2022-11-02 13:08:07.898132+02', NULL, '93609df1-39ab-46e8-8fba-4d207cdbe461', 'c427d112-23d8-4634-994e-d65a79e718fc', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "oauthuser1", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "oauthuser1", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '') ON CONFLICT DO NOTHING; -- -- Data for Name: workspace_apps; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.workspace_apps VALUES ('36b65d0c-042b-4653-863a-655ee739861c', '2022-11-02 13:03:45.048584+02', '45e89705-e09d-4850-bcec-f9a937f5d78d', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_apps VALUES ('347909cf-e3a9-4548-a800-73b4d166305a', '2022-11-02 13:04:14.072762+02', 'a90934b3-34da-400a-a583-fa4dcb3b4668', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_apps VALUES ('a47965a2-0a25-4810-8cc9-d283c86ab34c', '2022-11-02 13:04:22.825963+02', '8fa17bbd-c48c-44c7-91ae-d4acbc755fad', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_apps VALUES ('6ee01bda-a857-413a-8755-93365a955ece', '2022-11-02 13:04:58.765877+02', '5f8e48e4-1304-45bd-b91a-ab12c8bfc20f', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_apps VALUES ('ed0b4a8b-9cb3-45f9-ac86-31038d192b66', '2022-11-02 13:05:43.8806+02', '7a1ce5f8-8d00-431c-ad1b-97a846512804', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_apps VALUES ('9a5bf95c-dc6d-4780-9a62-6cb5ffe8d2a6', '2022-11-02 13:06:08.663494+02', 'a000d66f-e24e-4d40-9d2c-ec3809da3a55', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_apps VALUES ('7e5670b0-0643-4622-a6f2-3986b39ee260', '2022-11-02 13:07:57.52769+02', 'ef095280-d76a-422d-812f-0991ad06f2de', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_apps VALUES ('69bbcdf0-c193-4e91-b998-8bfa97425d27', '2022-11-02 13:08:10.804861+02', '7bb646eb-7baa-4300-a580-addb3fe89529', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; +INSERT INTO workspace_apps VALUES ('36b65d0c-042b-4653-863a-655ee739861c', '2022-11-02 13:03:45.048584+02', '45e89705-e09d-4850-bcec-f9a937f5d78d', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; +INSERT INTO workspace_apps VALUES ('347909cf-e3a9-4548-a800-73b4d166305a', '2022-11-02 13:04:14.072762+02', 'a90934b3-34da-400a-a583-fa4dcb3b4668', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; +INSERT INTO workspace_apps VALUES ('a47965a2-0a25-4810-8cc9-d283c86ab34c', '2022-11-02 13:04:22.825963+02', '8fa17bbd-c48c-44c7-91ae-d4acbc755fad', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; +INSERT INTO workspace_apps VALUES ('6ee01bda-a857-413a-8755-93365a955ece', '2022-11-02 13:04:58.765877+02', '5f8e48e4-1304-45bd-b91a-ab12c8bfc20f', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; +INSERT INTO workspace_apps VALUES ('ed0b4a8b-9cb3-45f9-ac86-31038d192b66', '2022-11-02 13:05:43.8806+02', '7a1ce5f8-8d00-431c-ad1b-97a846512804', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; +INSERT INTO workspace_apps VALUES ('9a5bf95c-dc6d-4780-9a62-6cb5ffe8d2a6', '2022-11-02 13:06:08.663494+02', 'a000d66f-e24e-4d40-9d2c-ec3809da3a55', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; +INSERT INTO workspace_apps VALUES ('7e5670b0-0643-4622-a6f2-3986b39ee260', '2022-11-02 13:07:57.52769+02', 'ef095280-d76a-422d-812f-0991ad06f2de', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; +INSERT INTO workspace_apps VALUES ('69bbcdf0-c193-4e91-b998-8bfa97425d27', '2022-11-02 13:08:10.804861+02', '7bb646eb-7baa-4300-a580-addb3fe89529', 'code-server', '/icon/code.svg', NULL, 'http://localhost:8080/?folder=/home/coder', false) ON CONFLICT DO NOTHING; -- -- Data for Name: workspaces; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.workspaces VALUES ('3a9a1feb-e89d-457c-9d53-ac751b198ebe', '2022-11-02 13:04:19.044082+02', '2022-11-02 13:04:19.044082+02', '30095c71-380b-457a-8995-97b8ee6e5307', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '4cc1f466-f326-477e-8762-9d0c6781fc56', false, 'my-workspace', 'CRON_TZ=UTC 0 9 * * MON-FRI', 28800000000000) ON CONFLICT DO NOTHING; -INSERT INTO public.workspaces VALUES ('b90547be-8870-4d68-8184-e8b2242b7c01', '2022-11-02 13:05:40.181124+02', '2022-11-02 13:05:40.181124+02', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '4cc1f466-f326-477e-8762-9d0c6781fc56', false, 'test1', NULL, 604800000000000) ON CONFLICT DO NOTHING; -INSERT INTO public.workspaces VALUES ('2d72d32e-3021-4843-b582-d962fee897e2', '2022-11-02 13:06:03.554876+02', '2022-11-02 13:06:03.554876+02', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '4cc1f466-f326-477e-8762-9d0c6781fc56', false, 'scheduled', 'CRON_TZ=Europe/Helsinki 30 09 * * *', 288000000000000) ON CONFLICT DO NOTHING; +INSERT INTO workspaces VALUES ('3a9a1feb-e89d-457c-9d53-ac751b198ebe', '2022-11-02 13:04:19.044082+02', '2022-11-02 13:04:19.044082+02', '30095c71-380b-457a-8995-97b8ee6e5307', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '4cc1f466-f326-477e-8762-9d0c6781fc56', false, 'my-workspace', 'CRON_TZ=UTC 0 9 * * MON-FRI', 28800000000000) ON CONFLICT DO NOTHING; +INSERT INTO workspaces VALUES ('b90547be-8870-4d68-8184-e8b2242b7c01', '2022-11-02 13:05:40.181124+02', '2022-11-02 13:05:40.181124+02', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '4cc1f466-f326-477e-8762-9d0c6781fc56', false, 'test1', NULL, 604800000000000) ON CONFLICT DO NOTHING; +INSERT INTO workspaces VALUES ('2d72d32e-3021-4843-b582-d962fee897e2', '2022-11-02 13:06:03.554876+02', '2022-11-02 13:06:03.554876+02', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '4cc1f466-f326-477e-8762-9d0c6781fc56', false, 'scheduled', 'CRON_TZ=Europe/Helsinki 30 09 * * *', 288000000000000) ON CONFLICT DO NOTHING; -- -- Data for Name: workspace_builds; Type: TABLE DATA; Schema: public; Owner: coder -- -INSERT INTO public.workspace_builds VALUES ('a8c0b8c5-c9a8-4f33-93a4-8142e6858244', '2022-11-02 13:04:19.044082+02', '2022-11-02 13:04:22.82111+02', '3a9a1feb-e89d-457c-9d53-ac751b198ebe', '920baba5-4c64-4686-8b7d-d1bef5683eae', 'wizardly_lumiere8', 1, 'start', '30095c71-380b-457a-8995-97b8ee6e5307', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20352c0a2020226c696e65616765223a202230633238626130332d623932322d333431342d353766642d656631336166333061373662222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202231376363313761662d386262622d343736312d616433392d646136326164353132633233222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202233613961316665622d653839642d343537632d396435332d616337353162313938656265222c0a202020202020202020202020226e616d65223a20226d792d776f726b7370616365222c0a202020202020202020202020226f776e6572223a202261646d696e222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202233303039356337312d333830622d343537612d383939352d393762386565366535333037222c0a2020202020202020202020202273746172745f636f756e74223a20312c0a202020202020202020202020227472616e736974696f6e223a20227374617274220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a202261646d696e222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a202261646d696e220a2020202020202020202020207d2c0a202020202020202020202020226964223a202230393037646162392d313930392d346430332d626666642d393336613261303561323261222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202237363965646261312d626663662d343662662d613565382d306632353333643534663731220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202230393037646162392d313930392d346430332d626666642d393336613261303561323261222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202235663163326261332d366232362d343039632d393739302d646635366236326566636263222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f636f6e7461696e6572222c0a202020202020226e616d65223a2022776f726b7370616365222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022696e6465785f6b6579223a20302c0a2020202020202020202022736368656d615f76657273696f6e223a20322c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022617474616368223a2066616c73652c0a20202020202020202020202022627269646765223a2022222c0a202020202020202020202020226361706162696c6974696573223a205b5d2c0a20202020202020202020202022636f6d6d616e64223a205b5d2c0a20202020202020202020202022636f6e7461696e65725f6c6f6773223a206e756c6c2c0a202020202020202020202020226370755f736574223a2022222c0a202020202020202020202020226370755f736861726573223a20302c0a2020202020202020202020202264657374726f795f67726163655f7365636f6e6473223a206e756c6c2c0a2020202020202020202020202264657669636573223a205b5d2c0a20202020202020202020202022646e73223a205b0a202020202020202020202020202022312e312e312e31220a2020202020202020202020205d2c0a20202020202020202020202022646e735f6f707473223a206e756c6c2c0a20202020202020202020202022646e735f736561726368223a206e756c6c2c0a20202020202020202020202022646f6d61696e6e616d65223a2022222c0a20202020202020202020202022656e747279706f696e74223a205b0a2020202020202020202020202020227368222c0a2020202020202020202020202020222d63222c0a20202020202020202020202020202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74220a2020202020202020202020205d2c0a20202020202020202020202022656e76223a205b0a202020202020202020202020202022434f4445525f4147454e545f544f4b454e3d37363965646261312d626663662d343662662d613565382d306632353333643534663731220a2020202020202020202020205d2c0a20202020202020202020202022657869745f636f6465223a206e756c6c2c0a2020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202267707573223a206e756c6c2c0a2020202020202020202020202267726f75705f616464223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a206e756c6c2c0a20202020202020202020202022686f7374223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022686f7374223a2022686f73742e646f636b65722e696e7465726e616c222c0a20202020202020202020202020202020226970223a2022686f73742d67617465776179220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022686f73746e616d65223a20226d792d776f726b7370616365222c0a202020202020202020202020226964223a202261316666353336343732653965666464356564393064366161666332383636393739393636383030376162393338323335363761633038303636383232323434222c0a20202020202020202020202022696d616765223a20227368613235363a32663565366139303562653439366363373931306133643838613362646536343033333733616462623734323861326364346262303938346236343364373731222c0a20202020202020202020202022696e6974223a2066616c73652c0a2020202020202020202020202269705f61646472657373223a20223137322e31372e302e32222c0a2020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a202020202020202020202020226970635f6d6f6465223a202270726976617465222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226c696e6b73223a206e756c6c2c0a202020202020202020202020226c6f675f647269766572223a20226a6f75726e616c64222c0a202020202020202020202020226c6f675f6f707473223a206e756c6c2c0a202020202020202020202020226c6f6773223a2066616c73652c0a202020202020202020202020226d61785f72657472795f636f756e74223a20302c0a202020202020202020202020226d656d6f7279223a20302c0a202020202020202020202020226d656d6f72795f73776170223a20302c0a202020202020202020202020226d6f756e7473223a205b5d2c0a202020202020202020202020226d7573745f72756e223a20747275652c0a202020202020202020202020226e616d65223a2022636f6465722d61646d696e2d6d792d776f726b7370616365222c0a202020202020202020202020226e6574776f726b5f616c696173223a206e756c6c2c0a202020202020202020202020226e6574776f726b5f64617461223a205b0a20202020202020202020202020207b0a202020202020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202020202022676c6f62616c5f697076365f61646472657373223a2022222c0a2020202020202020202020202020202022676c6f62616c5f697076365f7072656669785f6c656e677468223a20302c0a202020202020202020202020202020202269705f61646472657373223a20223137322e31372e302e32222c0a202020202020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a2020202020202020202020202020202022697076365f67617465776179223a2022222c0a20202020202020202020202020202020226e6574776f726b5f6e616d65223a2022627269646765220a20202020202020202020202020207d0a2020202020202020202020205d2c0a202020202020202020202020226e6574776f726b5f6d6f6465223a202264656661756c74222c0a202020202020202020202020226e6574776f726b73223a206e756c6c2c0a202020202020202020202020226e6574776f726b735f616476616e636564223a205b5d2c0a202020202020202020202020227069645f6d6f6465223a2022222c0a20202020202020202020202022706f727473223a205b5d2c0a2020202020202020202020202270726976696c65676564223a2066616c73652c0a202020202020202020202020227075626c6973685f616c6c5f706f727473223a2066616c73652c0a20202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202272656d6f76655f766f6c756d6573223a20747275652c0a2020202020202020202020202272657374617274223a20226e6f222c0a20202020202020202020202022726d223a2066616c73652c0a2020202020202020202020202272756e74696d65223a202272756e63222c0a2020202020202020202020202273656375726974795f6f707473223a205b5d2c0a2020202020202020202020202273686d5f73697a65223a2036342c0a202020202020202020202020227374617274223a20747275652c0a20202020202020202020202022737464696e5f6f70656e223a2066616c73652c0a2020202020202020202020202273746f705f7369676e616c223a2022222c0a2020202020202020202020202273746f705f74696d656f7574223a20302c0a2020202020202020202020202273746f726167655f6f707473223a206e756c6c2c0a2020202020202020202020202273797363746c73223a206e756c6c2c0a20202020202020202020202022746d706673223a206e756c6c2c0a20202020202020202020202022747479223a2066616c73652c0a20202020202020202020202022756c696d6974223a205b5d2c0a2020202020202020202020202275706c6f6164223a205b5d2c0a2020202020202020202020202275736572223a202231303030222c0a20202020202020202020202022757365726e735f6d6f6465223a2022222c0a20202020202020202020202022766f6c756d6573223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022636f6e7461696e65725f70617468223a20222f686f6d652f636f6465722f222c0a202020202020202020202020202020202266726f6d5f636f6e7461696e6572223a2022222c0a2020202020202020202020202020202022686f73745f70617468223a2022222c0a2020202020202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202020202022766f6c756d655f6e616d65223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022776f726b696e675f646972223a20222f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b0a2020202020202020202020205b0a20202020202020202020202020207b0a202020202020202020202020202020202274797065223a20226765745f61747472222c0a202020202020202020202020202020202276616c7565223a2022656e76220a20202020202020202020202020207d0a2020202020202020202020205d0a202020202020202020205d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a496966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65222c0a20202020202020202020202022646f636b65725f766f6c756d652e686f6d655f766f6c756d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a206e756c6c2c0a202020202020202020202020226964223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 21:04:22.82111+02') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_builds VALUES ('ea36844d-8eb6-41a2-a237-e9a8ae3f99ea', '2022-11-02 13:04:48.073344+02', '2022-11-02 13:04:51.504141+02', '3a9a1feb-e89d-457c-9d53-ac751b198ebe', '920baba5-4c64-4686-8b7d-d1bef5683eae', 'vigorous_matsumoto6', 2, 'stop', '30095c71-380b-457a-8995-97b8ee6e5307', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20372c0a2020226c696e65616765223a202230633238626130332d623932322d333431342d353766642d656631336166333061373662222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202236396438633234372d613463632d346465362d383663302d636530373332353533313661222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202233613961316665622d653839642d343537632d396435332d616337353162313938656265222c0a202020202020202020202020226e616d65223a20226d792d776f726b7370616365222c0a202020202020202020202020226f776e6572223a202261646d696e222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202233303039356337312d333830622d343537612d383939352d393762386565366535333037222c0a2020202020202020202020202273746172745f636f756e74223a20302c0a202020202020202020202020227472616e736974696f6e223a202273746f70220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a202261646d696e222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a202261646d696e220a2020202020202020202020207d2c0a202020202020202020202020226964223a202230393037646162392d313930392d346430332d626666642d393336613261303561323261222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202263653730353961352d633637622d343163372d616330302d666266643630646637356465220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202230393037646162392d313930392d346430332d626666642d393336613261303561323261222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202235663163326261332d366232362d343039632d393739302d646635366236326566636263222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a207b7d2c0a202020202020202020202020226964223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 21:04:51.504141+02') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_builds VALUES ('c1d2c9d5-6f30-4cd0-9ac5-6bf1c6039988', '2022-11-02 13:04:55.224578+02', '2022-11-02 13:04:58.762449+02', '3a9a1feb-e89d-457c-9d53-ac751b198ebe', '920baba5-4c64-4686-8b7d-d1bef5683eae', 'romantic_johnson8', 3, 'start', '30095c71-380b-457a-8995-97b8ee6e5307', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20392c0a2020226c696e65616765223a202230633238626130332d623932322d333431342d353766642d656631336166333061373662222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202262363561373637662d373139612d343838332d613138362d616562316163303563623235222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202233613961316665622d653839642d343537632d396435332d616337353162313938656265222c0a202020202020202020202020226e616d65223a20226d792d776f726b7370616365222c0a202020202020202020202020226f776e6572223a202261646d696e222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202233303039356337312d333830622d343537612d383939352d393762386565366535333037222c0a2020202020202020202020202273746172745f636f756e74223a20312c0a202020202020202020202020227472616e736974696f6e223a20227374617274220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a202261646d696e222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a202261646d696e220a2020202020202020202020207d2c0a202020202020202020202020226964223a202230393037646162392d313930392d346430332d626666642d393336613261303561323261222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202265613731633136612d636639332d346334352d613166642d313030666330333435323866220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202230393037646162392d313930392d346430332d626666642d393336613261303561323261222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202235663163326261332d366232362d343039632d393739302d646635366236326566636263222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f636f6e7461696e6572222c0a202020202020226e616d65223a2022776f726b7370616365222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022696e6465785f6b6579223a20302c0a2020202020202020202022736368656d615f76657273696f6e223a20322c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022617474616368223a2066616c73652c0a20202020202020202020202022627269646765223a2022222c0a202020202020202020202020226361706162696c6974696573223a205b5d2c0a20202020202020202020202022636f6d6d616e64223a205b5d2c0a20202020202020202020202022636f6e7461696e65725f6c6f6773223a206e756c6c2c0a202020202020202020202020226370755f736574223a2022222c0a202020202020202020202020226370755f736861726573223a20302c0a2020202020202020202020202264657374726f795f67726163655f7365636f6e6473223a206e756c6c2c0a2020202020202020202020202264657669636573223a205b5d2c0a20202020202020202020202022646e73223a205b0a202020202020202020202020202022312e312e312e31220a2020202020202020202020205d2c0a20202020202020202020202022646e735f6f707473223a206e756c6c2c0a20202020202020202020202022646e735f736561726368223a206e756c6c2c0a20202020202020202020202022646f6d61696e6e616d65223a2022222c0a20202020202020202020202022656e747279706f696e74223a205b0a2020202020202020202020202020227368222c0a2020202020202020202020202020222d63222c0a20202020202020202020202020202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74220a2020202020202020202020205d2c0a20202020202020202020202022656e76223a205b0a202020202020202020202020202022434f4445525f4147454e545f544f4b454e3d65613731633136612d636639332d346334352d613166642d313030666330333435323866220a2020202020202020202020205d2c0a20202020202020202020202022657869745f636f6465223a206e756c6c2c0a2020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202267707573223a206e756c6c2c0a2020202020202020202020202267726f75705f616464223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a206e756c6c2c0a20202020202020202020202022686f7374223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022686f7374223a2022686f73742e646f636b65722e696e7465726e616c222c0a20202020202020202020202020202020226970223a2022686f73742d67617465776179220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022686f73746e616d65223a20226d792d776f726b7370616365222c0a202020202020202020202020226964223a202264623461656364653635376231323639333965386261643431326239353333313463643133313666653762336633643036313230336430613731653033376538222c0a20202020202020202020202022696d616765223a20227368613235363a32663565366139303562653439366363373931306133643838613362646536343033333733616462623734323861326364346262303938346236343364373731222c0a20202020202020202020202022696e6974223a2066616c73652c0a2020202020202020202020202269705f61646472657373223a20223137322e31372e302e32222c0a2020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a202020202020202020202020226970635f6d6f6465223a202270726976617465222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226c696e6b73223a206e756c6c2c0a202020202020202020202020226c6f675f647269766572223a20226a6f75726e616c64222c0a202020202020202020202020226c6f675f6f707473223a206e756c6c2c0a202020202020202020202020226c6f6773223a2066616c73652c0a202020202020202020202020226d61785f72657472795f636f756e74223a20302c0a202020202020202020202020226d656d6f7279223a20302c0a202020202020202020202020226d656d6f72795f73776170223a20302c0a202020202020202020202020226d6f756e7473223a205b5d2c0a202020202020202020202020226d7573745f72756e223a20747275652c0a202020202020202020202020226e616d65223a2022636f6465722d61646d696e2d6d792d776f726b7370616365222c0a202020202020202020202020226e6574776f726b5f616c696173223a206e756c6c2c0a202020202020202020202020226e6574776f726b5f64617461223a205b0a20202020202020202020202020207b0a202020202020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202020202022676c6f62616c5f697076365f61646472657373223a2022222c0a2020202020202020202020202020202022676c6f62616c5f697076365f7072656669785f6c656e677468223a20302c0a202020202020202020202020202020202269705f61646472657373223a20223137322e31372e302e32222c0a202020202020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a2020202020202020202020202020202022697076365f67617465776179223a2022222c0a20202020202020202020202020202020226e6574776f726b5f6e616d65223a2022627269646765220a20202020202020202020202020207d0a2020202020202020202020205d2c0a202020202020202020202020226e6574776f726b5f6d6f6465223a202264656661756c74222c0a202020202020202020202020226e6574776f726b73223a206e756c6c2c0a202020202020202020202020226e6574776f726b735f616476616e636564223a205b5d2c0a202020202020202020202020227069645f6d6f6465223a2022222c0a20202020202020202020202022706f727473223a205b5d2c0a2020202020202020202020202270726976696c65676564223a2066616c73652c0a202020202020202020202020227075626c6973685f616c6c5f706f727473223a2066616c73652c0a20202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202272656d6f76655f766f6c756d6573223a20747275652c0a2020202020202020202020202272657374617274223a20226e6f222c0a20202020202020202020202022726d223a2066616c73652c0a2020202020202020202020202272756e74696d65223a202272756e63222c0a2020202020202020202020202273656375726974795f6f707473223a205b5d2c0a2020202020202020202020202273686d5f73697a65223a2036342c0a202020202020202020202020227374617274223a20747275652c0a20202020202020202020202022737464696e5f6f70656e223a2066616c73652c0a2020202020202020202020202273746f705f7369676e616c223a2022222c0a2020202020202020202020202273746f705f74696d656f7574223a20302c0a2020202020202020202020202273746f726167655f6f707473223a206e756c6c2c0a2020202020202020202020202273797363746c73223a206e756c6c2c0a20202020202020202020202022746d706673223a206e756c6c2c0a20202020202020202020202022747479223a2066616c73652c0a20202020202020202020202022756c696d6974223a205b5d2c0a2020202020202020202020202275706c6f6164223a205b5d2c0a2020202020202020202020202275736572223a202231303030222c0a20202020202020202020202022757365726e735f6d6f6465223a2022222c0a20202020202020202020202022766f6c756d6573223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022636f6e7461696e65725f70617468223a20222f686f6d652f636f6465722f222c0a202020202020202020202020202020202266726f6d5f636f6e7461696e6572223a2022222c0a2020202020202020202020202020202022686f73745f70617468223a2022222c0a2020202020202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202020202022766f6c756d655f6e616d65223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022776f726b696e675f646972223a20222f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b0a2020202020202020202020205b0a20202020202020202020202020207b0a202020202020202020202020202020202274797065223a20226765745f61747472222c0a202020202020202020202020202020202276616c7565223a2022656e76220a20202020202020202020202020207d0a2020202020202020202020205d0a202020202020202020205d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a496966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65222c0a20202020202020202020202022646f636b65725f766f6c756d652e686f6d655f766f6c756d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a207b7d2c0a202020202020202020202020226964223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 21:04:58.762449+02') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_builds VALUES ('c7c0a371-db1e-4f10-ab34-6779f573554c', '2022-11-02 13:05:40.181124+02', '2022-11-02 13:05:43.876957+02', 'b90547be-8870-4d68-8184-e8b2242b7c01', '920baba5-4c64-4686-8b7d-d1bef5683eae', 'vigilant_shtern5', 1, 'start', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20352c0a2020226c696e65616765223a202231623532313632352d643737322d333863302d326634652d343163386635333763323638222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202266653431386538312d623861302d346636392d383765612d613061353937373435653239222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202262393035343762652d383837302d346436382d383138342d653862323234326237633031222c0a202020202020202020202020226e616d65223a20227465737431222c0a202020202020202020202020226f776e6572223a2022696d746865737570657276697a72222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202230656439626566632d343931312d346363662d613865322d353539626637326461613934222c0a2020202020202020202020202273746172745f636f756e74223a20312c0a202020202020202020202020227472616e736974696f6e223a20227374617274220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a2022696d746865737570657276697a72222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a2022696d746865737570657276697a72220a2020202020202020202020207d2c0a202020202020202020202020226964223a202236313337353939642d346164362d346535382d623066612d613364316138386661356139222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202263343237643131322d323364382d343633342d393934652d643635613739653731386663220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202236313337353939642d346164362d346535382d623066612d613364316138386661356139222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202233613538356263372d373565362d343038652d623062632d646336346534643362653061222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f636f6e7461696e6572222c0a202020202020226e616d65223a2022776f726b7370616365222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022696e6465785f6b6579223a20302c0a2020202020202020202022736368656d615f76657273696f6e223a20322c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022617474616368223a2066616c73652c0a20202020202020202020202022627269646765223a2022222c0a202020202020202020202020226361706162696c6974696573223a205b5d2c0a20202020202020202020202022636f6d6d616e64223a205b5d2c0a20202020202020202020202022636f6e7461696e65725f6c6f6773223a206e756c6c2c0a202020202020202020202020226370755f736574223a2022222c0a202020202020202020202020226370755f736861726573223a20302c0a2020202020202020202020202264657374726f795f67726163655f7365636f6e6473223a206e756c6c2c0a2020202020202020202020202264657669636573223a205b5d2c0a20202020202020202020202022646e73223a205b0a202020202020202020202020202022312e312e312e31220a2020202020202020202020205d2c0a20202020202020202020202022646e735f6f707473223a206e756c6c2c0a20202020202020202020202022646e735f736561726368223a206e756c6c2c0a20202020202020202020202022646f6d61696e6e616d65223a2022222c0a20202020202020202020202022656e747279706f696e74223a205b0a2020202020202020202020202020227368222c0a2020202020202020202020202020222d63222c0a20202020202020202020202020202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74220a2020202020202020202020205d2c0a20202020202020202020202022656e76223a205b0a202020202020202020202020202022434f4445525f4147454e545f544f4b454e3d63343237643131322d323364382d343633342d393934652d643635613739653731386663220a2020202020202020202020205d2c0a20202020202020202020202022657869745f636f6465223a206e756c6c2c0a2020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202267707573223a206e756c6c2c0a2020202020202020202020202267726f75705f616464223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a206e756c6c2c0a20202020202020202020202022686f7374223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022686f7374223a2022686f73742e646f636b65722e696e7465726e616c222c0a20202020202020202020202020202020226970223a2022686f73742d67617465776179220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022686f73746e616d65223a20227465737431222c0a202020202020202020202020226964223a202265656439366463633135393832396239613931656531336132313036623366383539383839366639396437313432373635323736393134653632386538313039222c0a20202020202020202020202022696d616765223a20227368613235363a32663565366139303562653439366363373931306133643838613362646536343033333733616462623734323861326364346262303938346236343364373731222c0a20202020202020202020202022696e6974223a2066616c73652c0a2020202020202020202020202269705f61646472657373223a20223137322e31372e302e33222c0a2020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a202020202020202020202020226970635f6d6f6465223a202270726976617465222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226c696e6b73223a206e756c6c2c0a202020202020202020202020226c6f675f647269766572223a20226a6f75726e616c64222c0a202020202020202020202020226c6f675f6f707473223a206e756c6c2c0a202020202020202020202020226c6f6773223a2066616c73652c0a202020202020202020202020226d61785f72657472795f636f756e74223a20302c0a202020202020202020202020226d656d6f7279223a20302c0a202020202020202020202020226d656d6f72795f73776170223a20302c0a202020202020202020202020226d6f756e7473223a205b5d2c0a202020202020202020202020226d7573745f72756e223a20747275652c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d7465737431222c0a202020202020202020202020226e6574776f726b5f616c696173223a206e756c6c2c0a202020202020202020202020226e6574776f726b5f64617461223a205b0a20202020202020202020202020207b0a202020202020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202020202022676c6f62616c5f697076365f61646472657373223a2022222c0a2020202020202020202020202020202022676c6f62616c5f697076365f7072656669785f6c656e677468223a20302c0a202020202020202020202020202020202269705f61646472657373223a20223137322e31372e302e33222c0a202020202020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a2020202020202020202020202020202022697076365f67617465776179223a2022222c0a20202020202020202020202020202020226e6574776f726b5f6e616d65223a2022627269646765220a20202020202020202020202020207d0a2020202020202020202020205d2c0a202020202020202020202020226e6574776f726b5f6d6f6465223a202264656661756c74222c0a202020202020202020202020226e6574776f726b73223a206e756c6c2c0a202020202020202020202020226e6574776f726b735f616476616e636564223a205b5d2c0a202020202020202020202020227069645f6d6f6465223a2022222c0a20202020202020202020202022706f727473223a205b5d2c0a2020202020202020202020202270726976696c65676564223a2066616c73652c0a202020202020202020202020227075626c6973685f616c6c5f706f727473223a2066616c73652c0a20202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202272656d6f76655f766f6c756d6573223a20747275652c0a2020202020202020202020202272657374617274223a20226e6f222c0a20202020202020202020202022726d223a2066616c73652c0a2020202020202020202020202272756e74696d65223a202272756e63222c0a2020202020202020202020202273656375726974795f6f707473223a205b5d2c0a2020202020202020202020202273686d5f73697a65223a2036342c0a202020202020202020202020227374617274223a20747275652c0a20202020202020202020202022737464696e5f6f70656e223a2066616c73652c0a2020202020202020202020202273746f705f7369676e616c223a2022222c0a2020202020202020202020202273746f705f74696d656f7574223a20302c0a2020202020202020202020202273746f726167655f6f707473223a206e756c6c2c0a2020202020202020202020202273797363746c73223a206e756c6c2c0a20202020202020202020202022746d706673223a206e756c6c2c0a20202020202020202020202022747479223a2066616c73652c0a20202020202020202020202022756c696d6974223a205b5d2c0a2020202020202020202020202275706c6f6164223a205b5d2c0a2020202020202020202020202275736572223a202231303030222c0a20202020202020202020202022757365726e735f6d6f6465223a2022222c0a20202020202020202020202022766f6c756d6573223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022636f6e7461696e65725f70617468223a20222f686f6d652f636f6465722f222c0a202020202020202020202020202020202266726f6d5f636f6e7461696e6572223a2022222c0a2020202020202020202020202020202022686f73745f70617468223a2022222c0a2020202020202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202020202022766f6c756d655f6e616d65223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022776f726b696e675f646972223a20222f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b0a2020202020202020202020205b0a20202020202020202020202020207b0a202020202020202020202020202020202274797065223a20226765745f61747472222c0a202020202020202020202020202020202276616c7565223a2022656e76220a20202020202020202020202020207d0a2020202020202020202020205d0a202020202020202020205d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a496966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65222c0a20202020202020202020202022646f636b65725f766f6c756d652e686f6d655f766f6c756d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a206e756c6c2c0a202020202020202020202020226964223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d696d746865737570657276697a722d74657374312d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-09 13:05:43.876957+02') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_builds VALUES ('a7477610-c69b-46d6-97fb-d6a3425e1ab4', '2022-11-02 13:06:03.554876+02', '2022-11-02 13:06:08.659241+02', '2d72d32e-3021-4843-b582-d962fee897e2', '920baba5-4c64-4686-8b7d-d1bef5683eae', 'hopeful_herschel7', 1, 'start', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20352c0a2020226c696e65616765223a202230626637343762342d663639312d626461382d653066662d646336306230333566303932222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202231306330316135302d626563312d343436632d623339352d356431383466626133363964222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202232643732643332652d333032312d343834332d623538322d643936326665653839376532222c0a202020202020202020202020226e616d65223a20227363686564756c6564222c0a202020202020202020202020226f776e6572223a2022696d746865737570657276697a72222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202230656439626566632d343931312d346363662d613865322d353539626637326461613934222c0a2020202020202020202020202273746172745f636f756e74223a20312c0a202020202020202020202020227472616e736974696f6e223a20227374617274220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a2022696d746865737570657276697a72222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a2022696d746865737570657276697a72220a2020202020202020202020207d2c0a202020202020202020202020226964223a202238383431383734642d306335632d343961622d616231302d326336623865353039343333222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202239306363313330652d396633392d343130612d613537342d373431653339373837376331220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202238383431383734642d306335632d343961622d616231302d326336623865353039343333222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202238366163393065322d636338312d343164632d613136652d656563613534633861336337222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f636f6e7461696e6572222c0a202020202020226e616d65223a2022776f726b7370616365222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022696e6465785f6b6579223a20302c0a2020202020202020202022736368656d615f76657273696f6e223a20322c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022617474616368223a2066616c73652c0a20202020202020202020202022627269646765223a2022222c0a202020202020202020202020226361706162696c6974696573223a205b5d2c0a20202020202020202020202022636f6d6d616e64223a205b5d2c0a20202020202020202020202022636f6e7461696e65725f6c6f6773223a206e756c6c2c0a202020202020202020202020226370755f736574223a2022222c0a202020202020202020202020226370755f736861726573223a20302c0a2020202020202020202020202264657374726f795f67726163655f7365636f6e6473223a206e756c6c2c0a2020202020202020202020202264657669636573223a205b5d2c0a20202020202020202020202022646e73223a205b0a202020202020202020202020202022312e312e312e31220a2020202020202020202020205d2c0a20202020202020202020202022646e735f6f707473223a206e756c6c2c0a20202020202020202020202022646e735f736561726368223a206e756c6c2c0a20202020202020202020202022646f6d61696e6e616d65223a2022222c0a20202020202020202020202022656e747279706f696e74223a205b0a2020202020202020202020202020227368222c0a2020202020202020202020202020222d63222c0a20202020202020202020202020202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74220a2020202020202020202020205d2c0a20202020202020202020202022656e76223a205b0a202020202020202020202020202022434f4445525f4147454e545f544f4b454e3d39306363313330652d396633392d343130612d613537342d373431653339373837376331220a2020202020202020202020205d2c0a20202020202020202020202022657869745f636f6465223a206e756c6c2c0a2020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202267707573223a206e756c6c2c0a2020202020202020202020202267726f75705f616464223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a206e756c6c2c0a20202020202020202020202022686f7374223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022686f7374223a2022686f73742e646f636b65722e696e7465726e616c222c0a20202020202020202020202020202020226970223a2022686f73742d67617465776179220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022686f73746e616d65223a20227363686564756c6564222c0a202020202020202020202020226964223a202230333161646132656164343736366465303833333166303565643265356630643637343136363531373735376564663162393234383836623133353963616235222c0a20202020202020202020202022696d616765223a20227368613235363a32663565366139303562653439366363373931306133643838613362646536343033333733616462623734323861326364346262303938346236343364373731222c0a20202020202020202020202022696e6974223a2066616c73652c0a2020202020202020202020202269705f61646472657373223a20223137322e31372e302e34222c0a2020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a202020202020202020202020226970635f6d6f6465223a202270726976617465222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226c696e6b73223a206e756c6c2c0a202020202020202020202020226c6f675f647269766572223a20226a6f75726e616c64222c0a202020202020202020202020226c6f675f6f707473223a206e756c6c2c0a202020202020202020202020226c6f6773223a2066616c73652c0a202020202020202020202020226d61785f72657472795f636f756e74223a20302c0a202020202020202020202020226d656d6f7279223a20302c0a202020202020202020202020226d656d6f72795f73776170223a20302c0a202020202020202020202020226d6f756e7473223a205b5d2c0a202020202020202020202020226d7573745f72756e223a20747275652c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d7363686564756c6564222c0a202020202020202020202020226e6574776f726b5f616c696173223a206e756c6c2c0a202020202020202020202020226e6574776f726b5f64617461223a205b0a20202020202020202020202020207b0a202020202020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202020202022676c6f62616c5f697076365f61646472657373223a2022222c0a2020202020202020202020202020202022676c6f62616c5f697076365f7072656669785f6c656e677468223a20302c0a202020202020202020202020202020202269705f61646472657373223a20223137322e31372e302e34222c0a202020202020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a2020202020202020202020202020202022697076365f67617465776179223a2022222c0a20202020202020202020202020202020226e6574776f726b5f6e616d65223a2022627269646765220a20202020202020202020202020207d0a2020202020202020202020205d2c0a202020202020202020202020226e6574776f726b5f6d6f6465223a202264656661756c74222c0a202020202020202020202020226e6574776f726b73223a206e756c6c2c0a202020202020202020202020226e6574776f726b735f616476616e636564223a205b5d2c0a202020202020202020202020227069645f6d6f6465223a2022222c0a20202020202020202020202022706f727473223a205b5d2c0a2020202020202020202020202270726976696c65676564223a2066616c73652c0a202020202020202020202020227075626c6973685f616c6c5f706f727473223a2066616c73652c0a20202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202272656d6f76655f766f6c756d6573223a20747275652c0a2020202020202020202020202272657374617274223a20226e6f222c0a20202020202020202020202022726d223a2066616c73652c0a2020202020202020202020202272756e74696d65223a202272756e63222c0a2020202020202020202020202273656375726974795f6f707473223a205b5d2c0a2020202020202020202020202273686d5f73697a65223a2036342c0a202020202020202020202020227374617274223a20747275652c0a20202020202020202020202022737464696e5f6f70656e223a2066616c73652c0a2020202020202020202020202273746f705f7369676e616c223a2022222c0a2020202020202020202020202273746f705f74696d656f7574223a20302c0a2020202020202020202020202273746f726167655f6f707473223a206e756c6c2c0a2020202020202020202020202273797363746c73223a206e756c6c2c0a20202020202020202020202022746d706673223a206e756c6c2c0a20202020202020202020202022747479223a2066616c73652c0a20202020202020202020202022756c696d6974223a205b5d2c0a2020202020202020202020202275706c6f6164223a205b5d2c0a2020202020202020202020202275736572223a202231303030222c0a20202020202020202020202022757365726e735f6d6f6465223a2022222c0a20202020202020202020202022766f6c756d6573223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022636f6e7461696e65725f70617468223a20222f686f6d652f636f6465722f222c0a202020202020202020202020202020202266726f6d5f636f6e7461696e6572223a2022222c0a2020202020202020202020202020202022686f73745f70617468223a2022222c0a2020202020202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202020202022766f6c756d655f6e616d65223a2022636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f74220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022776f726b696e675f646972223a20222f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b0a2020202020202020202020205b0a20202020202020202020202020207b0a202020202020202020202020202020202274797065223a20226765745f61747472222c0a202020202020202020202020202020202276616c7565223a2022656e76220a20202020202020202020202020207d0a2020202020202020202020205d0a202020202020202020205d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a496966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65222c0a20202020202020202020202022646f636b65725f766f6c756d652e686f6d655f766f6c756d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a206e756c6c2c0a202020202020202020202020226964223a2022636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-05 21:06:08.659241+02') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_builds VALUES ('cd6fe03b-d6cf-4d5d-a448-cef52c3ddea2', '2022-11-02 13:08:07.136084+02', '2022-11-02 13:08:10.801094+02', 'b90547be-8870-4d68-8184-e8b2242b7c01', '4e681a60-83da-42c2-902e-6535376ebb77', 'exciting_spence5', 2, 'start', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20382c0a2020226c696e65616765223a202231623532313632352d643737322d333863302d326634652d343163386635333763323638222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202261643237653534362d373665322d343461312d626566632d303465373336633764313336222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202262393035343762652d383837302d346436382d383138342d653862323234326237633031222c0a202020202020202020202020226e616d65223a20227465737431222c0a202020202020202020202020226f776e6572223a2022696d746865737570657276697a72222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202230656439626566632d343931312d346363662d613865322d353539626637326461613934222c0a2020202020202020202020202273746172745f636f756e74223a20312c0a202020202020202020202020227472616e736974696f6e223a20227374617274220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a2022696d746865737570657276697a72222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a2022696d746865737570657276697a72220a2020202020202020202020207d2c0a202020202020202020202020226964223a202236313337353939642d346164362d346535382d623066612d613364316138386661356139222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202265383265376366312d396366632d346334372d623539312d323234333966366330306363220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202236313337353939642d346164362d346535382d623066612d613364316138386661356139222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202233613538356263372d373565362d343038652d623062632d646336346534643362653061222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f636f6e7461696e6572222c0a202020202020226e616d65223a2022776f726b7370616365222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022696e6465785f6b6579223a20302c0a2020202020202020202022736368656d615f76657273696f6e223a20322c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022617474616368223a2066616c73652c0a20202020202020202020202022627269646765223a2022222c0a202020202020202020202020226361706162696c6974696573223a205b5d2c0a20202020202020202020202022636f6d6d616e64223a205b5d2c0a20202020202020202020202022636f6e7461696e65725f6c6f6773223a206e756c6c2c0a202020202020202020202020226370755f736574223a2022222c0a202020202020202020202020226370755f736861726573223a20302c0a2020202020202020202020202264657374726f795f67726163655f7365636f6e6473223a206e756c6c2c0a2020202020202020202020202264657669636573223a205b5d2c0a20202020202020202020202022646e73223a205b0a202020202020202020202020202022312e312e312e31220a2020202020202020202020205d2c0a20202020202020202020202022646e735f6f707473223a206e756c6c2c0a20202020202020202020202022646e735f736561726368223a206e756c6c2c0a20202020202020202020202022646f6d61696e6e616d65223a2022222c0a20202020202020202020202022656e747279706f696e74223a205b0a2020202020202020202020202020227368222c0a2020202020202020202020202020222d63222c0a20202020202020202020202020202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74220a2020202020202020202020205d2c0a20202020202020202020202022656e76223a205b0a202020202020202020202020202022434f4445525f4147454e545f544f4b454e3d65383265376366312d396366632d346334372d623539312d323234333966366330306363220a2020202020202020202020205d2c0a20202020202020202020202022657869745f636f6465223a206e756c6c2c0a2020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202267707573223a206e756c6c2c0a2020202020202020202020202267726f75705f616464223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a206e756c6c2c0a20202020202020202020202022686f7374223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022686f7374223a2022686f73742e646f636b65722e696e7465726e616c222c0a20202020202020202020202020202020226970223a2022686f73742d67617465776179220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022686f73746e616d65223a20227465737431222c0a202020202020202020202020226964223a202238366438353962313262363531393136306435336239396434663031353562663661656565666264333364383866653933653265656365663232656236356465222c0a20202020202020202020202022696d616765223a20227368613235363a32663565366139303562653439366363373931306133643838613362646536343033333733616462623734323861326364346262303938346236343364373731222c0a20202020202020202020202022696e6974223a2066616c73652c0a2020202020202020202020202269705f61646472657373223a20223137322e31372e302e33222c0a2020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a202020202020202020202020226970635f6d6f6465223a202270726976617465222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226c696e6b73223a206e756c6c2c0a202020202020202020202020226c6f675f647269766572223a20226a6f75726e616c64222c0a202020202020202020202020226c6f675f6f707473223a206e756c6c2c0a202020202020202020202020226c6f6773223a2066616c73652c0a202020202020202020202020226d61785f72657472795f636f756e74223a20302c0a202020202020202020202020226d656d6f7279223a20302c0a202020202020202020202020226d656d6f72795f73776170223a20302c0a202020202020202020202020226d6f756e7473223a205b5d2c0a202020202020202020202020226d7573745f72756e223a20747275652c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d7465737431222c0a202020202020202020202020226e6574776f726b5f616c696173223a206e756c6c2c0a202020202020202020202020226e6574776f726b5f64617461223a205b0a20202020202020202020202020207b0a202020202020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202020202022676c6f62616c5f697076365f61646472657373223a2022222c0a2020202020202020202020202020202022676c6f62616c5f697076365f7072656669785f6c656e677468223a20302c0a202020202020202020202020202020202269705f61646472657373223a20223137322e31372e302e33222c0a202020202020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a2020202020202020202020202020202022697076365f67617465776179223a2022222c0a20202020202020202020202020202020226e6574776f726b5f6e616d65223a2022627269646765220a20202020202020202020202020207d0a2020202020202020202020205d2c0a202020202020202020202020226e6574776f726b5f6d6f6465223a202264656661756c74222c0a202020202020202020202020226e6574776f726b73223a206e756c6c2c0a202020202020202020202020226e6574776f726b735f616476616e636564223a205b5d2c0a202020202020202020202020227069645f6d6f6465223a2022222c0a20202020202020202020202022706f727473223a205b5d2c0a2020202020202020202020202270726976696c65676564223a2066616c73652c0a202020202020202020202020227075626c6973685f616c6c5f706f727473223a2066616c73652c0a20202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202272656d6f76655f766f6c756d6573223a20747275652c0a2020202020202020202020202272657374617274223a20226e6f222c0a20202020202020202020202022726d223a2066616c73652c0a2020202020202020202020202272756e74696d65223a202272756e63222c0a2020202020202020202020202273656375726974795f6f707473223a205b5d2c0a2020202020202020202020202273686d5f73697a65223a2036342c0a202020202020202020202020227374617274223a20747275652c0a20202020202020202020202022737464696e5f6f70656e223a2066616c73652c0a2020202020202020202020202273746f705f7369676e616c223a2022222c0a2020202020202020202020202273746f705f74696d656f7574223a20302c0a2020202020202020202020202273746f726167655f6f707473223a206e756c6c2c0a2020202020202020202020202273797363746c73223a206e756c6c2c0a20202020202020202020202022746d706673223a206e756c6c2c0a20202020202020202020202022747479223a2066616c73652c0a20202020202020202020202022756c696d6974223a205b5d2c0a2020202020202020202020202275706c6f6164223a205b5d2c0a2020202020202020202020202275736572223a202231303030222c0a20202020202020202020202022757365726e735f6d6f6465223a2022222c0a20202020202020202020202022766f6c756d6573223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022636f6e7461696e65725f70617468223a20222f686f6d652f636f6465722f222c0a202020202020202020202020202020202266726f6d5f636f6e7461696e6572223a2022222c0a2020202020202020202020202020202022686f73745f70617468223a2022222c0a2020202020202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202020202022766f6c756d655f6e616d65223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022776f726b696e675f646972223a20222f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b0a2020202020202020202020205b0a20202020202020202020202020207b0a202020202020202020202020202020202274797065223a20226765745f61747472222c0a202020202020202020202020202020202276616c7565223a2022656e76220a20202020202020202020202020207d0a2020202020202020202020205d0a202020202020202020205d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a496966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65222c0a20202020202020202020202022646f636b65725f766f6c756d652e686f6d655f766f6c756d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a207b7d2c0a202020202020202020202020226964223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d696d746865737570657276697a722d74657374312d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-09 13:08:10.801094+02') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_builds VALUES ('b56215f2-3b5e-406a-ac54-41c284dc9af3', '2022-11-02 13:09:51.764313+02', '2022-11-02 13:09:54.749685+02', 'b90547be-8870-4d68-8184-e8b2242b7c01', '4e681a60-83da-42c2-902e-6535376ebb77', 'tender_shamir3', 3, 'stop', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a2031302c0a2020226c696e65616765223a202231623532313632352d643737322d333863302d326634652d343163386635333763323638222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202235386561326430392d393933622d343735342d386337642d306330656339343465326565222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202262393035343762652d383837302d346436382d383138342d653862323234326237633031222c0a202020202020202020202020226e616d65223a20227465737431222c0a202020202020202020202020226f776e6572223a2022696d746865737570657276697a72222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202230656439626566632d343931312d346363662d613865322d353539626637326461613934222c0a2020202020202020202020202273746172745f636f756e74223a20302c0a202020202020202020202020227472616e736974696f6e223a202273746f70220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a2022696d746865737570657276697a72222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a2022696d746865737570657276697a72220a2020202020202020202020207d2c0a202020202020202020202020226964223a202236313337353939642d346164362d346535382d623066612d613364316138386661356139222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202237303637643239342d393061392d343733342d613935622d306630316236363636633034220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202236313337353939642d346164362d346535382d623066612d613364316138386661356139222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202233613538356263372d373565362d343038652d623062632d646336346534643362653061222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a207b7d2c0a202020202020202020202020226964223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d696d746865737570657276697a722d74657374312d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-09 13:09:54.749685+02') ON CONFLICT DO NOTHING; -INSERT INTO public.workspace_builds VALUES ('2bfa1945-e81f-44e4-8f41-394200f6cb30', '2022-11-02 13:10:42.104687+02', '2022-11-02 13:10:45.60543+02', '2d72d32e-3021-4843-b582-d962fee897e2', '920baba5-4c64-4686-8b7d-d1bef5683eae', 'adoring_satoshi8', 2, 'stop', '30095c71-380b-457a-8995-97b8ee6e5307', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20372c0a2020226c696e65616765223a202230626637343762342d663639312d626461382d653066662d646336306230333566303932222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202261396262396332372d613533622d343532342d383231622d363464336266326461343763222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202232643732643332652d333032312d343834332d623538322d643936326665653839376532222c0a202020202020202020202020226e616d65223a20227363686564756c6564222c0a202020202020202020202020226f776e6572223a2022696d746865737570657276697a72222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202230656439626566632d343931312d346363662d613865322d353539626637326461613934222c0a2020202020202020202020202273746172745f636f756e74223a20302c0a202020202020202020202020227472616e736974696f6e223a202273746f70220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a2022696d746865737570657276697a72222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a2022696d746865737570657276697a72220a2020202020202020202020207d2c0a202020202020202020202020226964223a202238383431383734642d306335632d343961622d616231302d326336623865353039343333222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202234613131613336662d626230662d343764342d623737302d653430346166306465626535220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202238383431383734642d306335632d343961622d616231302d326336623865353039343333222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202238366163393065322d636338312d343164632d613136652d656563613534633861336337222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a207b7d2c0a202020202020202020202020226964223a2022636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-05 21:10:45.60543+02') ON CONFLICT DO NOTHING; +INSERT INTO workspace_builds VALUES ('a8c0b8c5-c9a8-4f33-93a4-8142e6858244', '2022-11-02 13:04:19.044082+02', '2022-11-02 13:04:22.82111+02', '3a9a1feb-e89d-457c-9d53-ac751b198ebe', '920baba5-4c64-4686-8b7d-d1bef5683eae', 'wizardly_lumiere8', 1, 'start', '30095c71-380b-457a-8995-97b8ee6e5307', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20352c0a2020226c696e65616765223a202230633238626130332d623932322d333431342d353766642d656631336166333061373662222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202231376363313761662d386262622d343736312d616433392d646136326164353132633233222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202233613961316665622d653839642d343537632d396435332d616337353162313938656265222c0a202020202020202020202020226e616d65223a20226d792d776f726b7370616365222c0a202020202020202020202020226f776e6572223a202261646d696e222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202233303039356337312d333830622d343537612d383939352d393762386565366535333037222c0a2020202020202020202020202273746172745f636f756e74223a20312c0a202020202020202020202020227472616e736974696f6e223a20227374617274220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a202261646d696e222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a202261646d696e220a2020202020202020202020207d2c0a202020202020202020202020226964223a202230393037646162392d313930392d346430332d626666642d393336613261303561323261222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202237363965646261312d626663662d343662662d613565382d306632353333643534663731220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202230393037646162392d313930392d346430332d626666642d393336613261303561323261222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202235663163326261332d366232362d343039632d393739302d646635366236326566636263222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f636f6e7461696e6572222c0a202020202020226e616d65223a2022776f726b7370616365222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022696e6465785f6b6579223a20302c0a2020202020202020202022736368656d615f76657273696f6e223a20322c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022617474616368223a2066616c73652c0a20202020202020202020202022627269646765223a2022222c0a202020202020202020202020226361706162696c6974696573223a205b5d2c0a20202020202020202020202022636f6d6d616e64223a205b5d2c0a20202020202020202020202022636f6e7461696e65725f6c6f6773223a206e756c6c2c0a202020202020202020202020226370755f736574223a2022222c0a202020202020202020202020226370755f736861726573223a20302c0a2020202020202020202020202264657374726f795f67726163655f7365636f6e6473223a206e756c6c2c0a2020202020202020202020202264657669636573223a205b5d2c0a20202020202020202020202022646e73223a205b0a202020202020202020202020202022312e312e312e31220a2020202020202020202020205d2c0a20202020202020202020202022646e735f6f707473223a206e756c6c2c0a20202020202020202020202022646e735f736561726368223a206e756c6c2c0a20202020202020202020202022646f6d61696e6e616d65223a2022222c0a20202020202020202020202022656e747279706f696e74223a205b0a2020202020202020202020202020227368222c0a2020202020202020202020202020222d63222c0a20202020202020202020202020202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74220a2020202020202020202020205d2c0a20202020202020202020202022656e76223a205b0a202020202020202020202020202022434f4445525f4147454e545f544f4b454e3d37363965646261312d626663662d343662662d613565382d306632353333643534663731220a2020202020202020202020205d2c0a20202020202020202020202022657869745f636f6465223a206e756c6c2c0a2020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202267707573223a206e756c6c2c0a2020202020202020202020202267726f75705f616464223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a206e756c6c2c0a20202020202020202020202022686f7374223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022686f7374223a2022686f73742e646f636b65722e696e7465726e616c222c0a20202020202020202020202020202020226970223a2022686f73742d67617465776179220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022686f73746e616d65223a20226d792d776f726b7370616365222c0a202020202020202020202020226964223a202261316666353336343732653965666464356564393064366161666332383636393739393636383030376162393338323335363761633038303636383232323434222c0a20202020202020202020202022696d616765223a20227368613235363a32663565366139303562653439366363373931306133643838613362646536343033333733616462623734323861326364346262303938346236343364373731222c0a20202020202020202020202022696e6974223a2066616c73652c0a2020202020202020202020202269705f61646472657373223a20223137322e31372e302e32222c0a2020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a202020202020202020202020226970635f6d6f6465223a202270726976617465222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226c696e6b73223a206e756c6c2c0a202020202020202020202020226c6f675f647269766572223a20226a6f75726e616c64222c0a202020202020202020202020226c6f675f6f707473223a206e756c6c2c0a202020202020202020202020226c6f6773223a2066616c73652c0a202020202020202020202020226d61785f72657472795f636f756e74223a20302c0a202020202020202020202020226d656d6f7279223a20302c0a202020202020202020202020226d656d6f72795f73776170223a20302c0a202020202020202020202020226d6f756e7473223a205b5d2c0a202020202020202020202020226d7573745f72756e223a20747275652c0a202020202020202020202020226e616d65223a2022636f6465722d61646d696e2d6d792d776f726b7370616365222c0a202020202020202020202020226e6574776f726b5f616c696173223a206e756c6c2c0a202020202020202020202020226e6574776f726b5f64617461223a205b0a20202020202020202020202020207b0a202020202020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202020202022676c6f62616c5f697076365f61646472657373223a2022222c0a2020202020202020202020202020202022676c6f62616c5f697076365f7072656669785f6c656e677468223a20302c0a202020202020202020202020202020202269705f61646472657373223a20223137322e31372e302e32222c0a202020202020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a2020202020202020202020202020202022697076365f67617465776179223a2022222c0a20202020202020202020202020202020226e6574776f726b5f6e616d65223a2022627269646765220a20202020202020202020202020207d0a2020202020202020202020205d2c0a202020202020202020202020226e6574776f726b5f6d6f6465223a202264656661756c74222c0a202020202020202020202020226e6574776f726b73223a206e756c6c2c0a202020202020202020202020226e6574776f726b735f616476616e636564223a205b5d2c0a202020202020202020202020227069645f6d6f6465223a2022222c0a20202020202020202020202022706f727473223a205b5d2c0a2020202020202020202020202270726976696c65676564223a2066616c73652c0a202020202020202020202020227075626c6973685f616c6c5f706f727473223a2066616c73652c0a20202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202272656d6f76655f766f6c756d6573223a20747275652c0a2020202020202020202020202272657374617274223a20226e6f222c0a20202020202020202020202022726d223a2066616c73652c0a2020202020202020202020202272756e74696d65223a202272756e63222c0a2020202020202020202020202273656375726974795f6f707473223a205b5d2c0a2020202020202020202020202273686d5f73697a65223a2036342c0a202020202020202020202020227374617274223a20747275652c0a20202020202020202020202022737464696e5f6f70656e223a2066616c73652c0a2020202020202020202020202273746f705f7369676e616c223a2022222c0a2020202020202020202020202273746f705f74696d656f7574223a20302c0a2020202020202020202020202273746f726167655f6f707473223a206e756c6c2c0a2020202020202020202020202273797363746c73223a206e756c6c2c0a20202020202020202020202022746d706673223a206e756c6c2c0a20202020202020202020202022747479223a2066616c73652c0a20202020202020202020202022756c696d6974223a205b5d2c0a2020202020202020202020202275706c6f6164223a205b5d2c0a2020202020202020202020202275736572223a202231303030222c0a20202020202020202020202022757365726e735f6d6f6465223a2022222c0a20202020202020202020202022766f6c756d6573223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022636f6e7461696e65725f70617468223a20222f686f6d652f636f6465722f222c0a202020202020202020202020202020202266726f6d5f636f6e7461696e6572223a2022222c0a2020202020202020202020202020202022686f73745f70617468223a2022222c0a2020202020202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202020202022766f6c756d655f6e616d65223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022776f726b696e675f646972223a20222f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b0a2020202020202020202020205b0a20202020202020202020202020207b0a202020202020202020202020202020202274797065223a20226765745f61747472222c0a202020202020202020202020202020202276616c7565223a2022656e76220a20202020202020202020202020207d0a2020202020202020202020205d0a202020202020202020205d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a496966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65222c0a20202020202020202020202022646f636b65725f766f6c756d652e686f6d655f766f6c756d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a206e756c6c2c0a202020202020202020202020226964223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', '52a90399-a53d-4644-be3c-47ee18a5716e', '2022-11-02 21:04:22.82111+02') ON CONFLICT DO NOTHING; +INSERT INTO workspace_builds VALUES ('ea36844d-8eb6-41a2-a237-e9a8ae3f99ea', '2022-11-02 13:04:48.073344+02', '2022-11-02 13:04:51.504141+02', '3a9a1feb-e89d-457c-9d53-ac751b198ebe', '920baba5-4c64-4686-8b7d-d1bef5683eae', 'vigorous_matsumoto6', 2, 'stop', '30095c71-380b-457a-8995-97b8ee6e5307', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20372c0a2020226c696e65616765223a202230633238626130332d623932322d333431342d353766642d656631336166333061373662222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202236396438633234372d613463632d346465362d383663302d636530373332353533313661222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202233613961316665622d653839642d343537632d396435332d616337353162313938656265222c0a202020202020202020202020226e616d65223a20226d792d776f726b7370616365222c0a202020202020202020202020226f776e6572223a202261646d696e222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202233303039356337312d333830622d343537612d383939352d393762386565366535333037222c0a2020202020202020202020202273746172745f636f756e74223a20302c0a202020202020202020202020227472616e736974696f6e223a202273746f70220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a202261646d696e222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a202261646d696e220a2020202020202020202020207d2c0a202020202020202020202020226964223a202230393037646162392d313930392d346430332d626666642d393336613261303561323261222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202263653730353961352d633637622d343163372d616330302d666266643630646637356465220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202230393037646162392d313930392d346430332d626666642d393336613261303561323261222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202235663163326261332d366232362d343039632d393739302d646635366236326566636263222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a207b7d2c0a202020202020202020202020226964223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', '104c5815-7bd2-4d09-b76c-00c61b95f0a6', '2022-11-02 21:04:51.504141+02') ON CONFLICT DO NOTHING; +INSERT INTO workspace_builds VALUES ('c1d2c9d5-6f30-4cd0-9ac5-6bf1c6039988', '2022-11-02 13:04:55.224578+02', '2022-11-02 13:04:58.762449+02', '3a9a1feb-e89d-457c-9d53-ac751b198ebe', '920baba5-4c64-4686-8b7d-d1bef5683eae', 'romantic_johnson8', 3, 'start', '30095c71-380b-457a-8995-97b8ee6e5307', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20392c0a2020226c696e65616765223a202230633238626130332d623932322d333431342d353766642d656631336166333061373662222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202262363561373637662d373139612d343838332d613138362d616562316163303563623235222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202233613961316665622d653839642d343537632d396435332d616337353162313938656265222c0a202020202020202020202020226e616d65223a20226d792d776f726b7370616365222c0a202020202020202020202020226f776e6572223a202261646d696e222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202233303039356337312d333830622d343537612d383939352d393762386565366535333037222c0a2020202020202020202020202273746172745f636f756e74223a20312c0a202020202020202020202020227472616e736974696f6e223a20227374617274220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a202261646d696e222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a202261646d696e220a2020202020202020202020207d2c0a202020202020202020202020226964223a202230393037646162392d313930392d346430332d626666642d393336613261303561323261222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202265613731633136612d636639332d346334352d613166642d313030666330333435323866220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202230393037646162392d313930392d346430332d626666642d393336613261303561323261222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202235663163326261332d366232362d343039632d393739302d646635366236326566636263222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f636f6e7461696e6572222c0a202020202020226e616d65223a2022776f726b7370616365222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022696e6465785f6b6579223a20302c0a2020202020202020202022736368656d615f76657273696f6e223a20322c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022617474616368223a2066616c73652c0a20202020202020202020202022627269646765223a2022222c0a202020202020202020202020226361706162696c6974696573223a205b5d2c0a20202020202020202020202022636f6d6d616e64223a205b5d2c0a20202020202020202020202022636f6e7461696e65725f6c6f6773223a206e756c6c2c0a202020202020202020202020226370755f736574223a2022222c0a202020202020202020202020226370755f736861726573223a20302c0a2020202020202020202020202264657374726f795f67726163655f7365636f6e6473223a206e756c6c2c0a2020202020202020202020202264657669636573223a205b5d2c0a20202020202020202020202022646e73223a205b0a202020202020202020202020202022312e312e312e31220a2020202020202020202020205d2c0a20202020202020202020202022646e735f6f707473223a206e756c6c2c0a20202020202020202020202022646e735f736561726368223a206e756c6c2c0a20202020202020202020202022646f6d61696e6e616d65223a2022222c0a20202020202020202020202022656e747279706f696e74223a205b0a2020202020202020202020202020227368222c0a2020202020202020202020202020222d63222c0a20202020202020202020202020202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74220a2020202020202020202020205d2c0a20202020202020202020202022656e76223a205b0a202020202020202020202020202022434f4445525f4147454e545f544f4b454e3d65613731633136612d636639332d346334352d613166642d313030666330333435323866220a2020202020202020202020205d2c0a20202020202020202020202022657869745f636f6465223a206e756c6c2c0a2020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202267707573223a206e756c6c2c0a2020202020202020202020202267726f75705f616464223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a206e756c6c2c0a20202020202020202020202022686f7374223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022686f7374223a2022686f73742e646f636b65722e696e7465726e616c222c0a20202020202020202020202020202020226970223a2022686f73742d67617465776179220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022686f73746e616d65223a20226d792d776f726b7370616365222c0a202020202020202020202020226964223a202264623461656364653635376231323639333965386261643431326239353333313463643133313666653762336633643036313230336430613731653033376538222c0a20202020202020202020202022696d616765223a20227368613235363a32663565366139303562653439366363373931306133643838613362646536343033333733616462623734323861326364346262303938346236343364373731222c0a20202020202020202020202022696e6974223a2066616c73652c0a2020202020202020202020202269705f61646472657373223a20223137322e31372e302e32222c0a2020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a202020202020202020202020226970635f6d6f6465223a202270726976617465222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226c696e6b73223a206e756c6c2c0a202020202020202020202020226c6f675f647269766572223a20226a6f75726e616c64222c0a202020202020202020202020226c6f675f6f707473223a206e756c6c2c0a202020202020202020202020226c6f6773223a2066616c73652c0a202020202020202020202020226d61785f72657472795f636f756e74223a20302c0a202020202020202020202020226d656d6f7279223a20302c0a202020202020202020202020226d656d6f72795f73776170223a20302c0a202020202020202020202020226d6f756e7473223a205b5d2c0a202020202020202020202020226d7573745f72756e223a20747275652c0a202020202020202020202020226e616d65223a2022636f6465722d61646d696e2d6d792d776f726b7370616365222c0a202020202020202020202020226e6574776f726b5f616c696173223a206e756c6c2c0a202020202020202020202020226e6574776f726b5f64617461223a205b0a20202020202020202020202020207b0a202020202020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202020202022676c6f62616c5f697076365f61646472657373223a2022222c0a2020202020202020202020202020202022676c6f62616c5f697076365f7072656669785f6c656e677468223a20302c0a202020202020202020202020202020202269705f61646472657373223a20223137322e31372e302e32222c0a202020202020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a2020202020202020202020202020202022697076365f67617465776179223a2022222c0a20202020202020202020202020202020226e6574776f726b5f6e616d65223a2022627269646765220a20202020202020202020202020207d0a2020202020202020202020205d2c0a202020202020202020202020226e6574776f726b5f6d6f6465223a202264656661756c74222c0a202020202020202020202020226e6574776f726b73223a206e756c6c2c0a202020202020202020202020226e6574776f726b735f616476616e636564223a205b5d2c0a202020202020202020202020227069645f6d6f6465223a2022222c0a20202020202020202020202022706f727473223a205b5d2c0a2020202020202020202020202270726976696c65676564223a2066616c73652c0a202020202020202020202020227075626c6973685f616c6c5f706f727473223a2066616c73652c0a20202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202272656d6f76655f766f6c756d6573223a20747275652c0a2020202020202020202020202272657374617274223a20226e6f222c0a20202020202020202020202022726d223a2066616c73652c0a2020202020202020202020202272756e74696d65223a202272756e63222c0a2020202020202020202020202273656375726974795f6f707473223a205b5d2c0a2020202020202020202020202273686d5f73697a65223a2036342c0a202020202020202020202020227374617274223a20747275652c0a20202020202020202020202022737464696e5f6f70656e223a2066616c73652c0a2020202020202020202020202273746f705f7369676e616c223a2022222c0a2020202020202020202020202273746f705f74696d656f7574223a20302c0a2020202020202020202020202273746f726167655f6f707473223a206e756c6c2c0a2020202020202020202020202273797363746c73223a206e756c6c2c0a20202020202020202020202022746d706673223a206e756c6c2c0a20202020202020202020202022747479223a2066616c73652c0a20202020202020202020202022756c696d6974223a205b5d2c0a2020202020202020202020202275706c6f6164223a205b5d2c0a2020202020202020202020202275736572223a202231303030222c0a20202020202020202020202022757365726e735f6d6f6465223a2022222c0a20202020202020202020202022766f6c756d6573223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022636f6e7461696e65725f70617468223a20222f686f6d652f636f6465722f222c0a202020202020202020202020202020202266726f6d5f636f6e7461696e6572223a2022222c0a2020202020202020202020202020202022686f73745f70617468223a2022222c0a2020202020202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202020202022766f6c756d655f6e616d65223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022776f726b696e675f646972223a20222f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b0a2020202020202020202020205b0a20202020202020202020202020207b0a202020202020202020202020202020202274797065223a20226765745f61747472222c0a202020202020202020202020202020202276616c7565223a2022656e76220a20202020202020202020202020207d0a2020202020202020202020205d0a202020202020202020205d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a496966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65222c0a20202020202020202020202022646f636b65725f766f6c756d652e686f6d655f766f6c756d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a207b7d2c0a202020202020202020202020226964223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d61646d696e2d6d792d776f726b73706163652d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', 'd6ec4737-feaa-446e-aa14-8b2b98b59bf9', '2022-11-02 21:04:58.762449+02') ON CONFLICT DO NOTHING; +INSERT INTO workspace_builds VALUES ('c7c0a371-db1e-4f10-ab34-6779f573554c', '2022-11-02 13:05:40.181124+02', '2022-11-02 13:05:43.876957+02', 'b90547be-8870-4d68-8184-e8b2242b7c01', '920baba5-4c64-4686-8b7d-d1bef5683eae', 'vigilant_shtern5', 1, 'start', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20352c0a2020226c696e65616765223a202231623532313632352d643737322d333863302d326634652d343163386635333763323638222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202266653431386538312d623861302d346636392d383765612d613061353937373435653239222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202262393035343762652d383837302d346436382d383138342d653862323234326237633031222c0a202020202020202020202020226e616d65223a20227465737431222c0a202020202020202020202020226f776e6572223a2022696d746865737570657276697a72222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202230656439626566632d343931312d346363662d613865322d353539626637326461613934222c0a2020202020202020202020202273746172745f636f756e74223a20312c0a202020202020202020202020227472616e736974696f6e223a20227374617274220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a2022696d746865737570657276697a72222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a2022696d746865737570657276697a72220a2020202020202020202020207d2c0a202020202020202020202020226964223a202236313337353939642d346164362d346535382d623066612d613364316138386661356139222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202263343237643131322d323364382d343633342d393934652d643635613739653731386663220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202236313337353939642d346164362d346535382d623066612d613364316138386661356139222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202233613538356263372d373565362d343038652d623062632d646336346534643362653061222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f636f6e7461696e6572222c0a202020202020226e616d65223a2022776f726b7370616365222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022696e6465785f6b6579223a20302c0a2020202020202020202022736368656d615f76657273696f6e223a20322c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022617474616368223a2066616c73652c0a20202020202020202020202022627269646765223a2022222c0a202020202020202020202020226361706162696c6974696573223a205b5d2c0a20202020202020202020202022636f6d6d616e64223a205b5d2c0a20202020202020202020202022636f6e7461696e65725f6c6f6773223a206e756c6c2c0a202020202020202020202020226370755f736574223a2022222c0a202020202020202020202020226370755f736861726573223a20302c0a2020202020202020202020202264657374726f795f67726163655f7365636f6e6473223a206e756c6c2c0a2020202020202020202020202264657669636573223a205b5d2c0a20202020202020202020202022646e73223a205b0a202020202020202020202020202022312e312e312e31220a2020202020202020202020205d2c0a20202020202020202020202022646e735f6f707473223a206e756c6c2c0a20202020202020202020202022646e735f736561726368223a206e756c6c2c0a20202020202020202020202022646f6d61696e6e616d65223a2022222c0a20202020202020202020202022656e747279706f696e74223a205b0a2020202020202020202020202020227368222c0a2020202020202020202020202020222d63222c0a20202020202020202020202020202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74220a2020202020202020202020205d2c0a20202020202020202020202022656e76223a205b0a202020202020202020202020202022434f4445525f4147454e545f544f4b454e3d63343237643131322d323364382d343633342d393934652d643635613739653731386663220a2020202020202020202020205d2c0a20202020202020202020202022657869745f636f6465223a206e756c6c2c0a2020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202267707573223a206e756c6c2c0a2020202020202020202020202267726f75705f616464223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a206e756c6c2c0a20202020202020202020202022686f7374223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022686f7374223a2022686f73742e646f636b65722e696e7465726e616c222c0a20202020202020202020202020202020226970223a2022686f73742d67617465776179220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022686f73746e616d65223a20227465737431222c0a202020202020202020202020226964223a202265656439366463633135393832396239613931656531336132313036623366383539383839366639396437313432373635323736393134653632386538313039222c0a20202020202020202020202022696d616765223a20227368613235363a32663565366139303562653439366363373931306133643838613362646536343033333733616462623734323861326364346262303938346236343364373731222c0a20202020202020202020202022696e6974223a2066616c73652c0a2020202020202020202020202269705f61646472657373223a20223137322e31372e302e33222c0a2020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a202020202020202020202020226970635f6d6f6465223a202270726976617465222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226c696e6b73223a206e756c6c2c0a202020202020202020202020226c6f675f647269766572223a20226a6f75726e616c64222c0a202020202020202020202020226c6f675f6f707473223a206e756c6c2c0a202020202020202020202020226c6f6773223a2066616c73652c0a202020202020202020202020226d61785f72657472795f636f756e74223a20302c0a202020202020202020202020226d656d6f7279223a20302c0a202020202020202020202020226d656d6f72795f73776170223a20302c0a202020202020202020202020226d6f756e7473223a205b5d2c0a202020202020202020202020226d7573745f72756e223a20747275652c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d7465737431222c0a202020202020202020202020226e6574776f726b5f616c696173223a206e756c6c2c0a202020202020202020202020226e6574776f726b5f64617461223a205b0a20202020202020202020202020207b0a202020202020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202020202022676c6f62616c5f697076365f61646472657373223a2022222c0a2020202020202020202020202020202022676c6f62616c5f697076365f7072656669785f6c656e677468223a20302c0a202020202020202020202020202020202269705f61646472657373223a20223137322e31372e302e33222c0a202020202020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a2020202020202020202020202020202022697076365f67617465776179223a2022222c0a20202020202020202020202020202020226e6574776f726b5f6e616d65223a2022627269646765220a20202020202020202020202020207d0a2020202020202020202020205d2c0a202020202020202020202020226e6574776f726b5f6d6f6465223a202264656661756c74222c0a202020202020202020202020226e6574776f726b73223a206e756c6c2c0a202020202020202020202020226e6574776f726b735f616476616e636564223a205b5d2c0a202020202020202020202020227069645f6d6f6465223a2022222c0a20202020202020202020202022706f727473223a205b5d2c0a2020202020202020202020202270726976696c65676564223a2066616c73652c0a202020202020202020202020227075626c6973685f616c6c5f706f727473223a2066616c73652c0a20202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202272656d6f76655f766f6c756d6573223a20747275652c0a2020202020202020202020202272657374617274223a20226e6f222c0a20202020202020202020202022726d223a2066616c73652c0a2020202020202020202020202272756e74696d65223a202272756e63222c0a2020202020202020202020202273656375726974795f6f707473223a205b5d2c0a2020202020202020202020202273686d5f73697a65223a2036342c0a202020202020202020202020227374617274223a20747275652c0a20202020202020202020202022737464696e5f6f70656e223a2066616c73652c0a2020202020202020202020202273746f705f7369676e616c223a2022222c0a2020202020202020202020202273746f705f74696d656f7574223a20302c0a2020202020202020202020202273746f726167655f6f707473223a206e756c6c2c0a2020202020202020202020202273797363746c73223a206e756c6c2c0a20202020202020202020202022746d706673223a206e756c6c2c0a20202020202020202020202022747479223a2066616c73652c0a20202020202020202020202022756c696d6974223a205b5d2c0a2020202020202020202020202275706c6f6164223a205b5d2c0a2020202020202020202020202275736572223a202231303030222c0a20202020202020202020202022757365726e735f6d6f6465223a2022222c0a20202020202020202020202022766f6c756d6573223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022636f6e7461696e65725f70617468223a20222f686f6d652f636f6465722f222c0a202020202020202020202020202020202266726f6d5f636f6e7461696e6572223a2022222c0a2020202020202020202020202020202022686f73745f70617468223a2022222c0a2020202020202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202020202022766f6c756d655f6e616d65223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022776f726b696e675f646972223a20222f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b0a2020202020202020202020205b0a20202020202020202020202020207b0a202020202020202020202020202020202274797065223a20226765745f61747472222c0a202020202020202020202020202020202276616c7565223a2022656e76220a20202020202020202020202020207d0a2020202020202020202020205d0a202020202020202020205d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a496966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65222c0a20202020202020202020202022646f636b65725f766f6c756d652e686f6d655f766f6c756d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a206e756c6c2c0a202020202020202020202020226964223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d696d746865737570657276697a722d74657374312d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', '4de9c41c-eb10-435a-8742-e8725e926a9d', '2022-11-09 13:05:43.876957+02') ON CONFLICT DO NOTHING; +INSERT INTO workspace_builds VALUES ('a7477610-c69b-46d6-97fb-d6a3425e1ab4', '2022-11-02 13:06:03.554876+02', '2022-11-02 13:06:08.659241+02', '2d72d32e-3021-4843-b582-d962fee897e2', '920baba5-4c64-4686-8b7d-d1bef5683eae', 'hopeful_herschel7', 1, 'start', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20352c0a2020226c696e65616765223a202230626637343762342d663639312d626461382d653066662d646336306230333566303932222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202231306330316135302d626563312d343436632d623339352d356431383466626133363964222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202232643732643332652d333032312d343834332d623538322d643936326665653839376532222c0a202020202020202020202020226e616d65223a20227363686564756c6564222c0a202020202020202020202020226f776e6572223a2022696d746865737570657276697a72222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202230656439626566632d343931312d346363662d613865322d353539626637326461613934222c0a2020202020202020202020202273746172745f636f756e74223a20312c0a202020202020202020202020227472616e736974696f6e223a20227374617274220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a2022696d746865737570657276697a72222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a2022696d746865737570657276697a72220a2020202020202020202020207d2c0a202020202020202020202020226964223a202238383431383734642d306335632d343961622d616231302d326336623865353039343333222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202239306363313330652d396633392d343130612d613537342d373431653339373837376331220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202238383431383734642d306335632d343961622d616231302d326336623865353039343333222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202238366163393065322d636338312d343164632d613136652d656563613534633861336337222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f636f6e7461696e6572222c0a202020202020226e616d65223a2022776f726b7370616365222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022696e6465785f6b6579223a20302c0a2020202020202020202022736368656d615f76657273696f6e223a20322c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022617474616368223a2066616c73652c0a20202020202020202020202022627269646765223a2022222c0a202020202020202020202020226361706162696c6974696573223a205b5d2c0a20202020202020202020202022636f6d6d616e64223a205b5d2c0a20202020202020202020202022636f6e7461696e65725f6c6f6773223a206e756c6c2c0a202020202020202020202020226370755f736574223a2022222c0a202020202020202020202020226370755f736861726573223a20302c0a2020202020202020202020202264657374726f795f67726163655f7365636f6e6473223a206e756c6c2c0a2020202020202020202020202264657669636573223a205b5d2c0a20202020202020202020202022646e73223a205b0a202020202020202020202020202022312e312e312e31220a2020202020202020202020205d2c0a20202020202020202020202022646e735f6f707473223a206e756c6c2c0a20202020202020202020202022646e735f736561726368223a206e756c6c2c0a20202020202020202020202022646f6d61696e6e616d65223a2022222c0a20202020202020202020202022656e747279706f696e74223a205b0a2020202020202020202020202020227368222c0a2020202020202020202020202020222d63222c0a20202020202020202020202020202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74220a2020202020202020202020205d2c0a20202020202020202020202022656e76223a205b0a202020202020202020202020202022434f4445525f4147454e545f544f4b454e3d39306363313330652d396633392d343130612d613537342d373431653339373837376331220a2020202020202020202020205d2c0a20202020202020202020202022657869745f636f6465223a206e756c6c2c0a2020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202267707573223a206e756c6c2c0a2020202020202020202020202267726f75705f616464223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a206e756c6c2c0a20202020202020202020202022686f7374223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022686f7374223a2022686f73742e646f636b65722e696e7465726e616c222c0a20202020202020202020202020202020226970223a2022686f73742d67617465776179220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022686f73746e616d65223a20227363686564756c6564222c0a202020202020202020202020226964223a202230333161646132656164343736366465303833333166303565643265356630643637343136363531373735376564663162393234383836623133353963616235222c0a20202020202020202020202022696d616765223a20227368613235363a32663565366139303562653439366363373931306133643838613362646536343033333733616462623734323861326364346262303938346236343364373731222c0a20202020202020202020202022696e6974223a2066616c73652c0a2020202020202020202020202269705f61646472657373223a20223137322e31372e302e34222c0a2020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a202020202020202020202020226970635f6d6f6465223a202270726976617465222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226c696e6b73223a206e756c6c2c0a202020202020202020202020226c6f675f647269766572223a20226a6f75726e616c64222c0a202020202020202020202020226c6f675f6f707473223a206e756c6c2c0a202020202020202020202020226c6f6773223a2066616c73652c0a202020202020202020202020226d61785f72657472795f636f756e74223a20302c0a202020202020202020202020226d656d6f7279223a20302c0a202020202020202020202020226d656d6f72795f73776170223a20302c0a202020202020202020202020226d6f756e7473223a205b5d2c0a202020202020202020202020226d7573745f72756e223a20747275652c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d7363686564756c6564222c0a202020202020202020202020226e6574776f726b5f616c696173223a206e756c6c2c0a202020202020202020202020226e6574776f726b5f64617461223a205b0a20202020202020202020202020207b0a202020202020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202020202022676c6f62616c5f697076365f61646472657373223a2022222c0a2020202020202020202020202020202022676c6f62616c5f697076365f7072656669785f6c656e677468223a20302c0a202020202020202020202020202020202269705f61646472657373223a20223137322e31372e302e34222c0a202020202020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a2020202020202020202020202020202022697076365f67617465776179223a2022222c0a20202020202020202020202020202020226e6574776f726b5f6e616d65223a2022627269646765220a20202020202020202020202020207d0a2020202020202020202020205d2c0a202020202020202020202020226e6574776f726b5f6d6f6465223a202264656661756c74222c0a202020202020202020202020226e6574776f726b73223a206e756c6c2c0a202020202020202020202020226e6574776f726b735f616476616e636564223a205b5d2c0a202020202020202020202020227069645f6d6f6465223a2022222c0a20202020202020202020202022706f727473223a205b5d2c0a2020202020202020202020202270726976696c65676564223a2066616c73652c0a202020202020202020202020227075626c6973685f616c6c5f706f727473223a2066616c73652c0a20202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202272656d6f76655f766f6c756d6573223a20747275652c0a2020202020202020202020202272657374617274223a20226e6f222c0a20202020202020202020202022726d223a2066616c73652c0a2020202020202020202020202272756e74696d65223a202272756e63222c0a2020202020202020202020202273656375726974795f6f707473223a205b5d2c0a2020202020202020202020202273686d5f73697a65223a2036342c0a202020202020202020202020227374617274223a20747275652c0a20202020202020202020202022737464696e5f6f70656e223a2066616c73652c0a2020202020202020202020202273746f705f7369676e616c223a2022222c0a2020202020202020202020202273746f705f74696d656f7574223a20302c0a2020202020202020202020202273746f726167655f6f707473223a206e756c6c2c0a2020202020202020202020202273797363746c73223a206e756c6c2c0a20202020202020202020202022746d706673223a206e756c6c2c0a20202020202020202020202022747479223a2066616c73652c0a20202020202020202020202022756c696d6974223a205b5d2c0a2020202020202020202020202275706c6f6164223a205b5d2c0a2020202020202020202020202275736572223a202231303030222c0a20202020202020202020202022757365726e735f6d6f6465223a2022222c0a20202020202020202020202022766f6c756d6573223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022636f6e7461696e65725f70617468223a20222f686f6d652f636f6465722f222c0a202020202020202020202020202020202266726f6d5f636f6e7461696e6572223a2022222c0a2020202020202020202020202020202022686f73745f70617468223a2022222c0a2020202020202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202020202022766f6c756d655f6e616d65223a2022636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f74220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022776f726b696e675f646972223a20222f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b0a2020202020202020202020205b0a20202020202020202020202020207b0a202020202020202020202020202020202274797065223a20226765745f61747472222c0a202020202020202020202020202020202276616c7565223a2022656e76220a20202020202020202020202020207d0a2020202020202020202020205d0a202020202020202020205d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a496966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65222c0a20202020202020202020202022646f636b65725f766f6c756d652e686f6d655f766f6c756d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a206e756c6c2c0a202020202020202020202020226964223a2022636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', '424a58cb-61d6-4627-9907-613c396c4a38', '2022-11-05 21:06:08.659241+02') ON CONFLICT DO NOTHING; +INSERT INTO workspace_builds VALUES ('cd6fe03b-d6cf-4d5d-a448-cef52c3ddea2', '2022-11-02 13:08:07.136084+02', '2022-11-02 13:08:10.801094+02', 'b90547be-8870-4d68-8184-e8b2242b7c01', '4e681a60-83da-42c2-902e-6535376ebb77', 'exciting_spence5', 2, 'start', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20382c0a2020226c696e65616765223a202231623532313632352d643737322d333863302d326634652d343163386635333763323638222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202261643237653534362d373665322d343461312d626566632d303465373336633764313336222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202262393035343762652d383837302d346436382d383138342d653862323234326237633031222c0a202020202020202020202020226e616d65223a20227465737431222c0a202020202020202020202020226f776e6572223a2022696d746865737570657276697a72222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202230656439626566632d343931312d346363662d613865322d353539626637326461613934222c0a2020202020202020202020202273746172745f636f756e74223a20312c0a202020202020202020202020227472616e736974696f6e223a20227374617274220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a2022696d746865737570657276697a72222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a2022696d746865737570657276697a72220a2020202020202020202020207d2c0a202020202020202020202020226964223a202236313337353939642d346164362d346535382d623066612d613364316138386661356139222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202265383265376366312d396366632d346334372d623539312d323234333966366330306363220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202236313337353939642d346164362d346535382d623066612d613364316138386661356139222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202233613538356263372d373565362d343038652d623062632d646336346534643362653061222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f636f6e7461696e6572222c0a202020202020226e616d65223a2022776f726b7370616365222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022696e6465785f6b6579223a20302c0a2020202020202020202022736368656d615f76657273696f6e223a20322c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022617474616368223a2066616c73652c0a20202020202020202020202022627269646765223a2022222c0a202020202020202020202020226361706162696c6974696573223a205b5d2c0a20202020202020202020202022636f6d6d616e64223a205b5d2c0a20202020202020202020202022636f6e7461696e65725f6c6f6773223a206e756c6c2c0a202020202020202020202020226370755f736574223a2022222c0a202020202020202020202020226370755f736861726573223a20302c0a2020202020202020202020202264657374726f795f67726163655f7365636f6e6473223a206e756c6c2c0a2020202020202020202020202264657669636573223a205b5d2c0a20202020202020202020202022646e73223a205b0a202020202020202020202020202022312e312e312e31220a2020202020202020202020205d2c0a20202020202020202020202022646e735f6f707473223a206e756c6c2c0a20202020202020202020202022646e735f736561726368223a206e756c6c2c0a20202020202020202020202022646f6d61696e6e616d65223a2022222c0a20202020202020202020202022656e747279706f696e74223a205b0a2020202020202020202020202020227368222c0a2020202020202020202020202020222d63222c0a20202020202020202020202020202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74220a2020202020202020202020205d2c0a20202020202020202020202022656e76223a205b0a202020202020202020202020202022434f4445525f4147454e545f544f4b454e3d65383265376366312d396366632d346334372d623539312d323234333966366330306363220a2020202020202020202020205d2c0a20202020202020202020202022657869745f636f6465223a206e756c6c2c0a2020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202267707573223a206e756c6c2c0a2020202020202020202020202267726f75705f616464223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a206e756c6c2c0a20202020202020202020202022686f7374223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022686f7374223a2022686f73742e646f636b65722e696e7465726e616c222c0a20202020202020202020202020202020226970223a2022686f73742d67617465776179220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022686f73746e616d65223a20227465737431222c0a202020202020202020202020226964223a202238366438353962313262363531393136306435336239396434663031353562663661656565666264333364383866653933653265656365663232656236356465222c0a20202020202020202020202022696d616765223a20227368613235363a32663565366139303562653439366363373931306133643838613362646536343033333733616462623734323861326364346262303938346236343364373731222c0a20202020202020202020202022696e6974223a2066616c73652c0a2020202020202020202020202269705f61646472657373223a20223137322e31372e302e33222c0a2020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a202020202020202020202020226970635f6d6f6465223a202270726976617465222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226c696e6b73223a206e756c6c2c0a202020202020202020202020226c6f675f647269766572223a20226a6f75726e616c64222c0a202020202020202020202020226c6f675f6f707473223a206e756c6c2c0a202020202020202020202020226c6f6773223a2066616c73652c0a202020202020202020202020226d61785f72657472795f636f756e74223a20302c0a202020202020202020202020226d656d6f7279223a20302c0a202020202020202020202020226d656d6f72795f73776170223a20302c0a202020202020202020202020226d6f756e7473223a205b5d2c0a202020202020202020202020226d7573745f72756e223a20747275652c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d7465737431222c0a202020202020202020202020226e6574776f726b5f616c696173223a206e756c6c2c0a202020202020202020202020226e6574776f726b5f64617461223a205b0a20202020202020202020202020207b0a202020202020202020202020202020202267617465776179223a20223137322e31372e302e31222c0a2020202020202020202020202020202022676c6f62616c5f697076365f61646472657373223a2022222c0a2020202020202020202020202020202022676c6f62616c5f697076365f7072656669785f6c656e677468223a20302c0a202020202020202020202020202020202269705f61646472657373223a20223137322e31372e302e33222c0a202020202020202020202020202020202269705f7072656669785f6c656e677468223a2031362c0a2020202020202020202020202020202022697076365f67617465776179223a2022222c0a20202020202020202020202020202020226e6574776f726b5f6e616d65223a2022627269646765220a20202020202020202020202020207d0a2020202020202020202020205d2c0a202020202020202020202020226e6574776f726b5f6d6f6465223a202264656661756c74222c0a202020202020202020202020226e6574776f726b73223a206e756c6c2c0a202020202020202020202020226e6574776f726b735f616476616e636564223a205b5d2c0a202020202020202020202020227069645f6d6f6465223a2022222c0a20202020202020202020202022706f727473223a205b5d2c0a2020202020202020202020202270726976696c65676564223a2066616c73652c0a202020202020202020202020227075626c6973685f616c6c5f706f727473223a2066616c73652c0a20202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202272656d6f76655f766f6c756d6573223a20747275652c0a2020202020202020202020202272657374617274223a20226e6f222c0a20202020202020202020202022726d223a2066616c73652c0a2020202020202020202020202272756e74696d65223a202272756e63222c0a2020202020202020202020202273656375726974795f6f707473223a205b5d2c0a2020202020202020202020202273686d5f73697a65223a2036342c0a202020202020202020202020227374617274223a20747275652c0a20202020202020202020202022737464696e5f6f70656e223a2066616c73652c0a2020202020202020202020202273746f705f7369676e616c223a2022222c0a2020202020202020202020202273746f705f74696d656f7574223a20302c0a2020202020202020202020202273746f726167655f6f707473223a206e756c6c2c0a2020202020202020202020202273797363746c73223a206e756c6c2c0a20202020202020202020202022746d706673223a206e756c6c2c0a20202020202020202020202022747479223a2066616c73652c0a20202020202020202020202022756c696d6974223a205b5d2c0a2020202020202020202020202275706c6f6164223a205b5d2c0a2020202020202020202020202275736572223a202231303030222c0a20202020202020202020202022757365726e735f6d6f6465223a2022222c0a20202020202020202020202022766f6c756d6573223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022636f6e7461696e65725f70617468223a20222f686f6d652f636f6465722f222c0a202020202020202020202020202020202266726f6d5f636f6e7461696e6572223a2022222c0a2020202020202020202020202020202022686f73745f70617468223a2022222c0a2020202020202020202020202020202022726561645f6f6e6c79223a2066616c73652c0a2020202020202020202020202020202022766f6c756d655f6e616d65223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74220a20202020202020202020202020207d0a2020202020202020202020205d2c0a20202020202020202020202022776f726b696e675f646972223a20222f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b0a2020202020202020202020205b0a20202020202020202020202020207b0a202020202020202020202020202020202274797065223a20226765745f61747472222c0a202020202020202020202020202020202276616c7565223a2022656e76220a20202020202020202020202020207d0a2020202020202020202020205d0a202020202020202020205d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a496966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65222c0a20202020202020202020202022646f636b65725f766f6c756d652e686f6d655f766f6c756d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a207b7d2c0a202020202020202020202020226964223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d696d746865737570657276697a722d74657374312d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', '452e84e3-6847-4fcc-b8b2-ee43cd1f37fc', '2022-11-09 13:08:10.801094+02') ON CONFLICT DO NOTHING; +INSERT INTO workspace_builds VALUES ('b56215f2-3b5e-406a-ac54-41c284dc9af3', '2022-11-02 13:09:51.764313+02', '2022-11-02 13:09:54.749685+02', 'b90547be-8870-4d68-8184-e8b2242b7c01', '4e681a60-83da-42c2-902e-6535376ebb77', 'tender_shamir3', 3, 'stop', '0ed9befc-4911-4ccf-a8e2-559bf72daa94', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a2031302c0a2020226c696e65616765223a202231623532313632352d643737322d333863302d326634652d343163386635333763323638222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202235386561326430392d393933622d343735342d386337642d306330656339343465326565222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202262393035343762652d383837302d346436382d383138342d653862323234326237633031222c0a202020202020202020202020226e616d65223a20227465737431222c0a202020202020202020202020226f776e6572223a2022696d746865737570657276697a72222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202230656439626566632d343931312d346363662d613865322d353539626637326461613934222c0a2020202020202020202020202273746172745f636f756e74223a20302c0a202020202020202020202020227472616e736974696f6e223a202273746f70220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a2022696d746865737570657276697a72222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a2022696d746865737570657276697a72220a2020202020202020202020207d2c0a202020202020202020202020226964223a202236313337353939642d346164362d346535382d623066612d613364316138386661356139222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202237303637643239342d393061392d343733342d613935622d306630316236363636633034220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202236313337353939642d346164362d346535382d623066612d613364316138386661356139222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202233613538356263372d373565362d343038652d623062632d646336346534643362653061222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a207b7d2c0a202020202020202020202020226964223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d696d746865737570657276697a722d74657374312d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d74657374312d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', '52874f66-89cc-4e6b-8066-80ba21ad9e57', '2022-11-09 13:09:54.749685+02') ON CONFLICT DO NOTHING; +INSERT INTO workspace_builds VALUES ('2bfa1945-e81f-44e4-8f41-394200f6cb30', '2022-11-02 13:10:42.104687+02', '2022-11-02 13:10:45.60543+02', '2d72d32e-3021-4843-b582-d962fee897e2', '920baba5-4c64-4686-8b7d-d1bef5683eae', 'adoring_satoshi8', 2, 'stop', '30095c71-380b-457a-8995-97b8ee6e5307', '\x7b0a20202276657273696f6e223a20342c0a2020227465727261666f726d5f76657273696f6e223a2022312e322e38222c0a20202273657269616c223a20372c0a2020226c696e65616765223a202230626637343762342d663639312d626461382d653066662d646336306230333566303932222c0a2020226f757470757473223a207b7d2c0a2020227265736f7572636573223a205b0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f70726f766973696f6e6572222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a202020202020202020202020226964223a202261396262396332372d613533622d343532342d383231622d363464336266326461343763222c0a202020202020202020202020226f73223a20226c696e7578220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a202264617461222c0a2020202020202274797065223a2022636f6465725f776f726b7370616365222c0a202020202020226e616d65223a20226d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226163636573735f706f7274223a20333233322c0a202020202020202020202020226163636573735f75726c223a2022687474703a2f2f3139322e3136382e312e3134363a33323332222c0a202020202020202020202020226964223a202232643732643332652d333032312d343834332d623538322d643936326665653839376532222c0a202020202020202020202020226e616d65223a20227363686564756c6564222c0a202020202020202020202020226f776e6572223a2022696d746865737570657276697a72222c0a202020202020202020202020226f776e65725f656d61696c223a2022222c0a202020202020202020202020226f776e65725f6964223a202230656439626566632d343931312d346363662d613865322d353539626637326461613934222c0a2020202020202020202020202273746172745f636f756e74223a20302c0a202020202020202020202020227472616e736974696f6e223a202273746f70220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f6167656e74222c0a202020202020226e616d65223a20226d61696e222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a2020202020202020202020202261726368223a2022616d643634222c0a2020202020202020202020202261757468223a2022746f6b656e222c0a20202020202020202020202022646972223a206e756c6c2c0a20202020202020202020202022656e76223a207b0a2020202020202020202020202020224749545f415554484f525f454d41494c223a2022222c0a2020202020202020202020202020224749545f415554484f525f4e414d45223a2022696d746865737570657276697a72222c0a2020202020202020202020202020224749545f434f4d4d49545445525f454d41494c223a2022222c0a2020202020202020202020202020224749545f434f4d4d49545445525f4e414d45223a2022696d746865737570657276697a72220a2020202020202020202020207d2c0a202020202020202020202020226964223a202238383431383734642d306335632d343961622d616231302d326336623865353039343333222c0a20202020202020202020202022696e69745f736372697074223a202223212f7573722f62696e2f656e762073685c6e736574202d65757820706970656661696c5c6e42494e4152595f4c4f434154494f4e3d24286d6b74656d70202d64202d7420746d702e636f646572585858585858292f636f6465725c6e42494e4152595f55524c3d687474703a2f2f3139322e3136382e312e3134363a333233322f62696e2f636f6465722d6c696e75782d616d6436345c6e6966207768696368206375726c205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c746375726c202d6673534c205c22247b42494e4152595f55524c7d5c22202d6f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682077676574205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7477676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c69662077686963682062757379626f78205c75303033652f6465762f6e756c6c20325c75303033655c7530303236313b207468656e5c6e5c7462757379626f782077676574202d71205c22247b42494e4152595f55524c7d5c22202d4f205c22247b42494e4152595f4c4f434154494f4e7d5c225c6e656c73655c6e5c746563686f205c226572726f723a206e6f20646f776e6c6f616420746f6f6c20666f756e642c20706c6561736520696e7374616c6c206375726c2c2077676574206f722062757379626f7820776765745c225c6e5c746578697420315c6e66695c6e63686d6f64202b78202442494e4152595f4c4f434154494f4e5c6e6578706f727420434f4445525f4147454e545f415554483d5c22746f6b656e5c225c6e6578706f727420434f4445525f4147454e545f55524c3d5c22687474703a2f2f3139322e3136382e312e3134363a333233322f5c225c6e65786563202442494e4152595f4c4f434154494f4e206167656e74222c0a202020202020202020202020226f73223a20226c696e7578222c0a20202020202020202020202022737461727475705f736372697074223a2022636f64652d736572766572202d2d61757468206e6f6e65222c0a20202020202020202020202022746f6b656e223a202234613131613336662d626230662d343764342d623737302d653430346166306465626535220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022636f6465725f617070222c0a202020202020226e616d65223a2022636f64652d736572766572222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f636f6465722f636f6465725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20302c0a202020202020202020202261747472696275746573223a207b0a202020202020202020202020226167656e745f6964223a202238383431383734642d306335632d343961622d616231302d326336623865353039343333222c0a20202020202020202020202022636f6d6d616e64223a206e756c6c2c0a202020202020202020202020226865616c7468636865636b223a205b0a20202020202020202020202020207b0a2020202020202020202020202020202022696e74657276616c223a20332c0a20202020202020202020202020202020227468726573686f6c64223a2031302c0a202020202020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f6865616c74687a220a20202020202020202020202020207d0a2020202020202020202020205d2c0a2020202020202020202020202269636f6e223a20222f69636f6e2f636f64652e737667222c0a202020202020202020202020226964223a202238366163393065322d636338312d343164632d613136652d656563613534633861336337222c0a202020202020202020202020226e616d65223a206e756c6c2c0a2020202020202020202020202272656c61746976655f70617468223a206e756c6c2c0a20202020202020202020202022737562646f6d61696e223a206e756c6c2c0a2020202020202020202020202275726c223a2022687474703a2f2f6c6f63616c686f73743a383038302f3f666f6c6465723d2f686f6d652f636f646572220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a2022626e567362413d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022636f6465725f6167656e742e6d61696e222c0a20202020202020202020202022646174612e636f6465725f70726f766973696f6e65722e6d65222c0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d2c0a202020207b0a202020202020226d6f6465223a20226d616e61676564222c0a2020202020202274797065223a2022646f636b65725f766f6c756d65222c0a202020202020226e616d65223a2022686f6d655f766f6c756d65222c0a2020202020202270726f7669646572223a202270726f76696465725b5c2272656769737472792e7465727261666f726d2e696f2f6b7265757a7765726b65722f646f636b65725c225d222c0a20202020202022696e7374616e636573223a205b0a20202020202020207b0a2020202020202020202022736368656d615f76657273696f6e223a20312c0a202020202020202020202261747472696275746573223a207b0a20202020202020202020202022647269766572223a20226c6f63616c222c0a202020202020202020202020226472697665725f6f707473223a207b7d2c0a202020202020202020202020226964223a2022636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f74222c0a202020202020202020202020226c6162656c73223a205b5d2c0a202020202020202020202020226d6f756e74706f696e74223a20222f7661722f6c69622f646f636b65722f766f6c756d65732f636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f742f5f64617461222c0a202020202020202020202020226e616d65223a2022636f6465722d696d746865737570657276697a722d7363686564756c65642d726f6f74220a202020202020202020207d2c0a202020202020202020202273656e7369746976655f61747472696275746573223a205b5d2c0a202020202020202020202270726976617465223a202265794a7a5932686c62574666646d567963326c7662694936496a456966513d3d222c0a2020202020202020202022646570656e64656e63696573223a205b0a20202020202020202020202022646174612e636f6465725f776f726b73706163652e6d65220a202020202020202020205d0a20202020202020207d0a2020202020205d0a202020207d0a20205d0a7d0a0a', 'd842dc52-93b9-46ed-842d-d1569b50ddfc', '2022-11-05 21:10:45.60543+02') ON CONFLICT DO NOTHING; -- -- Name: licenses_id_seq; Type: SEQUENCE SET; Schema: public; Owner: coder -- -SELECT pg_catalog.setval('public.licenses_id_seq', 1, false); +SELECT pg_catalog.setval('licenses_id_seq', 1, false); -- diff --git a/coderd/database/migrations/testdata/fixtures/000048_userdelete.up.sql b/coderd/database/migrations/testdata/fixtures/000048_userdelete.up.sql index c4f8b2e9097..e5594bacee1 100644 --- a/coderd/database/migrations/testdata/fixtures/000048_userdelete.up.sql +++ b/coderd/database/migrations/testdata/fixtures/000048_userdelete.up.sql @@ -1,34 +1,34 @@ -- This is a deleted user that shares the same username and linked_id as the existing user below. -- Any future migrations need to handle this case. -INSERT INTO public.users(id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, deleted) +INSERT INTO users(id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, deleted) VALUES ('a0061a8e-7db7-4585-838c-3116a003dd21', 'githubuser@coder.com', 'githubuser', '\x', '2022-11-02 13:05:21.445455+02', '2022-11-02 13:05:21.445455+02', 'active', '{}', true) ON CONFLICT DO NOTHING; -INSERT INTO public.organization_members VALUES ('a0061a8e-7db7-4585-838c-3116a003dd21', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:05:21.447595+02', '2022-11-02 13:05:21.447595+02', '{}') ON CONFLICT DO NOTHING; -INSERT INTO public.user_links(user_id, login_type, linked_id, oauth_access_token) +INSERT INTO organization_members VALUES ('a0061a8e-7db7-4585-838c-3116a003dd21', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:05:21.447595+02', '2022-11-02 13:05:21.447595+02', '{}') ON CONFLICT DO NOTHING; +INSERT INTO user_links(user_id, login_type, linked_id, oauth_access_token) VALUES('a0061a8e-7db7-4585-838c-3116a003dd21', 'github', '100', ''); -INSERT INTO public.users(id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, deleted) +INSERT INTO users(id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, deleted) VALUES ('fc1511ef-4fcf-4a3b-98a1-8df64160e35a', 'githubuser@coder.com', 'githubuser', '\x', '2022-11-02 13:05:21.445455+02', '2022-11-02 13:05:21.445455+02', 'active', '{}', false) ON CONFLICT DO NOTHING; -INSERT INTO public.organization_members VALUES ('fc1511ef-4fcf-4a3b-98a1-8df64160e35a', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:05:21.447595+02', '2022-11-02 13:05:21.447595+02', '{}') ON CONFLICT DO NOTHING; -INSERT INTO public.user_links(user_id, login_type, linked_id, oauth_access_token) +INSERT INTO organization_members VALUES ('fc1511ef-4fcf-4a3b-98a1-8df64160e35a', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:05:21.447595+02', '2022-11-02 13:05:21.447595+02', '{}') ON CONFLICT DO NOTHING; +INSERT INTO user_links(user_id, login_type, linked_id, oauth_access_token) VALUES('fc1511ef-4fcf-4a3b-98a1-8df64160e35a', 'github', '100', ''); -- Additionally, there is no unique constraint on user_id. So also add another user_link for the same user. -- This has happened on a production database. -INSERT INTO public.user_links(user_id, login_type, linked_id, oauth_access_token) +INSERT INTO user_links(user_id, login_type, linked_id, oauth_access_token) VALUES('fc1511ef-4fcf-4a3b-98a1-8df64160e35a', 'oidc', 'foo', ''); -- Lastly, make 2 other users who have the same user link. -INSERT INTO public.users(id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, deleted) +INSERT INTO users(id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, deleted) VALUES ('580ed397-727d-4aaf-950a-51f89f556c24', 'dup_link_a@coder.com', 'dupe_a', '\x', '2022-11-02 13:05:21.445455+02', '2022-11-02 13:05:21.445455+02', 'active', '{}', false) ON CONFLICT DO NOTHING; -INSERT INTO public.organization_members VALUES ('580ed397-727d-4aaf-950a-51f89f556c24', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:05:21.447595+02', '2022-11-02 13:05:21.447595+02', '{}') ON CONFLICT DO NOTHING; -INSERT INTO public.user_links(user_id, login_type, linked_id, oauth_access_token) +INSERT INTO organization_members VALUES ('580ed397-727d-4aaf-950a-51f89f556c24', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:05:21.447595+02', '2022-11-02 13:05:21.447595+02', '{}') ON CONFLICT DO NOTHING; +INSERT INTO user_links(user_id, login_type, linked_id, oauth_access_token) VALUES('580ed397-727d-4aaf-950a-51f89f556c24', 'github', '500', ''); -INSERT INTO public.users(id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, deleted) +INSERT INTO users(id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, deleted) VALUES ('c813366b-2fde-45ae-920c-101c3ad6a1e1', 'dup_link_b@coder.com', 'dupe_b', '\x', '2022-11-02 13:05:21.445455+02', '2022-11-02 13:05:21.445455+02', 'active', '{}', false) ON CONFLICT DO NOTHING; -INSERT INTO public.organization_members VALUES ('c813366b-2fde-45ae-920c-101c3ad6a1e1', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:05:21.447595+02', '2022-11-02 13:05:21.447595+02', '{}') ON CONFLICT DO NOTHING; -INSERT INTO public.user_links(user_id, login_type, linked_id, oauth_access_token) +INSERT INTO organization_members VALUES ('c813366b-2fde-45ae-920c-101c3ad6a1e1', 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', '2022-11-02 13:05:21.447595+02', '2022-11-02 13:05:21.447595+02', '{}') ON CONFLICT DO NOTHING; +INSERT INTO user_links(user_id, login_type, linked_id, oauth_access_token) VALUES('c813366b-2fde-45ae-920c-101c3ad6a1e1', 'github', '500', ''); diff --git a/coderd/database/migrations/testdata/fixtures/000150_workspace_app_usage_stats.up.sql b/coderd/database/migrations/testdata/fixtures/000150_workspace_app_usage_stats.up.sql index 9a9a8f0fa72..98655f7b2ff 100644 --- a/coderd/database/migrations/testdata/fixtures/000150_workspace_app_usage_stats.up.sql +++ b/coderd/database/migrations/testdata/fixtures/000150_workspace_app_usage_stats.up.sql @@ -1,4 +1,4 @@ -INSERT INTO public.workspace_app_stats ( +INSERT INTO workspace_app_stats ( id, user_id, workspace_id, diff --git a/coderd/database/migrations/testdata/fixtures/000276_workspace_modules.up.sql b/coderd/database/migrations/testdata/fixtures/000276_workspace_modules.up.sql index b2ff302722b..2cbeb15460a 100644 --- a/coderd/database/migrations/testdata/fixtures/000276_workspace_modules.up.sql +++ b/coderd/database/migrations/testdata/fixtures/000276_workspace_modules.up.sql @@ -1,5 +1,5 @@ INSERT INTO - public.workspace_modules ( + workspace_modules ( id, job_id, transition, diff --git a/coderd/database/migrations/testdata/fixtures/000291_workspace_parameter_presets.up.sql b/coderd/database/migrations/testdata/fixtures/000291_workspace_parameter_presets.up.sql index 296df73a587..17dc26da8e4 100644 --- a/coderd/database/migrations/testdata/fixtures/000291_workspace_parameter_presets.up.sql +++ b/coderd/database/migrations/testdata/fixtures/000291_workspace_parameter_presets.up.sql @@ -1,15 +1,15 @@ -INSERT INTO public.organizations (id, name, description, created_at, updated_at, is_default, display_name, icon) VALUES ('20362772-802a-4a72-8e4f-3648b4bfd168', 'strange_hopper58', 'wizardly_stonebraker60', '2025-02-07 07:46:19.507551 +00:00', '2025-02-07 07:46:19.507552 +00:00', false, 'competent_rhodes59', ''); +INSERT INTO organizations (id, name, description, created_at, updated_at, is_default, display_name, icon) VALUES ('20362772-802a-4a72-8e4f-3648b4bfd168', 'strange_hopper58', 'wizardly_stonebraker60', '2025-02-07 07:46:19.507551 +00:00', '2025-02-07 07:46:19.507552 +00:00', false, 'competent_rhodes59', ''); -INSERT INTO public.users (id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name, github_com_user_id, hashed_one_time_passcode, one_time_passcode_expires_at) VALUES ('6c353aac-20de-467b-bdfb-3c30a37adcd2', 'vigorous_murdock61', 'affectionate_hawking62', 'lqTu9C5363AwD7NVNH6noaGjp91XIuZJ', '2025-02-07 07:46:19.510861 +00:00', '2025-02-07 07:46:19.512949 +00:00', 'active', '{}', 'password', '', false, '0001-01-01 00:00:00.000000', '', '', 'vigilant_hugle63', null, null, null); +INSERT INTO users (id, email, username, hashed_password, created_at, updated_at, status, rbac_roles, login_type, avatar_url, deleted, last_seen_at, quiet_hours_schedule, theme_preference, name, github_com_user_id, hashed_one_time_passcode, one_time_passcode_expires_at) VALUES ('6c353aac-20de-467b-bdfb-3c30a37adcd2', 'vigorous_murdock61', 'affectionate_hawking62', 'lqTu9C5363AwD7NVNH6noaGjp91XIuZJ', '2025-02-07 07:46:19.510861 +00:00', '2025-02-07 07:46:19.512949 +00:00', 'active', '{}', 'password', '', false, '0001-01-01 00:00:00.000000', '', '', 'vigilant_hugle63', null, null, null); -INSERT INTO public.templates (id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level) VALUES ('6b298946-7a4f-47ac-9158-b03b08740a41', '2025-02-07 07:46:19.513317 +00:00', '2025-02-07 07:46:19.513317 +00:00', '20362772-802a-4a72-8e4f-3648b4bfd168', false, 'modest_leakey64', 'echo', 'e6cfa2a4-e4cf-4182-9e19-08b975682a28', 'upbeat_wright65', 604800000000000, '6c353aac-20de-467b-bdfb-3c30a37adcd2', 'nervous_keller66', '{}', '{"20362772-802a-4a72-8e4f-3648b4bfd168": ["read", "use"]}', 'determined_aryabhata67', false, true, true, 0, 0, 0, 0, 0, 0, false, '', 3600000000000, 'owner'); -INSERT INTO public.template_versions (id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id) VALUES ('af58bd62-428c-4c33-849b-d43a3be07d93', '6b298946-7a4f-47ac-9158-b03b08740a41', '20362772-802a-4a72-8e4f-3648b4bfd168', '2025-02-07 07:46:19.514782 +00:00', '2025-02-07 07:46:19.514782 +00:00', 'distracted_shockley68', 'sleepy_turing69', 'f2e2ea1c-5aa3-4a1d-8778-2e5071efae59', '6c353aac-20de-467b-bdfb-3c30a37adcd2', '[]', '', false, null); +INSERT INTO templates (id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id, description, default_ttl, created_by, icon, user_acl, group_acl, display_name, allow_user_cancel_workspace_jobs, allow_user_autostart, allow_user_autostop, failure_ttl, time_til_dormant, time_til_dormant_autodelete, autostop_requirement_days_of_week, autostop_requirement_weeks, autostart_block_days_of_week, require_active_version, deprecated, activity_bump, max_port_sharing_level) VALUES ('6b298946-7a4f-47ac-9158-b03b08740a41', '2025-02-07 07:46:19.513317 +00:00', '2025-02-07 07:46:19.513317 +00:00', '20362772-802a-4a72-8e4f-3648b4bfd168', false, 'modest_leakey64', 'echo', 'e6cfa2a4-e4cf-4182-9e19-08b975682a28', 'upbeat_wright65', 604800000000000, '6c353aac-20de-467b-bdfb-3c30a37adcd2', 'nervous_keller66', '{}', '{"20362772-802a-4a72-8e4f-3648b4bfd168": ["read", "use"]}', 'determined_aryabhata67', false, true, true, 0, 0, 0, 0, 0, 0, false, '', 3600000000000, 'owner'); +INSERT INTO template_versions (id, template_id, organization_id, created_at, updated_at, name, readme, job_id, created_by, external_auth_providers, message, archived, source_example_id) VALUES ('af58bd62-428c-4c33-849b-d43a3be07d93', '6b298946-7a4f-47ac-9158-b03b08740a41', '20362772-802a-4a72-8e4f-3648b4bfd168', '2025-02-07 07:46:19.514782 +00:00', '2025-02-07 07:46:19.514782 +00:00', 'distracted_shockley68', 'sleepy_turing69', 'f2e2ea1c-5aa3-4a1d-8778-2e5071efae59', '6c353aac-20de-467b-bdfb-3c30a37adcd2', '[]', '', false, null); -INSERT INTO public.template_version_presets (id, template_version_id, name, created_at) VALUES ('28b42cc0-c4fe-4907-a0fe-e4d20f1e9bfe', 'af58bd62-428c-4c33-849b-d43a3be07d93', 'test', '0001-01-01 00:00:00.000000 +00:00'); +INSERT INTO template_version_presets (id, template_version_id, name, created_at) VALUES ('28b42cc0-c4fe-4907-a0fe-e4d20f1e9bfe', 'af58bd62-428c-4c33-849b-d43a3be07d93', 'test', '0001-01-01 00:00:00.000000 +00:00'); -- Add presets with the same template version ID and name -- to ensure they're correctly handled by the 00031*_preset_prebuilds migration. -INSERT INTO public.template_version_presets ( +INSERT INTO template_version_presets ( id, template_version_id, name, created_at ) VALUES ( @@ -19,7 +19,7 @@ VALUES ( '0001-01-01 00:00:00.000000 +00:00' ); -INSERT INTO public.template_version_presets ( +INSERT INTO template_version_presets ( id, template_version_id, name, created_at ) VALUES ( @@ -29,4 +29,4 @@ VALUES ( '0001-01-01 00:00:00.000000 +00:00' ); -INSERT INTO public.template_version_preset_parameters (id, template_version_preset_id, name, value) VALUES ('ea90ccd2-5024-459e-87e4-879afd24de0f', '28b42cc0-c4fe-4907-a0fe-e4d20f1e9bfe', 'test', 'test'); +INSERT INTO template_version_preset_parameters (id, template_version_preset_id, name, value) VALUES ('ea90ccd2-5024-459e-87e4-879afd24de0f', '28b42cc0-c4fe-4907-a0fe-e4d20f1e9bfe', 'test', 'test'); diff --git a/coderd/database/migrations/testdata/fixtures/000366_create_tasks_data_model.up.sql b/coderd/database/migrations/testdata/fixtures/000366_create_tasks_data_model.up.sql index b96ffc771d0..2e7ed253a67 100644 --- a/coderd/database/migrations/testdata/fixtures/000366_create_tasks_data_model.up.sql +++ b/coderd/database/migrations/testdata/fixtures/000366_create_tasks_data_model.up.sql @@ -1,4 +1,4 @@ -INSERT INTO public.tasks VALUES ( +INSERT INTO tasks VALUES ( 'f5a1c3e4-8b2d-4f6a-9d7e-2a8b5c9e1f3d', -- id 'bb640d07-ca8a-4869-b6bc-ae61ebb2fda1', -- organization_id '30095c71-380b-457a-8995-97b8ee6e5307', -- owner_id @@ -11,7 +11,7 @@ INSERT INTO public.tasks VALUES ( NULL -- deleted_at ) ON CONFLICT DO NOTHING; -INSERT INTO public.task_workspace_apps VALUES ( +INSERT INTO task_workspace_apps VALUES ( 'f5a1c3e4-8b2d-4f6a-9d7e-2a8b5c9e1f3d', -- task_id 'a8c0b8c5-c9a8-4f33-93a4-8142e6858244', -- workspace_build_id '8fa17bbd-c48c-44c7-91ae-d4acbc755fad', -- workspace_agent_id diff --git a/coderd/database/migrations/testdata/fixtures/000379_create_tasks_with_status_view.up.sql b/coderd/database/migrations/testdata/fixtures/000379_create_tasks_with_status_view.up.sql index c2d1bf11475..b742b6c31be 100644 --- a/coderd/database/migrations/testdata/fixtures/000379_create_tasks_with_status_view.up.sql +++ b/coderd/database/migrations/testdata/fixtures/000379_create_tasks_with_status_view.up.sql @@ -1,4 +1,4 @@ -INSERT INTO public.task_workspace_apps VALUES ( +INSERT INTO task_workspace_apps VALUES ( 'f5a1c3e4-8b2d-4f6a-9d7e-2a8b5c9e1f3d', -- task_id NULL, -- workspace_agent_id NULL, -- workspace_app_id diff --git a/coderd/database/modelqueries.go b/coderd/database/modelqueries.go index fae0f3eca4f..d770ef4547e 100644 --- a/coderd/database/modelqueries.go +++ b/coderd/database/modelqueries.go @@ -607,6 +607,7 @@ func (q *sqlQuerier) CountAuthorizedAuditLogs(ctx context.Context, arg CountAudi arg.DateTo, arg.BuildReason, arg.RequestID, + arg.CountCap, ) if err != nil { return 0, err @@ -743,6 +744,7 @@ func (q *sqlQuerier) CountAuthorizedConnectionLogs(ctx context.Context, arg Coun arg.WorkspaceID, arg.ConnectionID, arg.Status, + arg.CountCap, ) if err != nil { return 0, err diff --git a/coderd/database/modelqueries_internal_test.go b/coderd/database/modelqueries_internal_test.go index 9e84324b72e..3f425b4347d 100644 --- a/coderd/database/modelqueries_internal_test.go +++ b/coderd/database/modelqueries_internal_test.go @@ -145,5 +145,13 @@ func extractWhereClause(query string) string { // Remove SQL comments whereClause = regexp.MustCompile(`(?m)--.*$`).ReplaceAllString(whereClause, "") + // Normalize indentation so subquery wrapping doesn't cause + // mismatches. + lines := strings.Split(whereClause, "\n") + for i, line := range lines { + lines[i] = strings.TrimLeft(line, " \t") + } + whereClause = strings.Join(lines, "\n") + return strings.TrimSpace(whereClause) } diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 5c249f3bdf1..727369140d6 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -73,6 +73,11 @@ type sqlcQuerier interface { // CountInProgressPrebuilds returns the number of in-progress prebuilds, grouped by preset ID and transition. // Prebuild considered in-progress if it's in the "pending", "starting", "stopping", or "deleting" state. CountInProgressPrebuilds(ctx context.Context) ([]CountInProgressPrebuildsRow, error) + // Groups OIDC user links by their issuer prefix (the part before "||" in + // linked_id) and returns a count for each. Empty linked_ids are reported + // with an empty issuer_prefix. Used for analysis before resetting + // mismatched links. + CountOIDCLinkedIDsByIssuer(ctx context.Context) ([]CountOIDCLinkedIDsByIssuerRow, error) // CountPendingNonActivePrebuilds returns the number of pending prebuilds for non-active template versions CountPendingNonActivePrebuilds(ctx context.Context) ([]CountPendingNonActivePrebuildsRow, error) CountUnreadInboxNotificationsByUserID(ctx context.Context, userID uuid.UUID) (int64, error) @@ -447,6 +452,7 @@ type sqlcQuerier interface { // We do not start counting from 0 at the start_time. We check the last status change before the start_time for each user. As such, // the result shows the total number of users in each status on any particular day. GetUserStatusCounts(ctx context.Context, arg GetUserStatusCountsParams) ([]GetUserStatusCountsRow, error) + GetUserTaskNotificationAlertDismissed(ctx context.Context, userID uuid.UUID) (bool, error) GetUserTerminalFont(ctx context.Context, userID uuid.UUID) (string, error) GetUserThemePreference(ctx context.Context, userID uuid.UUID) (string, error) GetUserWorkspaceBuildParameters(ctx context.Context, arg GetUserWorkspaceBuildParametersParams) ([]GetUserWorkspaceBuildParametersRow, error) @@ -653,11 +659,19 @@ type sqlcQuerier interface { // This will always work regardless of the current state of the template version. UnarchiveTemplateVersion(ctx context.Context, arg UnarchiveTemplateVersionParams) error UnfavoriteWorkspace(ctx context.Context, id uuid.UUID) error + // Resets linked_id to '' for OIDC links where the linked_id is non-empty + // and does not begin with the expected issuer prefix. This allows users to + // re-authenticate under a new OIDC provider. + UnlinkOIDCUsersByIssuerMismatch(ctx context.Context, expectedPrefix string) (int64, error) UpdateAIBridgeInterceptionEnded(ctx context.Context, arg UpdateAIBridgeInterceptionEndedParams) (AIBridgeInterception, error) UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error UpdateCryptoKeyDeletesAt(ctx context.Context, arg UpdateCryptoKeyDeletesAtParams) (CryptoKey, error) UpdateCustomRole(ctx context.Context, arg UpdateCustomRoleParams) (CustomRole, error) UpdateExternalAuthLink(ctx context.Context, arg UpdateExternalAuthLinkParams) (ExternalAuthLink, error) + // Optimistic lock: only update the row if the refresh token in the database + // still matches the one we read before attempting the refresh. This prevents + // a concurrent caller that lost a token-refresh race from overwriting a valid + // token stored by the winner. UpdateExternalAuthLinkRefreshToken(ctx context.Context, arg UpdateExternalAuthLinkRefreshTokenParams) error UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) (GitSSHKey, error) UpdateGroupByID(ctx context.Context, arg UpdateGroupByIDParams) (Group, error) @@ -706,6 +720,9 @@ type sqlcQuerier interface { UpdateUserHashedPassword(ctx context.Context, arg UpdateUserHashedPasswordParams) error UpdateUserLastSeenAt(ctx context.Context, arg UpdateUserLastSeenAtParams) (User, error) UpdateUserLink(ctx context.Context, arg UpdateUserLinkParams) (UserLink, error) + // Backfills linked_id for legacy user_links that were created before + // linked_id tracking was added. Only updates when linked_id is empty + // to avoid overwriting a valid binding. UpdateUserLinkedID(ctx context.Context, arg UpdateUserLinkedIDParams) (UserLink, error) UpdateUserLoginType(ctx context.Context, arg UpdateUserLoginTypeParams) (User, error) UpdateUserNotificationPreferences(ctx context.Context, arg UpdateUserNotificationPreferencesParams) (int64, error) @@ -714,6 +731,7 @@ type sqlcQuerier interface { UpdateUserRoles(ctx context.Context, arg UpdateUserRolesParams) (User, error) UpdateUserSecret(ctx context.Context, arg UpdateUserSecretParams) (UserSecret, error) UpdateUserStatus(ctx context.Context, arg UpdateUserStatusParams) (User, error) + UpdateUserTaskNotificationAlertDismissed(ctx context.Context, arg UpdateUserTaskNotificationAlertDismissedParams) (bool, error) UpdateUserTerminalFont(ctx context.Context, arg UpdateUserTerminalFontParams) (UserConfig, error) UpdateUserThemePreference(ctx context.Context, arg UpdateUserThemePreferenceParams) (UserConfig, error) UpdateVolumeResourceMonitor(ctx context.Context, arg UpdateVolumeResourceMonitorParams) error diff --git a/coderd/database/querier_test.go b/coderd/database/querier_test.go index 4dbb4a350a1..0e4d2e1b31b 100644 --- a/coderd/database/querier_test.go +++ b/coderd/database/querier_test.go @@ -1305,6 +1305,16 @@ func TestProxyByHostname(t *testing.T) { accessURL: "https://two.coder.com", wildcardHostname: "*--suffix.two.coder.com", }, + { + name: "three", + accessURL: "https://three.coder.com:8443", + wildcardHostname: "*.wildcard.three.coder.com", + }, + { + name: "four", + accessURL: "https://four.coder.com/", + wildcardHostname: "*.wildcard.four.coder.com", + }, } for _, p := range proxies { dbgen.WorkspaceProxy(t, db, database.WorkspaceProxy{ @@ -1335,6 +1345,34 @@ func TestProxyByHostname(t *testing.T) { allowWildcardHost: true, matchProxyName: "one", }, + { + name: "MatchAccessURLWithPort", + testHostname: "three.coder.com", + allowAccessURL: true, + allowWildcardHost: false, + matchProxyName: "three", + }, + { + name: "MatchAccessURLWithTrailingSlash", + testHostname: "four.coder.com", + allowAccessURL: true, + allowWildcardHost: false, + matchProxyName: "four", + }, + { + name: "RejectAccessURLPrefix", + testHostname: "one.coder", + allowAccessURL: true, + allowWildcardHost: false, + matchProxyName: "", + }, + { + name: "RejectAccessURLTLDPrefix", + testHostname: "one.coder.co", + allowAccessURL: true, + allowWildcardHost: false, + matchProxyName: "", + }, { name: "MatchWildcard", testHostname: "something.wildcard.one.coder.com", @@ -1342,6 +1380,13 @@ func TestProxyByHostname(t *testing.T) { allowWildcardHost: true, matchProxyName: "one", }, + { + name: "RejectWildcardHostnamePrefix", + testHostname: "something.wildcard.one.coder", + allowAccessURL: false, + allowWildcardHost: true, + matchProxyName: "", + }, { name: "MatchSuffix", testHostname: "something--suffix.two.coder.com", @@ -1349,6 +1394,13 @@ func TestProxyByHostname(t *testing.T) { allowWildcardHost: true, matchProxyName: "two", }, + { + name: "RejectSuffixHostnamePrefix", + testHostname: "something--suffix.two.coder", + allowAccessURL: false, + allowWildcardHost: true, + matchProxyName: "", + }, { name: "ValidateHostname/1", testHostname: ".*ne.coder.com", @@ -6076,6 +6128,178 @@ func TestWorkspaceAgentNameUniqueTrigger(t *testing.T) { }) } +func TestUpsertWorkspaceAppCannotRebindAcrossWorkspaces(t *testing.T) { + t.Parallel() + + db, _ := dbtestutil.NewDB(t) + org := dbgen.Organization(t, db, database.Organization{}) + ctx := testutil.Context(t, testutil.WaitShort) + + // createWorkspace builds the owner -> template -> version -> workspace chain + // and returns the workspace plus its template version so callers can create + // additional builds (and thus agents) within the same workspace. + createWorkspace := func(t *testing.T) (database.WorkspaceTable, uuid.UUID) { + t.Helper() + user := dbgen.User(t, db, database.User{}) + template := dbgen.Template(t, db, database.Template{ + OrganizationID: org.ID, + CreatedBy: user.ID, + }) + version := dbgen.TemplateVersion(t, db, database.TemplateVersion{ + TemplateID: uuid.NullUUID{Valid: true, UUID: template.ID}, + OrganizationID: org.ID, + CreatedBy: user.ID, + }) + workspace := dbgen.Workspace(t, db, database.WorkspaceTable{ + OrganizationID: org.ID, + TemplateID: template.ID, + OwnerID: user.ID, + }) + return workspace, version.ID + } + + // addAgent creates a build, resource, and agent for the workspace. The + // build's JobID matches the resource's JobID so the upsert's + // agent -> resource -> workspace_builds(job_id) -> workspace_id traversal + // resolves to the workspace. + addAgent := func(t *testing.T, workspace database.WorkspaceTable, versionID uuid.UUID, buildNumber int32) database.WorkspaceAgent { + t.Helper() + job := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{ + Type: database.ProvisionerJobTypeWorkspaceBuild, + OrganizationID: org.ID, + }) + dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{ + BuildNumber: buildNumber, + JobID: job.ID, + WorkspaceID: workspace.ID, + TemplateVersionID: versionID, + }) + resource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{ + JobID: job.ID, + }) + return dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{ + ResourceID: resource.ID, + }) + } + + upsertApp := func(appID, agentID uuid.UUID, slug string) (database.WorkspaceApp, error) { + return db.UpsertWorkspaceApp(ctx, database.UpsertWorkspaceAppParams{ + ID: appID, + CreatedAt: dbtime.Now(), + AgentID: agentID, + Slug: slug, + DisplayName: "Code Server", + Icon: "/icon.png", + SharingLevel: database.AppSharingLevelOwner, + Health: database.WorkspaceAppHealthDisabled, + OpenIn: database.WorkspaceAppOpenInSlimWindow, + }) + } + + // Given: two independent workspaces, each with an agent that resolves to its + // own workspace. + workspaceA, versionA := createWorkspace(t) + workspaceB, versionB := createWorkspace(t) + agentA := addAgent(t, workspaceA, versionA, 1) + agentB := addAgent(t, workspaceB, versionB, 1) + + gotA, err := db.GetWorkspaceByAgentID(ctx, agentA.ID) + require.NoError(t, err) + require.Equal(t, workspaceA.ID, gotA.ID) + gotB, err := db.GetWorkspaceByAgentID(ctx, agentB.ID) + require.NoError(t, err) + require.Equal(t, workspaceB.ID, gotB.ID) + + appID := uuid.New() + const originalSlug = "code-server" + + // Initial insert under workspace A's agent succeeds (no conflict). + app, err := upsertApp(appID, agentA.ID, originalSlug) + require.NoError(t, err) + require.Equal(t, appID, app.ID) + require.Equal(t, agentA.ID, app.AgentID) + require.Equal(t, originalSlug, app.Slug) + + // Upserting the same app id onto workspace B's agent is rejected because the + // existing row and the incoming agent resolve to different workspaces. The + // guard updates zero rows, so the :one query returns sql.ErrNoRows. + _, err = upsertApp(appID, agentB.ID, "hijacked") + require.ErrorIs(t, err, sql.ErrNoRows) + + // The app remains bound to workspace A's agent, unchanged. + appsA, err := db.GetWorkspaceAppsByAgentID(ctx, agentA.ID) + require.NoError(t, err) + require.Len(t, appsA, 1) + require.Equal(t, appID, appsA[0].ID) + require.Equal(t, agentA.ID, appsA[0].AgentID) + require.Equal(t, originalSlug, appsA[0].Slug) + + // Workspace B's agent has no app. + appsB, err := db.GetWorkspaceAppsByAgentID(ctx, agentB.ID) + require.NoError(t, err) + require.Empty(t, appsB) + + // A legitimate rebuild of workspace A produces a new agent (agent IDs are + // regenerated every build). Rebinding the persistent app to it succeeds + // because both agents resolve to workspace A. + agentA2 := addAgent(t, workspaceA, versionA, 2) + app, err = upsertApp(appID, agentA2.ID, "code-server-v2") + require.NoError(t, err) + require.Equal(t, agentA2.ID, app.AgentID) + require.Equal(t, "code-server-v2", app.Slug) + + appsA2, err := db.GetWorkspaceAppsByAgentID(ctx, agentA2.ID) + require.NoError(t, err) + require.Len(t, appsA2, 1) + require.Equal(t, appID, appsA2[0].ID) + + // Set up a template-import agent. It is intentionally not associated with + // a workspace build, so it resolves to no workspace. + importJob := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{ + Type: database.ProvisionerJobTypeTemplateVersionImport, + OrganizationID: org.ID, + }) + importResource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{ + JobID: importJob.ID, + }) + importAgent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{ + ResourceID: importResource.ID, + }) + _, err = db.GetWorkspaceByAgentID(ctx, importAgent.ID) + require.ErrorIs(t, err, sql.ErrNoRows, "import agent must not resolve to a workspace") + + // An app that already belongs to a workspace cannot be rebound to a + // template-import agent. Otherwise a second update could move it from + // the import agent to a different workspace. + _, err = upsertApp(appID, importAgent.ID, "hijacked-by-import") + require.ErrorIs(t, err, sql.ErrNoRows) + + appsA2, err = db.GetWorkspaceAppsByAgentID(ctx, agentA2.ID) + require.NoError(t, err) + require.Len(t, appsA2, 1) + require.Equal(t, appID, appsA2[0].ID) + require.Equal(t, agentA2.ID, appsA2[0].AgentID) + require.Equal(t, "code-server-v2", appsA2[0].Slug) + + appsImport, err := db.GetWorkspaceAppsByAgentID(ctx, importAgent.ID) + require.NoError(t, err) + require.Empty(t, appsImport) + + _, err = upsertApp(appID, agentB.ID, "hijacked-after-import") + require.ErrorIs(t, err, sql.ErrNoRows) + + unownedAppID := uuid.New() + _, err = upsertApp(unownedAppID, importAgent.ID, "import-app") + require.NoError(t, err) + + // An app whose existing agent belongs to a template-import job resolves to + // no workspace, so rebinding it is permitted. It is not a cross-tenant + // victim. + rebound, err := upsertApp(unownedAppID, agentA.ID, "import-app") + require.NoError(t, err) + require.Equal(t, agentA.ID, rebound.AgentID) +} + func TestGetWorkspaceAgentsByParentID(t *testing.T) { t.Parallel() @@ -6107,6 +6331,56 @@ func TestGetWorkspaceAgentsByParentID(t *testing.T) { }) } +func TestGetWorkspaceAgentByInstanceID(t *testing.T) { + t.Parallel() + + // Context: https://github.com/coder/coder/pull/22196 + t.Run("DoesNotReturnSubAgents", func(t *testing.T) { + t.Parallel() + + // Given: A parent workspace agent with an AuthInstanceID and a + // sub-agent that shares the same AuthInstanceID. + db, _ := dbtestutil.NewDB(t) + org := dbgen.Organization(t, db, database.Organization{}) + job := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{ + Type: database.ProvisionerJobTypeTemplateVersionImport, + OrganizationID: org.ID, + }) + resource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{ + JobID: job.ID, + }) + + authInstanceID := fmt.Sprintf("instance-%s-%d", t.Name(), time.Now().UnixNano()) + parentAgent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{ + ResourceID: resource.ID, + AuthInstanceID: sql.NullString{ + String: authInstanceID, + Valid: true, + }, + }) + // Create a sub-agent with the same AuthInstanceID (simulating + // the old behavior before the fix). + _ = dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{ + ParentID: uuid.NullUUID{UUID: parentAgent.ID, Valid: true}, + ResourceID: resource.ID, + AuthInstanceID: sql.NullString{ + String: authInstanceID, + Valid: true, + }, + }) + + ctx := testutil.Context(t, testutil.WaitShort) + + // When: We look up the agent by instance ID. + agent, err := db.GetWorkspaceAgentByInstanceID(ctx, authInstanceID) + require.NoError(t, err) + + // Then: The result must be the parent agent, not the sub-agent. + assert.Equal(t, parentAgent.ID, agent.ID, "instance ID lookup should return the parent agent, not a sub-agent") + assert.False(t, agent.ParentID.Valid, "returned agent should not have a parent (should be the parent itself)") + }) +} + func requireUsersMatch(t testing.TB, expected []database.User, found []database.GetUsersRow, msg string) { t.Helper() require.ElementsMatch(t, expected, database.ConvertUserRows(found), msg) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index d99155ba999..572c8d2e9c3 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -1486,93 +1486,105 @@ func (q *sqlQuerier) UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDP } const countAuditLogs = `-- name: CountAuditLogs :one -SELECT COUNT(*) -FROM audit_logs - LEFT JOIN users ON audit_logs.user_id = users.id - LEFT JOIN organizations ON audit_logs.organization_id = organizations.id - -- First join on workspaces to get the initial workspace create - -- to workspace build 1 id. This is because the first create is - -- is a different audit log than subsequent starts. - LEFT JOIN workspaces ON audit_logs.resource_type = 'workspace' - AND audit_logs.resource_id = workspaces.id - -- Get the reason from the build if the resource type - -- is a workspace_build - LEFT JOIN workspace_builds wb_build ON audit_logs.resource_type = 'workspace_build' - AND audit_logs.resource_id = wb_build.id - -- Get the reason from the build #1 if this is the first - -- workspace create. - LEFT JOIN workspace_builds wb_workspace ON audit_logs.resource_type = 'workspace' - AND audit_logs.action = 'create' - AND workspaces.id = wb_workspace.workspace_id - AND wb_workspace.build_number = 1 -WHERE - -- Filter resource_type - CASE - WHEN $1::text != '' THEN resource_type = $1::resource_type - ELSE true - END - -- Filter resource_id - AND CASE - WHEN $2::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN resource_id = $2 - ELSE true - END - -- Filter organization_id - AND CASE - WHEN $3::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN audit_logs.organization_id = $3 - ELSE true - END - -- Filter by resource_target - AND CASE - WHEN $4::text != '' THEN resource_target = $4 - ELSE true - END - -- Filter action - AND CASE - WHEN $5::text != '' THEN action = $5::audit_action - ELSE true - END - -- Filter by user_id - AND CASE - WHEN $6::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN user_id = $6 - ELSE true - END - -- Filter by username - AND CASE - WHEN $7::text != '' THEN user_id = ( - SELECT id - FROM users - WHERE lower(username) = lower($7) - AND deleted = false - ) - ELSE true - END - -- Filter by user_email - AND CASE - WHEN $8::text != '' THEN users.email = $8 - ELSE true - END - -- Filter by date_from - AND CASE - WHEN $9::timestamp with time zone != '0001-01-01 00:00:00Z' THEN "time" >= $9 - ELSE true - END - -- Filter by date_to - AND CASE - WHEN $10::timestamp with time zone != '0001-01-01 00:00:00Z' THEN "time" <= $10 - ELSE true - END - -- Filter by build_reason - AND CASE - WHEN $11::text != '' THEN COALESCE(wb_build.reason::text, wb_workspace.reason::text) = $11 - ELSE true - END - -- Filter request_id - AND CASE - WHEN $12::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN audit_logs.request_id = $12 - ELSE true - END - -- Authorize Filter clause will be injected below in CountAuthorizedAuditLogs - -- @authorize_filter +SELECT COUNT(*) FROM ( + SELECT 1 + FROM audit_logs + LEFT JOIN users ON audit_logs.user_id = users.id + LEFT JOIN organizations ON audit_logs.organization_id = organizations.id + -- First join on workspaces to get the initial workspace create + -- to workspace build 1 id. This is because the first create is + -- is a different audit log than subsequent starts. + LEFT JOIN workspaces ON audit_logs.resource_type = 'workspace' + AND audit_logs.resource_id = workspaces.id + -- Get the reason from the build if the resource type + -- is a workspace_build + LEFT JOIN workspace_builds wb_build ON audit_logs.resource_type = 'workspace_build' + AND audit_logs.resource_id = wb_build.id + -- Get the reason from the build #1 if this is the first + -- workspace create. + LEFT JOIN workspace_builds wb_workspace ON audit_logs.resource_type = 'workspace' + AND audit_logs.action = 'create' + AND workspaces.id = wb_workspace.workspace_id + AND wb_workspace.build_number = 1 + WHERE + -- Filter resource_type + CASE + WHEN $1::text != '' THEN resource_type = $1::resource_type + ELSE true + END + -- Filter resource_id + AND CASE + WHEN $2::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN resource_id = $2 + ELSE true + END + -- Filter organization_id + AND CASE + WHEN $3::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN audit_logs.organization_id = $3 + ELSE true + END + -- Filter by resource_target + AND CASE + WHEN $4::text != '' THEN resource_target = $4 + ELSE true + END + -- Filter action + AND CASE + WHEN $5::text != '' THEN action = $5::audit_action + ELSE true + END + -- Filter by user_id + AND CASE + WHEN $6::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN user_id = $6 + ELSE true + END + -- Filter by username + AND CASE + WHEN $7::text != '' THEN user_id = ( + SELECT id + FROM users + WHERE lower(username) = lower($7) + AND deleted = false + ) + ELSE true + END + -- Filter by user_email + AND CASE + WHEN $8::text != '' THEN users.email = $8 + ELSE true + END + -- Filter by date_from + AND CASE + WHEN $9::timestamp with time zone != '0001-01-01 00:00:00Z' THEN "time" >= $9 + ELSE true + END + -- Filter by date_to + AND CASE + WHEN $10::timestamp with time zone != '0001-01-01 00:00:00Z' THEN "time" <= $10 + ELSE true + END + -- Filter by build_reason + AND CASE + WHEN $11::text != '' THEN COALESCE(wb_build.reason::text, wb_workspace.reason::text) = $11 + ELSE true + END + -- Filter request_id + AND CASE + WHEN $12::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN audit_logs.request_id = $12 + ELSE true + END + -- Authorize Filter clause will be injected below in CountAuthorizedAuditLogs + -- @authorize_filter + -- Avoid a slow scan on a large table with joins. The caller + -- passes the count cap and we add 1 so the frontend can detect + -- capping and show "... of N+". A cap of 0 means no limit (NULLIF + -- -> NULL + 1 = NULL). + -- NOTE: Parameterizing this so that we can easily change from, + -- e.g., 2000 to 5000. However, use literal NULL (or no LIMIT) + -- here if disabling the capping on a large table permanently. + -- This way the PG planner can plan parallel execution for + -- potential large wins. + LIMIT NULLIF($13::int, 0) + 1 +) AS limited_count ` type CountAuditLogsParams struct { @@ -1588,6 +1600,7 @@ type CountAuditLogsParams struct { DateTo time.Time `db:"date_to" json:"date_to"` BuildReason string `db:"build_reason" json:"build_reason"` RequestID uuid.UUID `db:"request_id" json:"request_id"` + CountCap int32 `db:"count_cap" json:"count_cap"` } func (q *sqlQuerier) CountAuditLogs(ctx context.Context, arg CountAuditLogsParams) (int64, error) { @@ -1604,6 +1617,7 @@ func (q *sqlQuerier) CountAuditLogs(ctx context.Context, arg CountAuditLogsParam arg.DateTo, arg.BuildReason, arg.RequestID, + arg.CountCap, ) var count int64 err := row.Scan(&count) @@ -1952,110 +1966,113 @@ func (q *sqlQuerier) InsertAuditLog(ctx context.Context, arg InsertAuditLogParam } const countConnectionLogs = `-- name: CountConnectionLogs :one -SELECT - COUNT(*) AS count -FROM - connection_logs -JOIN users AS workspace_owner ON - connection_logs.workspace_owner_id = workspace_owner.id -LEFT JOIN users ON - connection_logs.user_id = users.id -JOIN organizations ON - connection_logs.organization_id = organizations.id -WHERE - -- Filter organization_id - CASE - WHEN $1 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - connection_logs.organization_id = $1 - ELSE true - END - -- Filter by workspace owner username - AND CASE - WHEN $2 :: text != '' THEN - workspace_owner_id = ( - SELECT id FROM users - WHERE lower(username) = lower($2) AND deleted = false - ) - ELSE true - END - -- Filter by workspace_owner_id - AND CASE - WHEN $3 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - workspace_owner_id = $3 - ELSE true - END - -- Filter by workspace_owner_email - AND CASE - WHEN $4 :: text != '' THEN - workspace_owner_id = ( - SELECT id FROM users - WHERE email = $4 AND deleted = false - ) - ELSE true - END - -- Filter by type - AND CASE - WHEN $5 :: text != '' THEN - type = $5 :: connection_type - ELSE true - END - -- Filter by user_id - AND CASE - WHEN $6 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - user_id = $6 - ELSE true - END - -- Filter by username - AND CASE - WHEN $7 :: text != '' THEN - user_id = ( - SELECT id FROM users - WHERE lower(username) = lower($7) AND deleted = false - ) - ELSE true - END - -- Filter by user_email - AND CASE - WHEN $8 :: text != '' THEN - users.email = $8 - ELSE true - END - -- Filter by connected_after - AND CASE - WHEN $9 :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN - connect_time >= $9 - ELSE true - END - -- Filter by connected_before - AND CASE - WHEN $10 :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN - connect_time <= $10 - ELSE true - END - -- Filter by workspace_id - AND CASE - WHEN $11 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - connection_logs.workspace_id = $11 - ELSE true - END - -- Filter by connection_id - AND CASE - WHEN $12 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - connection_logs.connection_id = $12 - ELSE true - END - -- Filter by whether the session has a disconnect_time - AND CASE - WHEN $13 :: text != '' THEN - (($13 = 'ongoing' AND disconnect_time IS NULL) OR - ($13 = 'completed' AND disconnect_time IS NOT NULL)) AND - -- Exclude web events, since we don't know their close time. - "type" NOT IN ('workspace_app', 'port_forwarding') - ELSE true - END - -- Authorize Filter clause will be injected below in - -- CountAuthorizedConnectionLogs - -- @authorize_filter +SELECT COUNT(*) AS count FROM ( + SELECT 1 + FROM + connection_logs + JOIN users AS workspace_owner ON + connection_logs.workspace_owner_id = workspace_owner.id + LEFT JOIN users ON + connection_logs.user_id = users.id + JOIN organizations ON + connection_logs.organization_id = organizations.id + WHERE + -- Filter organization_id + CASE + WHEN $1 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + connection_logs.organization_id = $1 + ELSE true + END + -- Filter by workspace owner username + AND CASE + WHEN $2 :: text != '' THEN + workspace_owner_id = ( + SELECT id FROM users + WHERE lower(username) = lower($2) AND deleted = false + ) + ELSE true + END + -- Filter by workspace_owner_id + AND CASE + WHEN $3 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + workspace_owner_id = $3 + ELSE true + END + -- Filter by workspace_owner_email + AND CASE + WHEN $4 :: text != '' THEN + workspace_owner_id = ( + SELECT id FROM users + WHERE email = $4 AND deleted = false + ) + ELSE true + END + -- Filter by type + AND CASE + WHEN $5 :: text != '' THEN + type = $5 :: connection_type + ELSE true + END + -- Filter by user_id + AND CASE + WHEN $6 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + user_id = $6 + ELSE true + END + -- Filter by username + AND CASE + WHEN $7 :: text != '' THEN + user_id = ( + SELECT id FROM users + WHERE lower(username) = lower($7) AND deleted = false + ) + ELSE true + END + -- Filter by user_email + AND CASE + WHEN $8 :: text != '' THEN + users.email = $8 + ELSE true + END + -- Filter by connected_after + AND CASE + WHEN $9 :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN + connect_time >= $9 + ELSE true + END + -- Filter by connected_before + AND CASE + WHEN $10 :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN + connect_time <= $10 + ELSE true + END + -- Filter by workspace_id + AND CASE + WHEN $11 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + connection_logs.workspace_id = $11 + ELSE true + END + -- Filter by connection_id + AND CASE + WHEN $12 :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + connection_logs.connection_id = $12 + ELSE true + END + -- Filter by whether the session has a disconnect_time + AND CASE + WHEN $13 :: text != '' THEN + (($13 = 'ongoing' AND disconnect_time IS NULL) OR + ($13 = 'completed' AND disconnect_time IS NOT NULL)) AND + -- Exclude web events, since we don't know their close time. + "type" NOT IN ('workspace_app', 'port_forwarding') + ELSE true + END + -- Authorize Filter clause will be injected below in + -- CountAuthorizedConnectionLogs + -- @authorize_filter + -- NOTE: See the CountAuditLogs LIMIT note. + LIMIT NULLIF($14::int, 0) + 1 +) AS limited_count ` type CountConnectionLogsParams struct { @@ -2072,6 +2089,7 @@ type CountConnectionLogsParams struct { WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"` ConnectionID uuid.UUID `db:"connection_id" json:"connection_id"` Status string `db:"status" json:"status"` + CountCap int32 `db:"count_cap" json:"count_cap"` } func (q *sqlQuerier) CountConnectionLogs(ctx context.Context, arg CountConnectionLogsParams) (int64, error) { @@ -2089,6 +2107,7 @@ func (q *sqlQuerier) CountConnectionLogs(ctx context.Context, arg CountConnectio arg.WorkspaceID, arg.ConnectionID, arg.Status, + arg.CountCap, ) var count int64 err := row.Scan(&count) @@ -2960,9 +2979,11 @@ WHERE provider_id = $4 AND user_id = $5 +AND + oauth_refresh_token = $6 AND -- Required for sqlc to generate a parameter for the oauth_refresh_token_key_id - $6 :: text = $6 :: text + $7 :: text = $7 :: text ` type UpdateExternalAuthLinkRefreshTokenParams struct { @@ -2971,9 +2992,14 @@ type UpdateExternalAuthLinkRefreshTokenParams struct { UpdatedAt time.Time `db:"updated_at" json:"updated_at"` ProviderID string `db:"provider_id" json:"provider_id"` UserID uuid.UUID `db:"user_id" json:"user_id"` + OldOauthRefreshToken string `db:"old_oauth_refresh_token" json:"old_oauth_refresh_token"` OAuthRefreshTokenKeyID string `db:"oauth_refresh_token_key_id" json:"oauth_refresh_token_key_id"` } +// Optimistic lock: only update the row if the refresh token in the database +// still matches the one we read before attempting the refresh. This prevents +// a concurrent caller that lost a token-refresh race from overwriting a valid +// token stored by the winner. func (q *sqlQuerier) UpdateExternalAuthLinkRefreshToken(ctx context.Context, arg UpdateExternalAuthLinkRefreshTokenParams) error { _, err := q.db.ExecContext(ctx, updateExternalAuthLinkRefreshToken, arg.OauthRefreshFailureReason, @@ -2981,6 +3007,7 @@ func (q *sqlQuerier) UpdateExternalAuthLinkRefreshToken(ctx context.Context, arg arg.UpdatedAt, arg.ProviderID, arg.UserID, + arg.OldOauthRefreshToken, arg.OAuthRefreshTokenKeyID, ) return err @@ -11271,7 +11298,7 @@ WHERE ( ( $2 :: bool = true AND - url SIMILAR TO '[^:]*://' || $1 :: text || '([:/]?%)*' + url SIMILAR TO '[^:]*://' || $1 :: text || '([:/]%)*' ) OR ( $3 :: bool = true AND @@ -15583,6 +15610,55 @@ func (q *sqlQuerier) UpdateUsageEventsPostPublish(ctx context.Context, arg Updat return err } +const countOIDCLinkedIDsByIssuer = `-- name: CountOIDCLinkedIDsByIssuer :many +SELECT + (CASE + WHEN user_links.linked_id = '' THEN '' + ELSE split_part(user_links.linked_id, '||', 1) + END)::text AS issuer_prefix, + COUNT(*)::int AS count +FROM + user_links +INNER JOIN + users ON user_links.user_id = users.id +WHERE + user_links.login_type = 'oidc' + AND users.deleted = false +GROUP BY issuer_prefix +` + +type CountOIDCLinkedIDsByIssuerRow struct { + IssuerPrefix string `db:"issuer_prefix" json:"issuer_prefix"` + Count int32 `db:"count" json:"count"` +} + +// Groups OIDC user links by their issuer prefix (the part before "||" in +// linked_id) and returns a count for each. Empty linked_ids are reported +// with an empty issuer_prefix. Used for analysis before resetting +// mismatched links. +func (q *sqlQuerier) CountOIDCLinkedIDsByIssuer(ctx context.Context) ([]CountOIDCLinkedIDsByIssuerRow, error) { + rows, err := q.db.QueryContext(ctx, countOIDCLinkedIDsByIssuer) + if err != nil { + return nil, err + } + defer rows.Close() + var items []CountOIDCLinkedIDsByIssuerRow + for rows.Next() { + var i CountOIDCLinkedIDsByIssuerRow + if err := rows.Scan(&i.IssuerPrefix, &i.Count); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getUserLinkByLinkedID = `-- name: GetUserLinkByLinkedID :one SELECT user_links.user_id, user_links.login_type, user_links.linked_id, user_links.oauth_access_token, user_links.oauth_refresh_token, user_links.oauth_expiry, user_links.oauth_access_token_key_id, user_links.oauth_refresh_token_key_id, user_links.claims @@ -15841,6 +15917,28 @@ func (q *sqlQuerier) OIDCClaimFields(ctx context.Context, organizationID uuid.UU return items, nil } +const unlinkOIDCUsersByIssuerMismatch = `-- name: UnlinkOIDCUsersByIssuerMismatch :execrows +UPDATE user_links +SET linked_id = '' +FROM users +WHERE user_links.user_id = users.id + AND user_links.login_type = 'oidc' + AND user_links.linked_id != '' + AND NOT starts_with(user_links.linked_id, $1) + AND users.deleted = false +` + +// Resets linked_id to ” for OIDC links where the linked_id is non-empty +// and does not begin with the expected issuer prefix. This allows users to +// re-authenticate under a new OIDC provider. +func (q *sqlQuerier) UnlinkOIDCUsersByIssuerMismatch(ctx context.Context, expectedPrefix string) (int64, error) { + result, err := q.db.ExecContext(ctx, unlinkOIDCUsersByIssuerMismatch, expectedPrefix) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + const updateUserLink = `-- name: UpdateUserLink :one UPDATE user_links @@ -15898,7 +15996,7 @@ UPDATE SET linked_id = $1 WHERE - user_id = $2 AND login_type = $3 RETURNING user_id, login_type, linked_id, oauth_access_token, oauth_refresh_token, oauth_expiry, oauth_access_token_key_id, oauth_refresh_token_key_id, claims + user_id = $2 AND login_type = $3 AND linked_id = '' RETURNING user_id, login_type, linked_id, oauth_access_token, oauth_refresh_token, oauth_expiry, oauth_access_token_key_id, oauth_refresh_token_key_id, claims ` type UpdateUserLinkedIDParams struct { @@ -15907,6 +16005,9 @@ type UpdateUserLinkedIDParams struct { LoginType LoginType `db:"login_type" json:"login_type"` } +// Backfills linked_id for legacy user_links that were created before +// linked_id tracking was added. Only updates when linked_id is empty +// to avoid overwriting a valid binding. func (q *sqlQuerier) UpdateUserLinkedID(ctx context.Context, arg UpdateUserLinkedIDParams) (UserLink, error) { row := q.db.QueryRowContext(ctx, updateUserLinkedID, arg.LinkedID, arg.UserID, arg.LoginType) var i UserLink @@ -16324,6 +16425,23 @@ func (q *sqlQuerier) GetUserCount(ctx context.Context, includeSystem bool) (int6 return count, err } +const getUserTaskNotificationAlertDismissed = `-- name: GetUserTaskNotificationAlertDismissed :one +SELECT + value::boolean as task_notification_alert_dismissed +FROM + user_configs +WHERE + user_id = $1 + AND key = 'preference_task_notification_alert_dismissed' +` + +func (q *sqlQuerier) GetUserTaskNotificationAlertDismissed(ctx context.Context, userID uuid.UUID) (bool, error) { + row := q.db.QueryRowContext(ctx, getUserTaskNotificationAlertDismissed, userID) + var task_notification_alert_dismissed bool + err := row.Scan(&task_notification_alert_dismissed) + return task_notification_alert_dismissed, err +} + const getUserTerminalFont = `-- name: GetUserTerminalFont :one SELECT value as terminal_font @@ -17076,6 +17194,33 @@ func (q *sqlQuerier) UpdateUserStatus(ctx context.Context, arg UpdateUserStatusP return i, err } +const updateUserTaskNotificationAlertDismissed = `-- name: UpdateUserTaskNotificationAlertDismissed :one +INSERT INTO + user_configs (user_id, key, value) +VALUES + ($1, 'preference_task_notification_alert_dismissed', ($2::boolean)::text) +ON CONFLICT + ON CONSTRAINT user_configs_pkey +DO UPDATE +SET + value = $2 +WHERE user_configs.user_id = $1 + AND user_configs.key = 'preference_task_notification_alert_dismissed' +RETURNING value::boolean AS task_notification_alert_dismissed +` + +type UpdateUserTaskNotificationAlertDismissedParams struct { + UserID uuid.UUID `db:"user_id" json:"user_id"` + TaskNotificationAlertDismissed bool `db:"task_notification_alert_dismissed" json:"task_notification_alert_dismissed"` +} + +func (q *sqlQuerier) UpdateUserTaskNotificationAlertDismissed(ctx context.Context, arg UpdateUserTaskNotificationAlertDismissedParams) (bool, error) { + row := q.db.QueryRowContext(ctx, updateUserTaskNotificationAlertDismissed, arg.UserID, arg.TaskNotificationAlertDismissed) + var task_notification_alert_dismissed bool + err := row.Scan(&task_notification_alert_dismissed) + return task_notification_alert_dismissed, err +} + const updateUserTerminalFont = `-- name: UpdateUserTerminalFont :one INSERT INTO user_configs (user_id, key, value) @@ -18008,6 +18153,8 @@ WHERE auth_instance_id = $1 :: TEXT -- Filter out deleted sub agents. AND deleted = FALSE + -- Filter out sub agents, they do not authenticate with auth_instance_id. + AND parent_id IS NULL ORDER BY created_at DESC ` @@ -20428,6 +20575,42 @@ ON CONFLICT (id) DO UPDATE SET agent_id = EXCLUDED.agent_id, slug = EXCLUDED.slug, tooltip = EXCLUDED.tooltip +WHERE + -- Prevent cross-tenant/cross-workspace agent rebinding (SEC-91). + -- App IDs persist across builds of the same workspace, but agent IDs are + -- regenerated every build, so compare by the workspace that owns the agent + -- rather than by agent_id. Permit unowned apps to be claimed and permit + -- same-workspace rebuilds. If an existing app belongs to a workspace, block + -- moves to both different workspaces and template import or dry-run agents + -- that resolve to no workspace. The conflicting row is then left untouched, + -- and the :one query returns no row, which the caller treats as a + -- rejection. + NOT EXISTS ( + SELECT 1 + FROM workspace_agents AS existing_agent + INNER JOIN workspace_resources AS existing_resource + ON existing_agent.resource_id = existing_resource.id + INNER JOIN workspace_builds AS existing_build + ON existing_resource.job_id = existing_build.job_id + WHERE existing_agent.id = workspace_apps.agent_id + ) + OR EXISTS ( + SELECT 1 + FROM workspace_agents AS existing_agent + INNER JOIN workspace_resources AS existing_resource + ON existing_agent.resource_id = existing_resource.id + INNER JOIN workspace_builds AS existing_build + ON existing_resource.job_id = existing_build.job_id + INNER JOIN workspace_agents AS incoming_agent + ON incoming_agent.id = EXCLUDED.agent_id + INNER JOIN workspace_resources AS incoming_resource + ON incoming_agent.resource_id = incoming_resource.id + INNER JOIN workspace_builds AS incoming_build + ON incoming_resource.job_id = incoming_build.job_id + WHERE + existing_agent.id = workspace_apps.agent_id + AND existing_build.workspace_id = incoming_build.workspace_id + ) RETURNING id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order, hidden, open_in, display_group, tooltip ` diff --git a/coderd/database/queries/auditlogs.sql b/coderd/database/queries/auditlogs.sql index 63e8c721c8e..a0315235ccd 100644 --- a/coderd/database/queries/auditlogs.sql +++ b/coderd/database/queries/auditlogs.sql @@ -149,94 +149,105 @@ VALUES ( RETURNING *; -- name: CountAuditLogs :one -SELECT COUNT(*) -FROM audit_logs - LEFT JOIN users ON audit_logs.user_id = users.id - LEFT JOIN organizations ON audit_logs.organization_id = organizations.id - -- First join on workspaces to get the initial workspace create - -- to workspace build 1 id. This is because the first create is - -- is a different audit log than subsequent starts. - LEFT JOIN workspaces ON audit_logs.resource_type = 'workspace' - AND audit_logs.resource_id = workspaces.id - -- Get the reason from the build if the resource type - -- is a workspace_build - LEFT JOIN workspace_builds wb_build ON audit_logs.resource_type = 'workspace_build' - AND audit_logs.resource_id = wb_build.id - -- Get the reason from the build #1 if this is the first - -- workspace create. - LEFT JOIN workspace_builds wb_workspace ON audit_logs.resource_type = 'workspace' - AND audit_logs.action = 'create' - AND workspaces.id = wb_workspace.workspace_id - AND wb_workspace.build_number = 1 -WHERE - -- Filter resource_type - CASE - WHEN @resource_type::text != '' THEN resource_type = @resource_type::resource_type - ELSE true - END - -- Filter resource_id - AND CASE - WHEN @resource_id::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN resource_id = @resource_id - ELSE true - END - -- Filter organization_id - AND CASE - WHEN @organization_id::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN audit_logs.organization_id = @organization_id - ELSE true - END - -- Filter by resource_target - AND CASE - WHEN @resource_target::text != '' THEN resource_target = @resource_target - ELSE true - END - -- Filter action - AND CASE - WHEN @action::text != '' THEN action = @action::audit_action - ELSE true - END - -- Filter by user_id - AND CASE - WHEN @user_id::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN user_id = @user_id - ELSE true - END - -- Filter by username - AND CASE - WHEN @username::text != '' THEN user_id = ( - SELECT id - FROM users - WHERE lower(username) = lower(@username) - AND deleted = false - ) - ELSE true - END - -- Filter by user_email - AND CASE - WHEN @email::text != '' THEN users.email = @email - ELSE true - END - -- Filter by date_from - AND CASE - WHEN @date_from::timestamp with time zone != '0001-01-01 00:00:00Z' THEN "time" >= @date_from - ELSE true - END - -- Filter by date_to - AND CASE - WHEN @date_to::timestamp with time zone != '0001-01-01 00:00:00Z' THEN "time" <= @date_to - ELSE true - END - -- Filter by build_reason - AND CASE - WHEN @build_reason::text != '' THEN COALESCE(wb_build.reason::text, wb_workspace.reason::text) = @build_reason - ELSE true - END - -- Filter request_id - AND CASE - WHEN @request_id::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN audit_logs.request_id = @request_id - ELSE true - END - -- Authorize Filter clause will be injected below in CountAuthorizedAuditLogs - -- @authorize_filter -; +SELECT COUNT(*) FROM ( + SELECT 1 + FROM audit_logs + LEFT JOIN users ON audit_logs.user_id = users.id + LEFT JOIN organizations ON audit_logs.organization_id = organizations.id + -- First join on workspaces to get the initial workspace create + -- to workspace build 1 id. This is because the first create is + -- is a different audit log than subsequent starts. + LEFT JOIN workspaces ON audit_logs.resource_type = 'workspace' + AND audit_logs.resource_id = workspaces.id + -- Get the reason from the build if the resource type + -- is a workspace_build + LEFT JOIN workspace_builds wb_build ON audit_logs.resource_type = 'workspace_build' + AND audit_logs.resource_id = wb_build.id + -- Get the reason from the build #1 if this is the first + -- workspace create. + LEFT JOIN workspace_builds wb_workspace ON audit_logs.resource_type = 'workspace' + AND audit_logs.action = 'create' + AND workspaces.id = wb_workspace.workspace_id + AND wb_workspace.build_number = 1 + WHERE + -- Filter resource_type + CASE + WHEN @resource_type::text != '' THEN resource_type = @resource_type::resource_type + ELSE true + END + -- Filter resource_id + AND CASE + WHEN @resource_id::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN resource_id = @resource_id + ELSE true + END + -- Filter organization_id + AND CASE + WHEN @organization_id::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN audit_logs.organization_id = @organization_id + ELSE true + END + -- Filter by resource_target + AND CASE + WHEN @resource_target::text != '' THEN resource_target = @resource_target + ELSE true + END + -- Filter action + AND CASE + WHEN @action::text != '' THEN action = @action::audit_action + ELSE true + END + -- Filter by user_id + AND CASE + WHEN @user_id::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN user_id = @user_id + ELSE true + END + -- Filter by username + AND CASE + WHEN @username::text != '' THEN user_id = ( + SELECT id + FROM users + WHERE lower(username) = lower(@username) + AND deleted = false + ) + ELSE true + END + -- Filter by user_email + AND CASE + WHEN @email::text != '' THEN users.email = @email + ELSE true + END + -- Filter by date_from + AND CASE + WHEN @date_from::timestamp with time zone != '0001-01-01 00:00:00Z' THEN "time" >= @date_from + ELSE true + END + -- Filter by date_to + AND CASE + WHEN @date_to::timestamp with time zone != '0001-01-01 00:00:00Z' THEN "time" <= @date_to + ELSE true + END + -- Filter by build_reason + AND CASE + WHEN @build_reason::text != '' THEN COALESCE(wb_build.reason::text, wb_workspace.reason::text) = @build_reason + ELSE true + END + -- Filter request_id + AND CASE + WHEN @request_id::uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN audit_logs.request_id = @request_id + ELSE true + END + -- Authorize Filter clause will be injected below in CountAuthorizedAuditLogs + -- @authorize_filter + -- Avoid a slow scan on a large table with joins. The caller + -- passes the count cap and we add 1 so the frontend can detect + -- capping and show "... of N+". A cap of 0 means no limit (NULLIF + -- -> NULL + 1 = NULL). + -- NOTE: Parameterizing this so that we can easily change from, + -- e.g., 2000 to 5000. However, use literal NULL (or no LIMIT) + -- here if disabling the capping on a large table permanently. + -- This way the PG planner can plan parallel execution for + -- potential large wins. + LIMIT NULLIF(@count_cap::int, 0) + 1 +) AS limited_count; -- name: DeleteOldAuditLogConnectionEvents :exec DELETE FROM audit_logs diff --git a/coderd/database/queries/connectionlogs.sql b/coderd/database/queries/connectionlogs.sql index eb2d1b0cb17..4df92fdad42 100644 --- a/coderd/database/queries/connectionlogs.sql +++ b/coderd/database/queries/connectionlogs.sql @@ -133,111 +133,113 @@ OFFSET @offset_opt; -- name: CountConnectionLogs :one -SELECT - COUNT(*) AS count -FROM - connection_logs -JOIN users AS workspace_owner ON - connection_logs.workspace_owner_id = workspace_owner.id -LEFT JOIN users ON - connection_logs.user_id = users.id -JOIN organizations ON - connection_logs.organization_id = organizations.id -WHERE - -- Filter organization_id - CASE - WHEN @organization_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - connection_logs.organization_id = @organization_id - ELSE true - END - -- Filter by workspace owner username - AND CASE - WHEN @workspace_owner :: text != '' THEN - workspace_owner_id = ( - SELECT id FROM users - WHERE lower(username) = lower(@workspace_owner) AND deleted = false - ) - ELSE true - END - -- Filter by workspace_owner_id - AND CASE - WHEN @workspace_owner_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - workspace_owner_id = @workspace_owner_id - ELSE true - END - -- Filter by workspace_owner_email - AND CASE - WHEN @workspace_owner_email :: text != '' THEN - workspace_owner_id = ( - SELECT id FROM users - WHERE email = @workspace_owner_email AND deleted = false - ) - ELSE true - END - -- Filter by type - AND CASE - WHEN @type :: text != '' THEN - type = @type :: connection_type - ELSE true - END - -- Filter by user_id - AND CASE - WHEN @user_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - user_id = @user_id - ELSE true - END - -- Filter by username - AND CASE - WHEN @username :: text != '' THEN - user_id = ( - SELECT id FROM users - WHERE lower(username) = lower(@username) AND deleted = false - ) - ELSE true - END - -- Filter by user_email - AND CASE - WHEN @user_email :: text != '' THEN - users.email = @user_email - ELSE true - END - -- Filter by connected_after - AND CASE - WHEN @connected_after :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN - connect_time >= @connected_after - ELSE true - END - -- Filter by connected_before - AND CASE - WHEN @connected_before :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN - connect_time <= @connected_before - ELSE true - END - -- Filter by workspace_id - AND CASE - WHEN @workspace_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - connection_logs.workspace_id = @workspace_id - ELSE true - END - -- Filter by connection_id - AND CASE - WHEN @connection_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN - connection_logs.connection_id = @connection_id - ELSE true - END - -- Filter by whether the session has a disconnect_time - AND CASE - WHEN @status :: text != '' THEN - ((@status = 'ongoing' AND disconnect_time IS NULL) OR - (@status = 'completed' AND disconnect_time IS NOT NULL)) AND - -- Exclude web events, since we don't know their close time. - "type" NOT IN ('workspace_app', 'port_forwarding') - ELSE true - END - -- Authorize Filter clause will be injected below in - -- CountAuthorizedConnectionLogs - -- @authorize_filter -; +SELECT COUNT(*) AS count FROM ( + SELECT 1 + FROM + connection_logs + JOIN users AS workspace_owner ON + connection_logs.workspace_owner_id = workspace_owner.id + LEFT JOIN users ON + connection_logs.user_id = users.id + JOIN organizations ON + connection_logs.organization_id = organizations.id + WHERE + -- Filter organization_id + CASE + WHEN @organization_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + connection_logs.organization_id = @organization_id + ELSE true + END + -- Filter by workspace owner username + AND CASE + WHEN @workspace_owner :: text != '' THEN + workspace_owner_id = ( + SELECT id FROM users + WHERE lower(username) = lower(@workspace_owner) AND deleted = false + ) + ELSE true + END + -- Filter by workspace_owner_id + AND CASE + WHEN @workspace_owner_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + workspace_owner_id = @workspace_owner_id + ELSE true + END + -- Filter by workspace_owner_email + AND CASE + WHEN @workspace_owner_email :: text != '' THEN + workspace_owner_id = ( + SELECT id FROM users + WHERE email = @workspace_owner_email AND deleted = false + ) + ELSE true + END + -- Filter by type + AND CASE + WHEN @type :: text != '' THEN + type = @type :: connection_type + ELSE true + END + -- Filter by user_id + AND CASE + WHEN @user_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + user_id = @user_id + ELSE true + END + -- Filter by username + AND CASE + WHEN @username :: text != '' THEN + user_id = ( + SELECT id FROM users + WHERE lower(username) = lower(@username) AND deleted = false + ) + ELSE true + END + -- Filter by user_email + AND CASE + WHEN @user_email :: text != '' THEN + users.email = @user_email + ELSE true + END + -- Filter by connected_after + AND CASE + WHEN @connected_after :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN + connect_time >= @connected_after + ELSE true + END + -- Filter by connected_before + AND CASE + WHEN @connected_before :: timestamp with time zone != '0001-01-01 00:00:00Z' THEN + connect_time <= @connected_before + ELSE true + END + -- Filter by workspace_id + AND CASE + WHEN @workspace_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + connection_logs.workspace_id = @workspace_id + ELSE true + END + -- Filter by connection_id + AND CASE + WHEN @connection_id :: uuid != '00000000-0000-0000-0000-000000000000'::uuid THEN + connection_logs.connection_id = @connection_id + ELSE true + END + -- Filter by whether the session has a disconnect_time + AND CASE + WHEN @status :: text != '' THEN + ((@status = 'ongoing' AND disconnect_time IS NULL) OR + (@status = 'completed' AND disconnect_time IS NOT NULL)) AND + -- Exclude web events, since we don't know their close time. + "type" NOT IN ('workspace_app', 'port_forwarding') + ELSE true + END + -- Authorize Filter clause will be injected below in + -- CountAuthorizedConnectionLogs + -- @authorize_filter + -- NOTE: See the CountAuditLogs LIMIT note. + LIMIT NULLIF(@count_cap::int, 0) + 1 +) AS limited_count; -- name: UpsertConnectionLog :one INSERT INTO connection_logs ( diff --git a/coderd/database/queries/externalauth.sql b/coderd/database/queries/externalauth.sql index 9ca5cf6f871..e5d0ec548bf 100644 --- a/coderd/database/queries/externalauth.sql +++ b/coderd/database/queries/externalauth.sql @@ -48,6 +48,10 @@ UPDATE external_auth_links SET WHERE provider_id = $1 AND user_id = $2 RETURNING *; -- name: UpdateExternalAuthLinkRefreshToken :exec +-- Optimistic lock: only update the row if the refresh token in the database +-- still matches the one we read before attempting the refresh. This prevents +-- a concurrent caller that lost a token-refresh race from overwriting a valid +-- token stored by the winner. UPDATE external_auth_links SET @@ -60,6 +64,8 @@ WHERE provider_id = @provider_id AND user_id = @user_id +AND + oauth_refresh_token = @old_oauth_refresh_token AND -- Required for sqlc to generate a parameter for the oauth_refresh_token_key_id @oauth_refresh_token_key_id :: text = @oauth_refresh_token_key_id :: text; diff --git a/coderd/database/queries/proxies.sql b/coderd/database/queries/proxies.sql index df59d3baf10..cac44de84d4 100644 --- a/coderd/database/queries/proxies.sql +++ b/coderd/database/queries/proxies.sql @@ -120,7 +120,7 @@ WHERE ( ( @allow_access_url :: bool = true AND - url SIMILAR TO '[^:]*://' || @hostname :: text || '([:/]?%)*' + url SIMILAR TO '[^:]*://' || @hostname :: text || '([:/]%)*' ) OR ( @allow_wildcard_hostname :: bool = true AND diff --git a/coderd/database/queries/user_links.sql b/coderd/database/queries/user_links.sql index 43e7fad64e7..fb7567e4ff3 100644 --- a/coderd/database/queries/user_links.sql +++ b/coderd/database/queries/user_links.sql @@ -37,14 +37,6 @@ INSERT INTO VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9 ) RETURNING *; --- name: UpdateUserLinkedID :one -UPDATE - user_links -SET - linked_id = $1 -WHERE - user_id = $2 AND login_type = $3 RETURNING *; - -- name: UpdateUserLink :one UPDATE user_links @@ -58,6 +50,17 @@ SET WHERE user_id = $7 AND login_type = $8 RETURNING *; +-- name: UpdateUserLinkedID :one +-- Backfills linked_id for legacy user_links that were created before +-- linked_id tracking was added. Only updates when linked_id is empty +-- to avoid overwriting a valid binding. +UPDATE + user_links +SET + linked_id = @linked_id +WHERE + user_id = @user_id AND login_type = @login_type AND linked_id = '' RETURNING *; + -- name: OIDCClaimFields :many -- OIDCClaimFields returns a list of distinct keys in the the merged_claims fields. -- This query is used to generate the list of available sync fields for idp sync settings. @@ -110,3 +113,36 @@ WHERE ELSE true END ; + +-- name: CountOIDCLinkedIDsByIssuer :many +-- Groups OIDC user links by their issuer prefix (the part before "||" in +-- linked_id) and returns a count for each. Empty linked_ids are reported +-- with an empty issuer_prefix. Used for analysis before resetting +-- mismatched links. +SELECT + (CASE + WHEN user_links.linked_id = '' THEN '' + ELSE split_part(user_links.linked_id, '||', 1) + END)::text AS issuer_prefix, + COUNT(*)::int AS count +FROM + user_links +INNER JOIN + users ON user_links.user_id = users.id +WHERE + user_links.login_type = 'oidc' + AND users.deleted = false +GROUP BY issuer_prefix; + +-- name: UnlinkOIDCUsersByIssuerMismatch :execrows +-- Resets linked_id to '' for OIDC links where the linked_id is non-empty +-- and does not begin with the expected issuer prefix. This allows users to +-- re-authenticate under a new OIDC provider. +UPDATE user_links +SET linked_id = '' +FROM users +WHERE user_links.user_id = users.id + AND user_links.login_type = 'oidc' + AND user_links.linked_id != '' + AND NOT starts_with(user_links.linked_id, @expected_prefix) + AND users.deleted = false; diff --git a/coderd/database/queries/users.sql b/coderd/database/queries/users.sql index 0b6e52d6bc9..889e99a3300 100644 --- a/coderd/database/queries/users.sql +++ b/coderd/database/queries/users.sql @@ -168,6 +168,29 @@ WHERE user_configs.user_id = @user_id AND user_configs.key = 'terminal_font' RETURNING *; +-- name: GetUserTaskNotificationAlertDismissed :one +SELECT + value::boolean as task_notification_alert_dismissed +FROM + user_configs +WHERE + user_id = @user_id + AND key = 'preference_task_notification_alert_dismissed'; + +-- name: UpdateUserTaskNotificationAlertDismissed :one +INSERT INTO + user_configs (user_id, key, value) +VALUES + (@user_id, 'preference_task_notification_alert_dismissed', (@task_notification_alert_dismissed::boolean)::text) +ON CONFLICT + ON CONSTRAINT user_configs_pkey +DO UPDATE +SET + value = @task_notification_alert_dismissed +WHERE user_configs.user_id = @user_id + AND user_configs.key = 'preference_task_notification_alert_dismissed' +RETURNING value::boolean AS task_notification_alert_dismissed; + -- name: UpdateUserRoles :one UPDATE users diff --git a/coderd/database/queries/workspaceagents.sql b/coderd/database/queries/workspaceagents.sql index b60d1f2c884..5181995d020 100644 --- a/coderd/database/queries/workspaceagents.sql +++ b/coderd/database/queries/workspaceagents.sql @@ -17,6 +17,8 @@ WHERE auth_instance_id = @auth_instance_id :: TEXT -- Filter out deleted sub agents. AND deleted = FALSE + -- Filter out sub agents, they do not authenticate with auth_instance_id. + AND parent_id IS NULL ORDER BY created_at DESC; diff --git a/coderd/database/queries/workspaceapps.sql b/coderd/database/queries/workspaceapps.sql index b3694fb2750..6bcd0678fe3 100644 --- a/coderd/database/queries/workspaceapps.sql +++ b/coderd/database/queries/workspaceapps.sql @@ -55,6 +55,42 @@ ON CONFLICT (id) DO UPDATE SET agent_id = EXCLUDED.agent_id, slug = EXCLUDED.slug, tooltip = EXCLUDED.tooltip +WHERE + -- Prevent cross-tenant/cross-workspace agent rebinding (SEC-91). + -- App IDs persist across builds of the same workspace, but agent IDs are + -- regenerated every build, so compare by the workspace that owns the agent + -- rather than by agent_id. Permit unowned apps to be claimed and permit + -- same-workspace rebuilds. If an existing app belongs to a workspace, block + -- moves to both different workspaces and template import or dry-run agents + -- that resolve to no workspace. The conflicting row is then left untouched, + -- and the :one query returns no row, which the caller treats as a + -- rejection. + NOT EXISTS ( + SELECT 1 + FROM workspace_agents AS existing_agent + INNER JOIN workspace_resources AS existing_resource + ON existing_agent.resource_id = existing_resource.id + INNER JOIN workspace_builds AS existing_build + ON existing_resource.job_id = existing_build.job_id + WHERE existing_agent.id = workspace_apps.agent_id + ) + OR EXISTS ( + SELECT 1 + FROM workspace_agents AS existing_agent + INNER JOIN workspace_resources AS existing_resource + ON existing_agent.resource_id = existing_resource.id + INNER JOIN workspace_builds AS existing_build + ON existing_resource.job_id = existing_build.job_id + INNER JOIN workspace_agents AS incoming_agent + ON incoming_agent.id = EXCLUDED.agent_id + INNER JOIN workspace_resources AS incoming_resource + ON incoming_agent.resource_id = incoming_resource.id + INNER JOIN workspace_builds AS incoming_build + ON incoming_resource.job_id = incoming_build.job_id + WHERE + existing_agent.id = workspace_apps.agent_id + AND existing_build.workspace_id = incoming_build.workspace_id + ) RETURNING *; -- name: UpdateWorkspaceAppHealthByID :exec diff --git a/coderd/externalauth/externalauth.go b/coderd/externalauth/externalauth.go index f33a9d36700..1d641acadb5 100644 --- a/coderd/externalauth/externalauth.go +++ b/coderd/externalauth/externalauth.go @@ -138,8 +138,6 @@ func IsInvalidTokenError(err error) bool { } // RefreshToken automatically refreshes the token if expired and permitted. -// If an error is returned, the token is either invalid, or an error occurred. -// Use 'IsInvalidTokenError(err)' to determine the difference. func (c *Config) RefreshToken(ctx context.Context, db database.Store, externalAuthLink database.ExternalAuthLink) (database.ExternalAuthLink, error) { // If the token is expired and refresh is disabled, we prompt // the user to authenticate again. @@ -195,6 +193,9 @@ func (c *Config) RefreshToken(ctx context.Context, db database.Store, externalAu UpdatedAt: dbtime.Now(), ProviderID: externalAuthLink.ProviderID, UserID: externalAuthLink.UserID, + // Optimistic lock: only clear the token if it hasn't been + // updated by a concurrent caller that won the refresh race. + OldOauthRefreshToken: externalAuthLink.OAuthRefreshToken, }) if dbExecErr != nil { // This error should be rare. @@ -237,6 +238,37 @@ func (c *Config) RefreshToken(ctx context.Context, db database.Store, externalAu return externalAuthLink, xerrors.Errorf("generate token extra: %w", err) } + // Persist the refreshed token to the DB before validation. GitHub + // rotates refresh tokens on every use, so the old refresh token is + // already invalid on the IDP side. If we validated first and the + // validation endpoint was unavailable (e.g. rate-limited 403), the + // new token would be silently lost and the user would be forced to + // re-authenticate manually. + // Use a detached context for the DB write only. The IDP already + // consumed the old refresh token, so if the caller's request + // context is canceled mid-save, the new token would be lost. + persistCtx, persistCancel := context.WithTimeout(context.WithoutCancel(ctx), 10*time.Second) + defer persistCancel() + + originalAccessToken := externalAuthLink.OAuthAccessToken + if token.AccessToken != originalAccessToken { + updatedAuthLink, err := db.UpdateExternalAuthLink(persistCtx, database.UpdateExternalAuthLinkParams{ + ProviderID: c.ID, + UserID: externalAuthLink.UserID, + UpdatedAt: dbtime.Now(), + OAuthAccessToken: token.AccessToken, + OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required + OAuthRefreshToken: token.RefreshToken, + OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required + OAuthExpiry: token.Expiry, + OAuthExtra: extra, + }) + if err != nil { + return updatedAuthLink, xerrors.Errorf("persist refreshed token: %w", err) + } + externalAuthLink = updatedAuthLink + } + r := retry.New(50*time.Millisecond, 200*time.Millisecond) // See the comment below why the retry and cancel is required. retryCtx, retryCtxCancel := context.WithTimeout(ctx, time.Second) @@ -261,35 +293,18 @@ validate: return externalAuthLink, InvalidTokenError("token failed to validate") } - if token.AccessToken != externalAuthLink.OAuthAccessToken { - updatedAuthLink, err := db.UpdateExternalAuthLink(ctx, database.UpdateExternalAuthLinkParams{ - ProviderID: c.ID, - UserID: externalAuthLink.UserID, - UpdatedAt: dbtime.Now(), - OAuthAccessToken: token.AccessToken, - OAuthAccessTokenKeyID: sql.NullString{}, // dbcrypt will update as required - OAuthRefreshToken: token.RefreshToken, - OAuthRefreshTokenKeyID: sql.NullString{}, // dbcrypt will update as required - OAuthExpiry: token.Expiry, - OAuthExtra: extra, + // Update the associated user's github.com user ID if the token + // is for github.com and validation returned user info. + if token.AccessToken != originalAccessToken && IsGithubDotComURL(c.AuthCodeURL("")) && user != nil { + err = db.UpdateUserGithubComUserID(ctx, database.UpdateUserGithubComUserIDParams{ + ID: externalAuthLink.UserID, + GithubComUserID: sql.NullInt64{ + Int64: user.ID, + Valid: true, + }, }) if err != nil { - return updatedAuthLink, xerrors.Errorf("update external auth link: %w", err) - } - externalAuthLink = updatedAuthLink - - // Update the associated users github.com username if the token is for github.com. - if IsGithubDotComURL(c.AuthCodeURL("")) && user != nil { - err = db.UpdateUserGithubComUserID(ctx, database.UpdateUserGithubComUserIDParams{ - ID: externalAuthLink.UserID, - GithubComUserID: sql.NullInt64{ - Int64: user.ID, - Valid: true, - }, - }) - if err != nil { - return externalAuthLink, xerrors.Errorf("update user github com user id: %w", err) - } + return externalAuthLink, xerrors.Errorf("update user github com user id: %w", err) } } diff --git a/coderd/externalauth/externalauth_test.go b/coderd/externalauth/externalauth_test.go index 670d1cbf112..e48c7cfbc7a 100644 --- a/coderd/externalauth/externalauth_test.go +++ b/coderd/externalauth/externalauth_test.go @@ -8,6 +8,7 @@ import ( "net/http/httptest" "net/url" "strings" + "sync/atomic" "testing" "time" @@ -27,6 +28,7 @@ import ( "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbmock" "github.com/coder/coder/v2/coderd/database/dbtestutil" + "github.com/coder/coder/v2/coderd/database/dbtime" "github.com/coder/coder/v2/coderd/externalauth" "github.com/coder/coder/v2/coderd/promoauth" "github.com/coder/coder/v2/codersdk" @@ -93,6 +95,7 @@ func TestRefreshToken(t *testing.T) { // Zero time used link.OAuthExpiry = time.Time{} + _, err := config.RefreshToken(ctx, nil, link) require.NoError(t, err) require.True(t, validated, "token should have been validated") @@ -107,6 +110,7 @@ func TestRefreshToken(t *testing.T) { }, }, } + _, err := config.RefreshToken(context.Background(), nil, database.ExternalAuthLink{ OAuthExpiry: expired, }) @@ -118,6 +122,11 @@ func TestRefreshToken(t *testing.T) { t.Run("ValidateServerError", func(t *testing.T) { t.Parallel() + ctrl := gomock.NewController(t) + mDB := dbmock.NewMockStore(ctrl) + mDB.EXPECT().UpdateExternalAuthLink(gomock.Any(), gomock.Any()). + Return(database.ExternalAuthLink{}, nil).AnyTimes() + const staticError = "static error" validated := false fake, config, link := setupOauth2Test(t, testConfig{ @@ -134,7 +143,7 @@ func TestRefreshToken(t *testing.T) { ctx := oidc.ClientContext(context.Background(), fake.HTTPClient(nil)) link.OAuthExpiry = expired - _, err := config.RefreshToken(ctx, nil, link) + _, err := config.RefreshToken(ctx, mDB, link) require.ErrorContains(t, err, staticError) // Unsure if this should be the correct behavior. It's an invalid token because // 'ValidateToken()' failed with a runtime error. This was the previous behavior, @@ -221,6 +230,11 @@ func TestRefreshToken(t *testing.T) { t.Run("ValidateFailure", func(t *testing.T) { t.Parallel() + ctrl := gomock.NewController(t) + mDB := dbmock.NewMockStore(ctrl) + mDB.EXPECT().UpdateExternalAuthLink(gomock.Any(), gomock.Any()). + Return(database.ExternalAuthLink{}, nil).AnyTimes() + const staticError = "static error" validated := false fake, config, link := setupOauth2Test(t, testConfig{ @@ -237,7 +251,7 @@ func TestRefreshToken(t *testing.T) { ctx := oidc.ClientContext(context.Background(), fake.HTTPClient(nil)) link.OAuthExpiry = expired - _, err := config.RefreshToken(ctx, nil, link) + _, err := config.RefreshToken(ctx, mDB, link) require.ErrorContains(t, err, "token failed to validate") require.True(t, externalauth.IsInvalidTokenError(err)) require.True(t, validated, "token should have been attempted to be validated") @@ -344,7 +358,6 @@ func TestRefreshToken(t *testing.T) { require.NoError(t, err) require.Equal(t, updated.OAuthAccessToken, dbLink.OAuthAccessToken, "token is updated in the DB") }) - t.Run("WithExtra", func(t *testing.T) { t.Parallel() @@ -379,6 +392,235 @@ func TestRefreshToken(t *testing.T) { require.True(t, ok) require.Equal(t, updated.OAuthAccessToken, mapping["access_token"]) }) + + // SaveBeforeValidate tests that a successfully refreshed token is + // persisted to the DB even when post-refresh validation fails. This + // prevents the data-loss scenario where GitHub rotates the refresh + // token on use but the new token is silently discarded because a + // rate-limited validation endpoint returns 403. + t.Run("SaveBeforeValidate", func(t *testing.T) { + t.Parallel() + + db, _ := dbtestutil.NewDB(t) + + // simulateRateLimit controls whether the validate endpoint + // returns 403 (true) or 200 (false). + var simulateRateLimit atomic.Bool + simulateRateLimit.Store(true) + + var refreshCalls atomic.Int64 + fake, config, link := setupOauth2Test(t, testConfig{ + FakeIDPOpts: []oidctest.FakeIDPOpt{ + oidctest.WithRefresh(func(_ string) error { + refreshCalls.Add(1) + return nil + }), + oidctest.WithDynamicUserInfo(func(_ string) (jwt.MapClaims, error) { + if simulateRateLimit.Load() { + return jwt.MapClaims{}, oidctest.StatusError(http.StatusForbidden, xerrors.New("rate limit exceeded")) + } + return jwt.MapClaims{}, nil + }), + }, + ExternalAuthOpt: func(cfg *externalauth.Config) { + cfg.Type = codersdk.EnhancedExternalAuthProviderGitHub.String() + }, + DB: db, + }) + + ctx := oidc.ClientContext(context.Background(), fake.HTTPClient(nil)) + + oldAccessToken := link.OAuthAccessToken + oldRefreshToken := link.OAuthRefreshToken + + // Expire the token to force a refresh. + link.OAuthExpiry = expired + + // First call: refresh succeeds, validation fails (403). + _, err := config.RefreshToken(ctx, db, link) + require.Error(t, err, "expected error because validation returned 403") + require.True(t, externalauth.IsInvalidTokenError(err)) + require.Equal(t, int64(1), refreshCalls.Load(), "IDP refresh should have been called exactly once") + + // Critical assertion: the DB must contain the NEW tokens from the + // successful refresh, not the old (now-stale) ones. + dbLink, err := db.GetExternalAuthLink(context.Background(), database.GetExternalAuthLinkParams{ + ProviderID: link.ProviderID, + UserID: link.UserID, + }) + require.NoError(t, err) + require.NotEqual(t, oldAccessToken, dbLink.OAuthAccessToken, + "DB should have the new access token from the successful refresh") + require.NotEqual(t, oldRefreshToken, dbLink.OAuthRefreshToken, + "DB should have the new refresh token (old one was rotated by the IDP)") + + // Second call: uses the saved token from DB, no re-refresh. + // The saved token has a future expiry, so TokenSource should return + // it without contacting the IDP. Validation should succeed now. + simulateRateLimit.Store(false) + updated, err := config.RefreshToken(ctx, db, dbLink) + require.NoError(t, err, "second call should succeed because rate limit lifted") + require.Equal(t, int64(1), refreshCalls.Load(), + "IDP refresh should NOT have been called again; the saved token is not expired") + require.Equal(t, dbLink.OAuthAccessToken, updated.OAuthAccessToken, + "returned token should match what was saved in the DB") + }) + + // SaveBeforeValidate_ContextCanceled verifies the early DB save + // uses a detached context. The parent context is canceled inside + // the refresh hook (after TokenSource.Token() but before the DB + // write), and the test asserts the new token is still persisted. + t.Run("SaveBeforeValidate_ContextCanceled", func(t *testing.T) { + t.Parallel() + + db, _ := dbtestutil.NewDB(t) + + var refreshCalls atomic.Int64 + cancelOnRefresh, cancel := context.WithCancel(context.Background()) + defer cancel() + + fake, config, link := setupOauth2Test(t, testConfig{ + FakeIDPOpts: []oidctest.FakeIDPOpt{ + oidctest.WithRefresh(func(_ string) error { + refreshCalls.Add(1) + // Cancel the parent context after refresh succeeds + // but before the DB save and validation. + cancel() + return nil + }), + oidctest.WithDynamicUserInfo(func(_ string) (jwt.MapClaims, error) { + return jwt.MapClaims{}, nil + }), + }, + ExternalAuthOpt: func(cfg *externalauth.Config) { + cfg.Type = codersdk.EnhancedExternalAuthProviderGitHub.String() + }, + DB: db, + }) + + ctx := oidc.ClientContext(cancelOnRefresh, fake.HTTPClient(nil)) + + oldAccessToken := link.OAuthAccessToken + oldRefreshToken := link.OAuthRefreshToken + link.OAuthExpiry = expired + + _, err := config.RefreshToken(ctx, db, link) + require.NoError(t, err) + require.Equal(t, int64(1), refreshCalls.Load()) + + dbLink, err := db.GetExternalAuthLink(context.Background(), database.GetExternalAuthLinkParams{ + ProviderID: link.ProviderID, + UserID: link.UserID, + }) + require.NoError(t, err) + require.NotEqual(t, oldAccessToken, dbLink.OAuthAccessToken, + "DB should have the new access token despite context cancellation") + require.NotEqual(t, oldRefreshToken, dbLink.OAuthRefreshToken, + "DB should have the new refresh token despite context cancellation") + }) + + // SaveBeforeValidate_DBError tests that when the early DB save + // fails after a successful IDP refresh, the error is surfaced + // as a non-InvalidTokenError. This is a degraded state (token + // issued by IDP but not persisted), and callers should see a + // real error, not a "please re-authenticate" prompt. + t.Run("SaveBeforeValidate_DBError", func(t *testing.T) { + t.Parallel() + + ctrl := gomock.NewController(t) + mDB := dbmock.NewMockStore(ctrl) + + fake, config, link := setupOauth2Test(t, testConfig{ + FakeIDPOpts: []oidctest.FakeIDPOpt{ + oidctest.WithRefresh(func(_ string) error { + return nil + }), + }, + ExternalAuthOpt: func(cfg *externalauth.Config) { + cfg.Type = codersdk.EnhancedExternalAuthProviderGitHub.String() + }, + }) + + ctx := oidc.ClientContext(context.Background(), fake.HTTPClient(nil)) + link.OAuthExpiry = expired + + mDB.EXPECT(). + UpdateExternalAuthLink(gomock.Any(), gomock.Any()). + Return(database.ExternalAuthLink{}, xerrors.New("db connection lost")) + + _, err := config.RefreshToken(ctx, mDB, link) + require.Error(t, err) + require.Contains(t, err.Error(), "persist refreshed token") + require.False(t, externalauth.IsInvalidTokenError(err), + "DB errors should not be treated as invalid token") + }) + + // OptimisticLockPreventsStaleOverwrite verifies that the + // UpdateExternalAuthLinkRefreshToken WHERE clause prevents a + // stale caller from overwriting a valid refresh token saved + // by a concurrent winner. + t.Run("OptimisticLockPreventsStaleOverwrite", func(t *testing.T) { + t.Parallel() + + db, _ := dbtestutil.NewDB(t) + + fake, config, link := setupOauth2Test(t, testConfig{ + FakeIDPOpts: []oidctest.FakeIDPOpt{ + oidctest.WithRefresh(func(_ string) error { + return nil + }), + oidctest.WithDynamicUserInfo(func(_ string) (jwt.MapClaims, error) { + return jwt.MapClaims{}, nil + }), + }, + ExternalAuthOpt: func(cfg *externalauth.Config) { + cfg.Type = codersdk.EnhancedExternalAuthProviderGitHub.String() + }, + DB: db, + }) + + ctx := oidc.ClientContext(context.Background(), fake.HTTPClient(nil)) + + // Snapshot the original tokens before any refresh. + oldRefreshToken := link.OAuthRefreshToken + + // Expire the token to force a refresh. + link.OAuthExpiry = expired + + // Caller A: refresh and save successfully. + updated, err := config.RefreshToken(ctx, db, link) + require.NoError(t, err) + require.NotEqual(t, oldRefreshToken, updated.OAuthRefreshToken, + "caller A should have a new refresh token") + + // Caller B had a stale read of the original link. It tries to + // destroy the refresh token using the OLD refresh token in the + // optimistic lock. Because caller A already wrote a different + // refresh token, this WHERE clause matches nothing. + err = db.UpdateExternalAuthLinkRefreshToken(ctx, database.UpdateExternalAuthLinkRefreshTokenParams{ + OauthRefreshFailureReason: "simulated failure from stale caller B", + OAuthRefreshToken: "", + OAuthRefreshTokenKeyID: "", + UpdatedAt: dbtime.Now(), + ProviderID: link.ProviderID, + UserID: link.UserID, + OldOauthRefreshToken: oldRefreshToken, + }) + require.NoError(t, err, "optimistic lock write should not error, it is a no-op") + + // Verify DB still has caller A's valid token. + dbLink, err := db.GetExternalAuthLink(context.Background(), database.GetExternalAuthLinkParams{ + ProviderID: link.ProviderID, + UserID: link.UserID, + }) + require.NoError(t, err) + require.Equal(t, updated.OAuthAccessToken, dbLink.OAuthAccessToken, + "caller A's access token should still be in DB") + require.Equal(t, updated.OAuthRefreshToken, dbLink.OAuthRefreshToken, + "caller A's refresh token should still be in DB") + require.Empty(t, dbLink.OauthRefreshFailureReason, + "caller B's failure reason should not have been written") + }) } func TestRevokeToken(t *testing.T) { diff --git a/coderd/files.go b/coderd/files.go index eaab00c4014..fd867d38a21 100644 --- a/coderd/files.go +++ b/coderd/files.go @@ -79,11 +79,24 @@ func (api *API) postFile(rw http.ResponseWriter, r *http.Request) { data, err = archive.CreateTarFromZip(zipReader, HTTPFileMaxBytes) if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error processing .zip archive.", - Detail: err.Error(), - }) - return + switch { + case errors.Is(err, archive.ErrArchiveTooLarge): + httpapi.Write(ctx, rw, http.StatusRequestEntityTooLarge, codersdk.Response{ + Message: "Expanded .zip archive exceeds maximum size.", + }) + return + case errors.Is(err, archive.ErrInvalidZipContent): + httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ + Message: "Invalid .zip archive contents.", + }) + return + default: + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Internal error processing .zip archive.", + Detail: err.Error(), + }) + return + } } contentType = tarMimeType } diff --git a/coderd/files_test.go b/coderd/files_test.go index b7f981d5e5c..8dc1891087d 100644 --- a/coderd/files_test.go +++ b/coderd/files_test.go @@ -2,8 +2,11 @@ package coderd_test import ( "archive/tar" + "archive/zip" "bytes" "context" + "encoding/binary" + "io" "net/http" "sync" "testing" @@ -14,6 +17,7 @@ import ( "github.com/coder/coder/v2/archive" "github.com/coder/coder/v2/archive/archivetest" + "github.com/coder/coder/v2/coderd" "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/testutil" @@ -21,6 +25,19 @@ import ( func TestPostFiles(t *testing.T) { t.Parallel() + buildZipWithFile := func(t *testing.T, name string, writeContents func(w io.Writer) error) []byte { + t.Helper() + + var zipBytes bytes.Buffer + zw := zip.NewWriter(&zipBytes) + w, err := zw.Create(name) + require.NoError(t, err) + require.NoError(t, writeContents(w)) + require.NoError(t, zw.Close()) + + return zipBytes.Bytes() + } + t.Run("BadContentType", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, nil) @@ -71,6 +88,41 @@ func TestPostFiles(t *testing.T) { _, err = client.Upload(ctx, codersdk.ContentTypeTar, bytes.NewReader(data)) require.NoError(t, err) }) + t.Run("InvalidZipMetadata", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + _ = coderdtest.CreateFirstUser(t, client) + + corruptZipUncompressedSize := func(t *testing.T, zipBytes []byte, size uint32) []byte { + t.Helper() + + const ( + directoryHeaderSignature = "PK\x01\x02" + uncompressedSizeOffset = 24 + ) + hdrOffset := bytes.Index(zipBytes, []byte(directoryHeaderSignature)) + require.NotEqual(t, -1, hdrOffset, "missing ZIP central directory header") + corrupted := bytes.Clone(zipBytes) + sizeBytes := corrupted[hdrOffset+uncompressedSizeOffset : hdrOffset+uncompressedSizeOffset+4] + binary.LittleEndian.PutUint32(sizeBytes, size) + + return corrupted + } + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + zipBytes := buildZipWithFile(t, "hello.txt", func(w io.Writer) error { + _, err := w.Write([]byte("hello")) + return err + }) + zipBytes = corruptZipUncompressedSize(t, zipBytes, 6) + + _, err := client.Upload(ctx, codersdk.ContentTypeZip, bytes.NewReader(zipBytes)) + var apiErr *codersdk.Error + require.ErrorAs(t, err, &apiErr) + require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) + }) t.Run("InsertConcurrent", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, nil) @@ -95,6 +147,46 @@ func TestPostFiles(t *testing.T) { wg.Done() end.Wait() }) + //nolint:paralleltest // This subtest is intentionally serial to + // avoid extra memory pressure. + t.Run("OversizedZipExpansion", func(t *testing.T) { + client := coderdtest.New(t, nil) + _ = coderdtest.CreateFirstUser(t, client) + + buildZipWithSizedFile := func(t *testing.T, name string, size int64) []byte { + return buildZipWithFile(t, name, func(w io.Writer) error { + chunk := bytes.Repeat([]byte("a"), 32*1024) + for written := int64(0); written < size; { + n := len(chunk) + if remaining := size - written; int64(n) > remaining { + n = int(remaining) + } + + _, err := w.Write(chunk[:n]) + if err != nil { + return err + } + written += int64(n) + } + + return nil + }) + } + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + // Leave only enough room for the tar trailer. The single + // entry header then pushes the converted tar output over the + // file size limit. + size := int64(coderd.HTTPFileMaxBytes - 1024) + zipBytes := buildZipWithSizedFile(t, "oversized.txt", size) + + _, err := client.Upload(ctx, codersdk.ContentTypeZip, bytes.NewReader(zipBytes)) + var apiErr *codersdk.Error + require.ErrorAs(t, err, &apiErr) + require.Equal(t, http.StatusRequestEntityTooLarge, apiErr.StatusCode()) + }) } func TestDownload(t *testing.T) { diff --git a/coderd/httpapi/request.go b/coderd/httpapi/request.go index 6a07ede6dce..95d786d2417 100644 --- a/coderd/httpapi/request.go +++ b/coderd/httpapi/request.go @@ -8,17 +8,6 @@ const ( XForwardedHostHeader = "X-Forwarded-Host" ) -// RequestHost returns the name of the host from the request. It prioritizes -// 'X-Forwarded-Host' over r.Host since most requests are being proxied. -func RequestHost(r *http.Request) string { - host := r.Header.Get(XForwardedHostHeader) - if host != "" { - return host - } - - return r.Host -} - func IsWebsocketUpgrade(r *http.Request) bool { vs := r.Header.Values("Upgrade") for _, v := range vs { diff --git a/coderd/httpmw/apikey.go b/coderd/httpmw/apikey.go index 29296fea59f..e02e9d259ea 100644 --- a/coderd/httpmw/apikey.go +++ b/coderd/httpmw/apikey.go @@ -390,7 +390,11 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon } // Only update the ExpiresAt once an hour to prevent database spam. // We extend the ExpiresAt to reduce re-authentication. - if !cfg.DisableSessionExpiryRefresh { + // Only apply sliding-window expiry refresh to interactive login + // sessions. Programmatic API tokens (LoginTypeToken, created via + // `coder tokens create`) honor a fixed, finite lifetime and must not be + // silently extended to now+lifetime on each authenticated request. + if !cfg.DisableSessionExpiryRefresh && key.LoginType != database.LoginTypeToken { apiKeyLifetime := time.Duration(key.LifetimeSeconds) * time.Second if key.ExpiresAt.Sub(now) <= apiKeyLifetime-time.Hour { key.ExpiresAt = now.Add(apiKeyLifetime) diff --git a/coderd/httpmw/apikey_test.go b/coderd/httpmw/apikey_test.go index 020dc28e601..457dd2d9fe7 100644 --- a/coderd/httpmw/apikey_test.go +++ b/coderd/httpmw/apikey_test.go @@ -440,6 +440,39 @@ func TestAPIKey(t *testing.T) { require.NotEqual(t, sentAPIKey.ExpiresAt, gotAPIKey.ExpiresAt) }) + t.Run("TokenNoExpiryRefresh", func(t *testing.T) { + t.Parallel() + var ( + db, _ = dbtestutil.NewDB(t) + user = dbgen.User(t, db, database.User{}) + sentAPIKey, token = dbgen.APIKey(t, db, database.APIKey{ + UserID: user.ID, + LastUsed: dbtime.Now(), + ExpiresAt: dbtime.Now().Add(time.Minute), + LoginType: database.LoginTypeToken, + }) + + r = httptest.NewRequest("GET", "/", nil) + rw = httptest.NewRecorder() + ) + r.Header.Set(codersdk.SessionTokenHeader, token) + + httpmw.ExtractAPIKeyMW(httpmw.ExtractAPIKeyConfig{ + DB: db, + RedirectToLogin: false, + })(successHandler).ServeHTTP(rw, r) + res := rw.Result() + defer res.Body.Close() + require.Equal(t, http.StatusOK, res.StatusCode) + + gotAPIKey, err := db.GetAPIKeyByID(r.Context(), sentAPIKey.ID) + require.NoError(t, err) + + // Programmatic tokens honor a fixed lifetime, so the expiry must not be + // extended on use even though it is within the refresh window. + require.Equal(t, sentAPIKey.ExpiresAt, gotAPIKey.ExpiresAt) + }) + t.Run("NoRefresh", func(t *testing.T) { t.Parallel() var ( diff --git a/coderd/httpmw/csp.go b/coderd/httpmw/csp.go index f39781ad51b..1395d9ccdb7 100644 --- a/coderd/httpmw/csp.go +++ b/coderd/httpmw/csp.go @@ -142,6 +142,22 @@ func CSPHeaders(telemetry bool, proxyHosts func() []*proxyhealth.ProxyHost, stat cspSrcs.Append(directive, values...) } + // Default to 'self' to prevent clickjacking unless + // explicitly overridden via staticAdditions (e.g. for + // embeddable routes). + // + // An explicit empty value means "omit frame-ancestors + // entirely", which is needed for embed routes where + // non-network-scheme parents (e.g. vscode-webview://) + // must be able to frame the page. The CSP wildcard '*' + // only matches network schemes (http, https, ws, wss) + // so it cannot cover custom schemes. + if vals, ok := cspSrcs[CSPFrameAncestors]; !ok { + cspSrcs[CSPFrameAncestors] = []string{"'self'"} + } else if len(vals) == 0 { + delete(cspSrcs, CSPFrameAncestors) + } + var csp strings.Builder for src, vals := range cspSrcs { _, _ = fmt.Fprintf(&csp, "%s %s; ", src, strings.Join(vals, " ")) diff --git a/coderd/httpmw/csp_test.go b/coderd/httpmw/csp_test.go index ba88320e6fa..105abd0df18 100644 --- a/coderd/httpmw/csp_test.go +++ b/coderd/httpmw/csp_test.go @@ -12,6 +12,63 @@ import ( "github.com/coder/coder/v2/coderd/proxyhealth" ) +func TestCSPFrameAncestors(t *testing.T) { + t.Parallel() + + t.Run("DefaultSelf", func(t *testing.T) { + t.Parallel() + + r := httptest.NewRequest(http.MethodGet, "/", nil) + rw := httptest.NewRecorder() + + httpmw.CSPHeaders(false, func() []*proxyhealth.ProxyHost { + return nil + }, nil)(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.WriteHeader(http.StatusOK) + })).ServeHTTP(rw, r) + + csp := rw.Header().Get("Content-Security-Policy") + require.Contains(t, csp, "frame-ancestors 'self'") + }) + + t.Run("OverrideViaStaticAdditions", func(t *testing.T) { + t.Parallel() + + r := httptest.NewRequest(http.MethodGet, "/", nil) + rw := httptest.NewRecorder() + + httpmw.CSPHeaders(false, func() []*proxyhealth.ProxyHost { + return nil + }, map[httpmw.CSPFetchDirective][]string{ + httpmw.CSPFrameAncestors: {"https://example.com"}, + })(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.WriteHeader(http.StatusOK) + })).ServeHTTP(rw, r) + + csp := rw.Header().Get("Content-Security-Policy") + require.Contains(t, csp, "frame-ancestors https://example.com") + require.NotContains(t, csp, "frame-ancestors 'self'") + }) + + t.Run("OmitWhenEmpty", func(t *testing.T) { + t.Parallel() + + r := httptest.NewRequest(http.MethodGet, "/", nil) + rw := httptest.NewRecorder() + + httpmw.CSPHeaders(false, func() []*proxyhealth.ProxyHost { + return nil + }, map[httpmw.CSPFetchDirective][]string{ + httpmw.CSPFrameAncestors: {}, + })(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.WriteHeader(http.StatusOK) + })).ServeHTTP(rw, r) + + csp := rw.Header().Get("Content-Security-Policy") + require.NotContains(t, csp, "frame-ancestors") + }) +} + func TestCSP(t *testing.T) { t.Parallel() diff --git a/coderd/httpmw/loggermw/logger.go b/coderd/httpmw/loggermw/logger.go index edd878efa98..b1da6139510 100644 --- a/coderd/httpmw/loggermw/logger.go +++ b/coderd/httpmw/loggermw/logger.go @@ -12,7 +12,6 @@ import ( "github.com/go-chi/chi/v5" "cdr.dev/slog" - "github.com/coder/coder/v2/coderd/httpapi" "github.com/coder/coder/v2/coderd/tracing" ) @@ -69,7 +68,7 @@ func safeQueryParams(params url.Values) []slog.Field { return fields } -func Logger(log slog.Logger) func(next http.Handler) http.Handler { +func Logger(log slog.Logger, hostResolver func(*http.Request) string) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { start := time.Now() @@ -79,8 +78,14 @@ func Logger(log slog.Logger) func(next http.Handler) http.Handler { panic(fmt.Sprintf("ResponseWriter not a *tracing.StatusWriter; got %T", rw)) } + host := r.Host + if hostResolver != nil { + host = hostResolver(r) + } + httplog := log.With( - slog.F("host", httpapi.RequestHost(r)), + slog.F("host", host), + slog.F("received_host", r.Host), slog.F("path", r.URL.Path), slog.F("proto", r.Proto), slog.F("remote_addr", r.RemoteAddr), diff --git a/coderd/httpmw/loggermw/logger_internal_test.go b/coderd/httpmw/loggermw/logger_internal_test.go index 5f22de7477d..7a254894adc 100644 --- a/coderd/httpmw/loggermw/logger_internal_test.go +++ b/coderd/httpmw/loggermw/logger_internal_test.go @@ -68,7 +68,7 @@ func TestLoggerMiddleware_SingleRequest(t *testing.T) { }) // Wrap the test handler with the Logger middleware - loggerMiddleware := Logger(logger) + loggerMiddleware := Logger(logger, nil) wrappedHandler := loggerMiddleware(testHandler) // Create a test HTTP request @@ -90,7 +90,7 @@ func TestLoggerMiddleware_SingleRequest(t *testing.T) { } // Check that the log contains the expected fields - requiredFields := []string{"host", "path", "proto", "remote_addr", "start", "took", "status_code", "latency_ms"} + requiredFields := []string{"host", "received_host", "path", "proto", "remote_addr", "start", "took", "status_code", "latency_ms"} for _, field := range requiredFields { _, exists := fieldsMap[field] require.True(t, exists, "field %q is missing in log fields", field) @@ -102,6 +102,38 @@ func TestLoggerMiddleware_SingleRequest(t *testing.T) { require.Equal(t, fieldsMap["status_code"], http.StatusOK) } +func TestLoggerMiddleware_HostFields(t *testing.T) { + t.Parallel() + + sink := &fakeSink{} + logger := slog.Make(sink) + logger = logger.Leveled(slog.LevelDebug) + + testHandler := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { + rw.WriteHeader(http.StatusOK) + }) + + loggerMiddleware := Logger(logger, func(_ *http.Request) string { + return "effective.test" + }) + wrappedHandler := loggerMiddleware(testHandler) + + req := httptest.NewRequest(http.MethodGet, "http://received.test/path", nil) + + sw := &tracing.StatusWriter{ResponseWriter: httptest.NewRecorder()} + wrappedHandler.ServeHTTP(sw, req) + + require.Len(t, sink.entries, 1, "expected exactly one log entry") + + fieldsMap := make(map[string]any) + for _, field := range sink.entries[0].Fields { + fieldsMap[field.Name] = field.Value + } + + require.Equal(t, "effective.test", fieldsMap["host"]) + require.Equal(t, "received.test", fieldsMap["received_host"]) +} + func TestLoggerMiddleware_WebSocket(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort) @@ -129,7 +161,7 @@ func TestLoggerMiddleware_WebSocket(t *testing.T) { }) // Wrap the test handler with the Logger middleware - loggerMiddleware := Logger(logger) + loggerMiddleware := Logger(logger, nil) wrappedHandler := loggerMiddleware(testHandler) // RequestLogger expects the ResponseWriter to be *tracing.StatusWriter @@ -184,7 +216,7 @@ func TestRequestLogger_HTTPRouteParams(t *testing.T) { }) // Wrap the test handler with the Logger middleware - loggerMiddleware := Logger(logger) + loggerMiddleware := Logger(logger, nil) wrappedHandler := loggerMiddleware(testHandler) // Create a test HTTP request diff --git a/coderd/httpmw/realip.go b/coderd/httpmw/realip.go index 6f0f318b832..f428e15fcf4 100644 --- a/coderd/httpmw/realip.go +++ b/coderd/httpmw/realip.go @@ -105,6 +105,35 @@ func FilterUntrustedOriginHeaders(config *RealIPConfig, req *http.Request) { } } +// EffectiveHost returns the host Coder should trust for request handling. +// It uses X-Forwarded-Host only when the immediate peer is a configured +// trusted proxy. Otherwise it uses the received Host header. +func EffectiveHost(config *RealIPConfig, r *http.Request) string { + if config == nil { + config = &RealIPConfig{ + TrustedOrigins: nil, + TrustedHeaders: nil, + } + } + + // When ExtractRealIP has run, r.RemoteAddr may hold the forwarded + // client IP, and we should use the original socket peer for proxy + // trust decisions. + remoteAddr := r.RemoteAddr + state := RealIP(r.Context()) + if state != nil && state.OriginalRemoteAddr != "" { + remoteAddr = state.OriginalRemoteAddr + } + + if isContainedIn(config.TrustedOrigins, getRemoteAddress(remoteAddr)) { + if host := r.Header.Get(httpapi.XForwardedHostHeader); host != "" { + return host + } + } + + return r.Host +} + // EnsureXForwardedForHeader ensures that the request has an X-Forwarded-For // header. It uses the following logic: // diff --git a/coderd/httpmw/realip_test.go b/coderd/httpmw/realip_test.go index 18b870ae379..caa1fe98496 100644 --- a/coderd/httpmw/realip_test.go +++ b/coderd/httpmw/realip_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" + "github.com/coder/coder/v2/coderd/httpapi" "github.com/coder/coder/v2/coderd/httpmw" ) @@ -472,6 +473,112 @@ func TestFilterUntrusted(t *testing.T) { } } +func TestEffectiveHost(t *testing.T) { + t.Parallel() + + cidr32 := func(t *testing.T, ip string) *net.IPNet { + t.Helper() + + return &net.IPNet{ + IP: net.ParseIP(ip), + Mask: net.CIDRMask(32, 32), + } + } + + t.Run("UntrustedPeerFallsBackToReceivedHost", func(t *testing.T) { + t.Parallel() + + r := httptest.NewRequest(http.MethodGet, "http://received.test", nil) + r.RemoteAddr = "17.18.19.20:1234" + r.Header.Set(httpapi.XForwardedHostHeader, "app.test.coder.com") + + require.Equal(t, "received.test", httpmw.EffectiveHost(nil, r)) + }) + + t.Run("TrustedPeerUsesOriginalRemoteAddrForTrust", func(t *testing.T) { + t.Parallel() + + config := &httpmw.RealIPConfig{ + TrustedOrigins: []*net.IPNet{cidr32(t, "17.18.19.20")}, + TrustedHeaders: []string{"X-Real-Ip"}, + } + + r := httptest.NewRequest(http.MethodGet, "http://received.test", nil) + r.RemoteAddr = "17.18.19.20:1234" + // X-Real-Ip causes ExtractRealIP to rewrite r.RemoteAddr, so + // this test can verify trust still uses OriginalRemoteAddr, + // the actual socket peer. + r.Header.Set("X-Real-Ip", "99.88.77.66") + r.Header.Set(httpapi.XForwardedHostHeader, "app.test.coder.com") + + middleware := httpmw.ExtractRealIP(config) + next := http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { + require.Equal(t, "99.88.77.66", r.RemoteAddr) + require.Equal(t, "app.test.coder.com", httpmw.EffectiveHost(config, r)) + }) + + middleware(next).ServeHTTP(httptest.NewRecorder(), r) + }) + + t.Run("UntrustedPeerDoesNotHonorForwardedHost", func(t *testing.T) { + t.Parallel() + + config := &httpmw.RealIPConfig{ + TrustedOrigins: []*net.IPNet{cidr32(t, "99.88.77.66")}, + TrustedHeaders: []string{"X-Real-Ip"}, + } + + r := httptest.NewRequest(http.MethodGet, "http://received.test", nil) + r.RemoteAddr = "17.18.19.20:1234" + r.Header.Set("X-Real-Ip", "99.88.77.66") + r.Header.Set(httpapi.XForwardedHostHeader, "app.test.coder.com") + + middleware := httpmw.ExtractRealIP(config) + nextHandler := http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { + require.Equal(t, "17.18.19.20", r.RemoteAddr) + require.Equal(t, "received.test", httpmw.EffectiveHost(config, r)) + }) + + middleware(nextHandler).ServeHTTP(httptest.NewRecorder(), r) + }) + + t.Run("TrustedPeerWithoutForwardedHostFallsBackToReceivedHost", func(t *testing.T) { + t.Parallel() + + config := &httpmw.RealIPConfig{ + TrustedOrigins: []*net.IPNet{cidr32(t, "17.18.19.20")}, + TrustedHeaders: []string{"X-Real-Ip"}, + } + + r := httptest.NewRequest(http.MethodGet, "http://received.test", nil) + r.RemoteAddr = "17.18.19.20:1234" + + middleware := httpmw.ExtractRealIP(config) + nextHandler := http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { + require.Equal(t, "received.test", httpmw.EffectiveHost(config, r)) + }) + + middleware(nextHandler).ServeHTTP(httptest.NewRecorder(), r) + }) + + t.Run("MalformedRemoteAddrFallsBackToReceivedHost", func(t *testing.T) { + t.Parallel() + + config := &httpmw.RealIPConfig{ + TrustedOrigins: []*net.IPNet{cidr32(t, "17.18.19.20")}, + TrustedHeaders: []string{"X-Real-Ip"}, + } + + r := httptest.NewRequest(http.MethodGet, "http://received.test", nil) + // A RemoteAddr that cannot be parsed into an IP must be treated as + // untrusted, so the forwarded host is ignored. + r.RemoteAddr = "garbage" + r.Header.Set(httpapi.XForwardedHostHeader, "app.test.coder.com") + + require.Equal(t, "received.test", httpmw.EffectiveHost(config, r)) + }) +} + // TestApplicationProxy checks headers passed to DevURL services are as expected. func TestApplicationProxy(t *testing.T) { t.Parallel() diff --git a/coderd/inboxnotifications.go b/coderd/inboxnotifications.go index 4bb3f9ec953..c26f435dd2b 100644 --- a/coderd/inboxnotifications.go +++ b/coderd/inboxnotifications.go @@ -21,7 +21,6 @@ import ( "github.com/coder/coder/v2/coderd/pubsub" markdown "github.com/coder/coder/v2/coderd/render" "github.com/coder/coder/v2/codersdk" - "github.com/coder/coder/v2/codersdk/wsjson" "github.com/coder/websocket" ) @@ -127,6 +126,7 @@ func (api *API) watchInboxNotifications(rw http.ResponseWriter, r *http.Request) templates = p.UUIDs(vals, []uuid.UUID{}, "templates") readStatus = p.String(vals, "all", "read_status") format = p.String(vals, notificationFormatMarkdown, "format") + logger = api.Logger.Named("inbox_notifications_watcher") ) p.ErrorExcessParams(vals) if len(p.Errors) > 0 { @@ -214,11 +214,17 @@ func (api *API) watchInboxNotifications(rw http.ResponseWriter, r *http.Request) return } - go httpapi.Heartbeat(ctx, conn) - defer conn.Close(websocket.StatusNormalClosure, "connection closed") + ctx, cancel := context.WithCancel(ctx) + defer cancel() - encoder := wsjson.NewEncoder[codersdk.GetInboxNotificationResponse](conn, websocket.MessageText) - defer encoder.Close(websocket.StatusNormalClosure) + _ = conn.CloseRead(context.Background()) + + ctx, wsNetConn := codersdk.WebsocketNetConn(ctx, conn, websocket.MessageText) + defer wsNetConn.Close() + + go httpapi.HeartbeatClose(ctx, logger, cancel, conn) + + encoder := json.NewEncoder(wsNetConn) // Log the request immediately instead of after it completes. if rl := loggermw.RequestLoggerFromContext(ctx); rl != nil { @@ -227,8 +233,12 @@ func (api *API) watchInboxNotifications(rw http.ResponseWriter, r *http.Request) for { select { + case <-api.ctx.Done(): + return + case <-ctx.Done(): return + case notif := <-notificationCh: unreadCount, err := api.Database.CountUnreadInboxNotificationsByUserID(ctx, apikey.UserID) if err != nil { diff --git a/coderd/members.go b/coderd/members.go index dd9ce73bba2..ba70956646f 100644 --- a/coderd/members.go +++ b/coderd/members.go @@ -238,9 +238,18 @@ func (api *API) paginatedMembers(rw http.ResponseWriter, r *http.Request) { memberRows = append(memberRows, row) } + if len(paginatedMemberRows) == 0 { + httpapi.Write(ctx, rw, http.StatusOK, codersdk.PaginatedMembersResponse{ + Members: []codersdk.OrganizationMemberWithUserData{}, + Count: 0, + }) + return + } + members, err := convertOrganizationMembersWithUserData(ctx, api.Database, memberRows) if err != nil { httpapi.InternalServerError(rw, err) + return } resp := codersdk.PaginatedMembersResponse{ diff --git a/coderd/notifications/dispatch/smtp/html.gotmpl b/coderd/notifications/dispatch/smtp/html.gotmpl index 4e49c4239d1..cecba560af2 100644 --- a/coderd/notifications/dispatch/smtp/html.gotmpl +++ b/coderd/notifications/dispatch/smtp/html.gotmpl @@ -8,7 +8,7 @@
- {{ app_name }} Logo + {{ app_name | html }} Logo

{{ .Labels._subject }} diff --git a/coderd/notifications/dispatch/smtp_internal_test.go b/coderd/notifications/dispatch/smtp_internal_test.go new file mode 100644 index 00000000000..70b08189ee9 --- /dev/null +++ b/coderd/notifications/dispatch/smtp_internal_test.go @@ -0,0 +1,44 @@ +package dispatch + +import ( + "html" + "strings" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/coder/coder/v2/coderd/notifications/render" + "github.com/coder/coder/v2/coderd/notifications/types" +) + +func TestSMTPHTMLTemplateEscapesAppearanceHelpers(t *testing.T) { + t.Parallel() + + const ( + appName = `Coder">` + logoURL = `https://example.com/logo.png">` + ) + + payload := types.MessagePayload{ + NotificationTemplateID: "00000000-0000-0000-0000-000000000000", + UserName: "Test User", + Labels: map[string]string{ + "_subject": "Test notification", + "_body": "

Test body

", + }, + } + helpers := map[string]any{ + "base_url": func() string { return "https://coder.example.com" }, + "current_year": func() string { return "2026" }, + "logo_url": func() string { return logoURL }, + "app_name": func() string { return appName }, + } + + got, err := render.GoTemplate(htmlTemplate, payload, helpers) + require.NoError(t, err) + + require.True(t, strings.Contains(got, html.EscapeString(appName)), "application name must be HTML escaped") + require.True(t, strings.Contains(got, html.EscapeString(logoURL)), "logo URL must be HTML escaped") + require.False(t, strings.Contains(got, appName), "raw application name must not be rendered") + require.False(t, strings.Contains(got, logoURL), "raw logo URL must not be rendered") +} diff --git a/coderd/prebuilds/api.go b/coderd/prebuilds/api.go index 0deab99416f..dc5092a06f2 100644 --- a/coderd/prebuilds/api.go +++ b/coderd/prebuilds/api.go @@ -63,6 +63,7 @@ type StateSnapshotter interface { type Claimer interface { Claim( ctx context.Context, + store database.Store, now time.Time, userID uuid.UUID, name string, diff --git a/coderd/prebuilds/noop.go b/coderd/prebuilds/noop.go index 0859d428b47..1dda74c1dd1 100644 --- a/coderd/prebuilds/noop.go +++ b/coderd/prebuilds/noop.go @@ -34,7 +34,7 @@ var DefaultReconciler ReconciliationOrchestrator = NoopReconciler{} type NoopClaimer struct{} -func (NoopClaimer) Claim(context.Context, time.Time, uuid.UUID, string, uuid.UUID, sql.NullString, sql.NullTime, sql.NullInt64) (*uuid.UUID, error) { +func (NoopClaimer) Claim(context.Context, database.Store, time.Time, uuid.UUID, string, uuid.UUID, sql.NullString, sql.NullTime, sql.NullInt64) (*uuid.UUID, error) { // Not entitled to claim prebuilds in AGPL version. return nil, ErrAGPLDoesNotSupportPrebuiltWorkspaces } diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index c4598beaf83..6341a53da57 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1614,6 +1614,7 @@ func (s *server) completeTemplateImportJob(ctx context.Context, job database.Pro slog.F("transition", transition)) if err := InsertWorkspaceResource(ctx, db, jobID, transition, resource, telemetrySnapshot); err != nil { + s.warnWorkspaceAppRebindRejected(ctx, jobID, err) return xerrors.Errorf("insert resource: %w", err) } } @@ -2016,6 +2017,7 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro InsertWorkspaceResourceWithAgentIDsFromProto(), ) if err != nil { + s.warnWorkspaceAppRebindRejected(ctx, jobID, err) return xerrors.Errorf("insert provisioner job: %w", err) } } @@ -2026,13 +2028,11 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro } var ( - hasAITask bool unknownAppID string taskAppID uuid.NullUUID taskAgentID uuid.NullUUID ) if tasks := jobType.WorkspaceBuild.GetAiTasks(); len(tasks) > 0 { - hasAITask = true task := tasks[0] if task == nil { return xerrors.Errorf("update ai task: task is nil") @@ -2048,7 +2048,6 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro if !slices.Contains(appIDs, appID) { unknownAppID = appID - hasAITask = false } else { // Only parse for valid app and agent to avoid fk violation. id, err := uuid.Parse(appID) @@ -2083,7 +2082,7 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro Level: []database.LogLevel{database.LogLevelWarn, database.LogLevelWarn, database.LogLevelWarn, database.LogLevelWarn}, Stage: []string{"Cleaning Up", "Cleaning Up", "Cleaning Up", "Cleaning Up"}, Output: []string{ - fmt.Sprintf("Unknown ai_task_app_id %q. This workspace will be unable to run AI tasks. This may be due to a template configuration issue, please check with the template author.", taskAppID.UUID.String()), + fmt.Sprintf("Unknown ai_task_app_id %q. This workspace will be unable to run AI tasks. This may be due to a template configuration issue, please check with the template author.", unknownAppID), "Template author: double-check the following:", " - You have associated the coder_ai_task with a valid coder_app in your template (ref: https://registry.terraform.io/providers/coder/coder/latest/docs/resources/ai_task).", " - You have associated the coder_agent with at least one other compute resource. Agents with no other associated resources are not inserted into the database.", @@ -2098,21 +2097,23 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro } } - if hasAITask && workspaceBuild.Transition == database.WorkspaceTransitionStart { - // Insert usage event for managed agents. - usageInserter := s.UsageInserter.Load() - if usageInserter != nil { - event := usagetypes.DCManagedAgentsV1{ - Count: 1, - } - err = (*usageInserter).InsertDiscreteUsageEvent(ctx, db, event) - if err != nil { - return xerrors.Errorf("insert %q event: %w", event.EventType(), err) + var hasAITask bool + if task, err := db.GetTaskByWorkspaceID(ctx, workspace.ID); err == nil { + hasAITask = true + if workspaceBuild.Transition == database.WorkspaceTransitionStart { + // Insert usage event for managed agents. + usageInserter := s.UsageInserter.Load() + if usageInserter != nil { + event := usagetypes.DCManagedAgentsV1{ + Count: 1, + } + err = (*usageInserter).InsertDiscreteUsageEvent(ctx, db, event) + if err != nil { + return xerrors.Errorf("insert %q event: %w", event.EventType(), err) + } } } - } - if task, err := db.GetTaskByWorkspaceID(ctx, workspace.ID); err == nil { // Irrespective of whether the agent or sidebar app is present, // perform the upsert to ensure a link between the task and // workspace build. Linking the task to the build is typically @@ -2434,6 +2435,7 @@ func (s *server) completeTemplateDryRunJob(ctx context.Context, job database.Pro err := InsertWorkspaceResource(ctx, db, jobID, database.WorkspaceTransitionStart, resource, telemetrySnapshot) if err != nil { + s.warnWorkspaceAppRebindRejected(ctx, jobID, err) return xerrors.Errorf("insert resource: %w", err) } } @@ -3017,6 +3019,17 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. Tooltip: app.Tooltip, }) if err != nil { + if errors.Is(err, sql.ErrNoRows) { + // The upsert's ON CONFLICT guard refused to rebind an app + // owned by a workspace to an agent outside that workspace, + // including agents from import or dry-run jobs that resolve + // to no workspace (SEC-91). + return &workspaceAppRebindError{ + slug: slug, + appID: id, + agentID: dbAgent.ID, + } + } return xerrors.Errorf("upsert app: %w", err) } snapshot.WorkspaceApps = append(snapshot.WorkspaceApps, telemetry.ConvertWorkspaceApp(dbApp)) @@ -3325,3 +3338,29 @@ func convertDisplayApps(apps *sdkproto.DisplayApps) []database.DisplayApp { } return dapps } + +type workspaceAppRebindError struct { + slug string + appID uuid.UUID + agentID uuid.UUID +} + +func (e *workspaceAppRebindError) Error() string { + return fmt.Sprintf("workspace app slug %q with ID %q is already bound to a workspace-owned agent and cannot be rebound to an agent in another workspace or to an agent without a workspace; refusing to rebind to agent ID %q", e.slug, e.appID, e.agentID) +} + +func (s *server) warnWorkspaceAppRebindRejected(ctx context.Context, jobID uuid.UUID, err error) { + slog.Helper() + + var rebindErr *workspaceAppRebindError + if !errors.As(err, &rebindErr) { + return + } + + s.Logger.Warn(ctx, "workspace app rebind rejected by SQL guard", + slog.F("job_id", jobID.String()), + slog.F("app_id", rebindErr.appID.String()), + slog.F("agent_id", rebindErr.agentID.String()), + slog.F("app_slug", rebindErr.slug), + ) +} diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index 4dc8621736b..7e88c5db10d 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -25,6 +25,7 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" "storj.io/drpc" + "cdr.dev/slog" "cdr.dev/slog/sloggers/slogtest" "github.com/coder/coder/v2/coderd" "github.com/coder/coder/v2/coderd/util/ptr" @@ -2179,6 +2180,109 @@ func TestCompleteJob(t *testing.T) { }) } }) + t.Run("WorkspaceBuild_CrossWorkspaceAppRebindRejected", func(t *testing.T) { + t.Parallel() + + logSink := &recordingSlogSink{} + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).AppendSinks(logSink) + srv, db, _, pd := setup(t, false, &overrides{provisionerdLogger: &logger}) + + // Given: a victim workspace whose agent owns an app with a known UUID. + victimAppID, victimAgentID, victimSlug := setupWorkspaceAppRebindVictim( + t, db, pd.OrganizationID, + ) + + // Given: an attacker workspace with a running build job acquired by the + // provisioner daemon. + attackerUser := dbgen.User(t, db, database.User{}) + attackerTemplate := dbgen.Template(t, db, database.Template{ + CreatedBy: attackerUser.ID, + OrganizationID: pd.OrganizationID, + }) + attackerVersion := dbgen.TemplateVersion(t, db, database.TemplateVersion{ + CreatedBy: attackerUser.ID, + OrganizationID: pd.OrganizationID, + TemplateID: uuid.NullUUID{UUID: attackerTemplate.ID, Valid: true}, + JobID: uuid.New(), + }) + attackerWorkspace := dbgen.Workspace(t, db, database.WorkspaceTable{ + TemplateID: attackerTemplate.ID, + OwnerID: attackerUser.ID, + OrganizationID: pd.OrganizationID, + }) + attackerBuildID := uuid.New() + attackerJob := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{ + InitiatorID: attackerUser.ID, + Type: database.ProvisionerJobTypeWorkspaceBuild, + Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{ + WorkspaceBuildID: attackerBuildID, + })), + OrganizationID: pd.OrganizationID, + }) + dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{ + ID: attackerBuildID, + JobID: attackerJob.ID, + WorkspaceID: attackerWorkspace.ID, + TemplateVersionID: attackerVersion.ID, + InitiatorID: attackerUser.ID, + Transition: database.WorkspaceTransitionStart, + Reason: database.BuildReasonInitiator, + }) + _, err := db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{ + OrganizationID: pd.OrganizationID, + WorkerID: uuid.NullUUID{UUID: pd.ID, Valid: true}, + Types: []database.ProvisionerType{database.ProvisionerTypeEcho}, + StartedAt: sql.NullTime{Time: time.Now(), Valid: true}, + ProvisionerTags: must(json.Marshal(attackerJob.Tags)), + }) + require.NoError(t, err) + + // When: the attacker's build completes with an app that reuses the + // victim's app UUID but points at the attacker's (new) agent. + attackerAgent := &sdkproto.Agent{ + Id: uuid.NewString(), + Name: "dev", + Auth: &sdkproto.Agent_Token{Token: uuid.NewString()}, + Apps: []*sdkproto.App{{ + Id: victimAppID.String(), + Slug: "attacker-app", + }}, + } + _, err = srv.CompleteJob(ctx, &proto.CompletedJob{ + JobId: attackerJob.ID.String(), + Type: &proto.CompletedJob_WorkspaceBuild_{ + WorkspaceBuild: &proto.CompletedJob_WorkspaceBuild{ + State: []byte{}, + Resources: []*sdkproto.Resource{{ + Name: "example", + Type: "aws_instance", + Agents: []*sdkproto.Agent{attackerAgent}, + }}, + }, + }, + }) + // Then: the build is rejected with the cross-tenant rebind error. + require.Error(t, err) + require.ErrorContains(t, err, "already bound to a workspace-owned agent") + assertWorkspaceAppRebindWarning( + t, + logSink, + workspaceAppRebindWarning{ + jobID: attackerJob.ID, + appID: victimAppID, + slug: "attacker-app", + agentID: attackerAgent.Id, + }, + ) + + // And: the victim's app remains bound to the victim agent, unchanged. + victimApps, err := db.GetWorkspaceAppsByAgentID(ctx, victimAgentID) + require.NoError(t, err) + require.Len(t, victimApps, 1) + require.Equal(t, victimAppID, victimApps[0].ID) + require.Equal(t, victimAgentID, victimApps[0].AgentID) + require.Equal(t, victimSlug, victimApps[0].Slug) + }) t.Run("TemplateDryRun", func(t *testing.T) { t.Parallel() srv, db, _, pd := setup(t, false, &overrides{}) @@ -2229,6 +2333,161 @@ func TestCompleteJob(t *testing.T) { require.NoError(t, err) }) + t.Run("TemplateDryRun_CrossWorkspaceAppRebindRejected", func(t *testing.T) { + t.Parallel() + logSink := &recordingSlogSink{} + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).AppendSinks(logSink) + srv, db, _, pd := setup(t, false, &overrides{provisionerdLogger: &logger}) + + victimAppID, victimAgentID, victimSlug := setupWorkspaceAppRebindVictim( + t, db, pd.OrganizationID, + ) + + user := dbgen.User(t, db, database.User{}) + version := dbgen.TemplateVersion(t, db, database.TemplateVersion{ + CreatedBy: user.ID, + OrganizationID: pd.OrganizationID, + JobID: uuid.New(), + }) + job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{ + ID: version.JobID, + Provisioner: database.ProvisionerTypeEcho, + Type: database.ProvisionerJobTypeTemplateVersionDryRun, + StorageMethod: database.ProvisionerStorageMethodFile, + Input: must(json.Marshal(provisionerdserver.TemplateVersionDryRunJob{ + TemplateVersionID: version.ID, + })), + OrganizationID: pd.OrganizationID, + Tags: pd.Tags, + }) + require.NoError(t, err) + _, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{ + WorkerID: uuid.NullUUID{UUID: pd.ID, Valid: true}, + Types: []database.ProvisionerType{database.ProvisionerTypeEcho}, + StartedAt: sql.NullTime{Time: dbtime.Now(), Valid: true}, + OrganizationID: pd.OrganizationID, + ProvisionerTags: must(json.Marshal(job.Tags)), + }) + require.NoError(t, err) + + dryRunAgent := &sdkproto.Agent{ + Name: "dev", + Auth: &sdkproto.Agent_Token{Token: uuid.NewString()}, + Apps: []*sdkproto.App{{ + Id: victimAppID.String(), + Slug: "dry-run-app", + }}, + } + _, err = srv.CompleteJob(ctx, &proto.CompletedJob{ + JobId: job.ID.String(), + Type: &proto.CompletedJob_TemplateDryRun_{ + TemplateDryRun: &proto.CompletedJob_TemplateDryRun{ + Resources: []*sdkproto.Resource{{ + Name: "something", + Type: "aws_instance", + Agents: []*sdkproto.Agent{dryRunAgent}, + }}, + }, + }, + }) + require.Error(t, err) + require.ErrorContains(t, err, "already bound to a workspace-owned agent") + assertWorkspaceAppRebindWarning( + t, + logSink, + workspaceAppRebindWarning{ + jobID: job.ID, + appID: victimAppID, + slug: "dry-run-app", + }, + ) + + victimApps, err := db.GetWorkspaceAppsByAgentID(ctx, victimAgentID) + require.NoError(t, err) + require.Len(t, victimApps, 1) + require.Equal(t, victimAppID, victimApps[0].ID) + require.Equal(t, victimAgentID, victimApps[0].AgentID) + require.Equal(t, victimSlug, victimApps[0].Slug) + }) + + t.Run("TemplateImport_CrossWorkspaceAppRebindRejected", func(t *testing.T) { + t.Parallel() + logSink := &recordingSlogSink{} + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).AppendSinks(logSink) + srv, db, _, pd := setup(t, false, &overrides{provisionerdLogger: &logger}) + + victimAppID, victimAgentID, victimSlug := setupWorkspaceAppRebindVictim( + t, db, pd.OrganizationID, + ) + + user := dbgen.User(t, db, database.User{}) + version := dbgen.TemplateVersion(t, db, database.TemplateVersion{ + CreatedBy: user.ID, + OrganizationID: pd.OrganizationID, + JobID: uuid.New(), + }) + job, err := db.InsertProvisionerJob(ctx, database.InsertProvisionerJobParams{ + ID: version.JobID, + Provisioner: database.ProvisionerTypeEcho, + Type: database.ProvisionerJobTypeTemplateVersionImport, + StorageMethod: database.ProvisionerStorageMethodFile, + Input: must(json.Marshal(provisionerdserver.TemplateVersionImportJob{ + TemplateVersionID: version.ID, + })), + OrganizationID: pd.OrganizationID, + Tags: pd.Tags, + }) + require.NoError(t, err) + _, err = db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{ + WorkerID: uuid.NullUUID{UUID: pd.ID, Valid: true}, + Types: []database.ProvisionerType{database.ProvisionerTypeEcho}, + StartedAt: sql.NullTime{Time: dbtime.Now(), Valid: true}, + OrganizationID: pd.OrganizationID, + ProvisionerTags: must(json.Marshal(job.Tags)), + }) + require.NoError(t, err) + + importAgent := &sdkproto.Agent{ + Name: "dev", + Auth: &sdkproto.Agent_Token{Token: uuid.NewString()}, + Apps: []*sdkproto.App{{ + Id: victimAppID.String(), + Slug: "import-app", + }}, + } + _, err = srv.CompleteJob(ctx, &proto.CompletedJob{ + JobId: job.ID.String(), + Type: &proto.CompletedJob_TemplateImport_{ + TemplateImport: &proto.CompletedJob_TemplateImport{ + StartResources: []*sdkproto.Resource{{ + Name: "something", + Type: "aws_instance", + Agents: []*sdkproto.Agent{importAgent}, + }}, + Plan: []byte("{}"), + }, + }, + }) + require.Error(t, err) + require.ErrorContains(t, err, "already bound to a workspace-owned agent") + assertWorkspaceAppRebindWarning( + t, + logSink, + workspaceAppRebindWarning{ + jobID: job.ID, + appID: victimAppID, + slug: "import-app", + }, + ) + + victimApps, err := db.GetWorkspaceAppsByAgentID(ctx, victimAgentID) + require.NoError(t, err) + require.Len(t, victimApps, 1) + require.Equal(t, victimAppID, victimApps[0].ID) + require.Equal(t, victimAgentID, victimApps[0].AgentID) + require.Equal(t, victimSlug, victimApps[0].Slug) + }) + t.Run("Modules", func(t *testing.T) { t.Parallel() @@ -2878,7 +3137,7 @@ func TestCompleteJob(t *testing.T) { sidebarAppID := uuid.New() for _, tc := range []testcase{ { - name: "has_ai_task is false by default", + name: "has_ai_task is false if task_id is nil", transition: database.WorkspaceTransitionStart, input: &proto.CompletedJob_WorkspaceBuild{ // No AiTasks defined. @@ -2887,6 +3146,37 @@ func TestCompleteJob(t *testing.T) { expectHasAiTask: false, expectUsageEvent: false, }, + { + name: "has_ai_task is false even if there are coder_ai_task resources, but no task_id", + transition: database.WorkspaceTransitionStart, + input: &proto.CompletedJob_WorkspaceBuild{ + AiTasks: []*sdkproto.AITask{ + { + Id: uuid.NewString(), + AppId: sidebarAppID.String(), + }, + }, + Resources: []*sdkproto.Resource{ + { + Agents: []*sdkproto.Agent{ + { + Id: uuid.NewString(), + Name: "a", + Apps: []*sdkproto.App{ + { + Id: sidebarAppID.String(), + Slug: "test-app", + }, + }, + }, + }, + }, + }, + }, + isTask: false, + expectHasAiTask: false, + expectUsageEvent: false, + }, { name: "has_ai_task is set to true", transition: database.WorkspaceTransitionStart, @@ -2964,15 +3254,17 @@ func TestCompleteJob(t *testing.T) { { Id: uuid.NewString(), // Non-existing app ID would previously trigger a FK violation. - // Now it should just be ignored. + // Now it will trigger a warning instead in the provisioner logs. AppId: sidebarAppID.String(), }, }, }, isTask: true, expectTaskStatus: database.TaskStatusInitializing, - expectHasAiTask: false, - expectUsageEvent: false, + // You can still "sort of" use a task in this state, but as we don't have + // the correct app ID you won't be able to communicate with it via Coder. + expectHasAiTask: true, + expectUsageEvent: true, }, { name: "has_ai_task is set to true, but transition is not start", @@ -3007,19 +3299,6 @@ func TestCompleteJob(t *testing.T) { expectHasAiTask: true, expectUsageEvent: false, }, - { - name: "current build does not have ai task but previous build did", - seedFunc: seedPreviousWorkspaceStartWithAITask, - transition: database.WorkspaceTransitionStop, - input: &proto.CompletedJob_WorkspaceBuild{ - AiTasks: []*sdkproto.AITask{}, - Resources: []*sdkproto.Resource{}, - }, - isTask: true, - expectTaskStatus: database.TaskStatusPaused, - expectHasAiTask: false, // We no longer inherit this from the previous build. - expectUsageEvent: false, - }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() @@ -3157,6 +3436,59 @@ func TestCompleteJob(t *testing.T) { }) } +func setupWorkspaceAppRebindVictim( + t *testing.T, + db database.Store, + organizationID uuid.UUID, +) (appID uuid.UUID, agentID uuid.UUID, slug string) { + t.Helper() + + victimUser := dbgen.User(t, db, database.User{}) + victimTemplate := dbgen.Template(t, db, database.Template{ + CreatedBy: victimUser.ID, + OrganizationID: organizationID, + }) + victimVersion := dbgen.TemplateVersion(t, db, database.TemplateVersion{ + CreatedBy: victimUser.ID, + OrganizationID: organizationID, + TemplateID: uuid.NullUUID{UUID: victimTemplate.ID, Valid: true}, + }) + victimWorkspace := dbgen.Workspace(t, db, database.WorkspaceTable{ + TemplateID: victimTemplate.ID, + OwnerID: victimUser.ID, + OrganizationID: organizationID, + }) + victimJob := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{ + Type: database.ProvisionerJobTypeWorkspaceBuild, + OrganizationID: organizationID, + StartedAt: sql.NullTime{Time: time.Now().Add(-time.Hour), Valid: true}, + CompletedAt: sql.NullTime{Time: time.Now().Add(-time.Hour), Valid: true}, + }) + dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{ + JobID: victimJob.ID, + WorkspaceID: victimWorkspace.ID, + TemplateVersionID: victimVersion.ID, + InitiatorID: victimUser.ID, + Transition: database.WorkspaceTransitionStart, + Reason: database.BuildReasonInitiator, + }) + victimResource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{ + JobID: victimJob.ID, + }) + victimAgent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{ + ResourceID: victimResource.ID, + }) + victimAppID := uuid.New() + const victimSlug = "code-server" + dbgen.WorkspaceApp(t, db, database.WorkspaceApp{ + ID: victimAppID, + AgentID: victimAgent.ID, + Slug: victimSlug, + }) + + return victimAppID, victimAgent.ID, victimSlug +} + type mockPrebuildsOrchestrator struct { agplprebuilds.ReconciliationOrchestrator @@ -4141,6 +4473,70 @@ func TestServer_ExpirePrebuildsSessionToken(t *testing.T) { require.ErrorIs(t, err, sql.ErrNoRows, "api key for prebuilds user should be deleted") } +type workspaceAppRebindWarning struct { + jobID uuid.UUID + appID uuid.UUID + slug string + agentID string +} + +func assertWorkspaceAppRebindWarning(t *testing.T, logSink *recordingSlogSink, want workspaceAppRebindWarning) { + t.Helper() + + for _, entry := range logSink.Entries() { + if entry.Message != "workspace app rebind rejected by SQL guard" { + continue + } + + require.Equal(t, slog.LevelWarn, entry.Level) + require.Contains(t, entry.File, "coderd/provisionerdserver/provisionerdserver.go") + require.NotContains(t, entry.Func, "warnWorkspaceAppRebindRejected") + fields := slogFieldsByName(entry.Fields) + require.Equal(t, want.jobID.String(), fields["job_id"]) + require.Equal(t, want.appID.String(), fields["app_id"]) + require.Equal(t, want.slug, fields["app_slug"]) + agentID, ok := fields["agent_id"].(string) + require.True(t, ok) + require.NotEqual(t, uuid.Nil.String(), agentID) + if want.agentID != "" { + require.Equal(t, want.agentID, agentID) + } else { + _, err := uuid.Parse(agentID) + require.NoError(t, err) + } + return + } + + require.Fail(t, "expected workspace app rebind warning") +} + +type recordingSlogSink struct { + mu sync.Mutex + entries []slog.SinkEntry +} + +func (s *recordingSlogSink) LogEntry(_ context.Context, entry slog.SinkEntry) { + s.mu.Lock() + defer s.mu.Unlock() + s.entries = append(s.entries, entry) +} + +func (*recordingSlogSink) Sync() {} + +func (s *recordingSlogSink) Entries() []slog.SinkEntry { + s.mu.Lock() + defer s.mu.Unlock() + return append([]slog.SinkEntry(nil), s.entries...) +} + +func slogFieldsByName(fields []slog.Field) map[string]any { + byName := make(map[string]any, len(fields)) + for _, field := range fields { + byName[field.Name] = field.Value + } + return byName +} + type overrides struct { ctx context.Context deploymentValues *codersdk.DeploymentValues @@ -4155,6 +4551,7 @@ type overrides struct { auditor audit.Auditor notificationEnqueuer notifications.Enqueuer prebuildsOrchestrator agplprebuilds.ReconciliationOrchestrator + provisionerdLogger *slog.Logger } func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisionerDaemonServer, database.Store, pubsub.Pubsub, database.ProvisionerDaemon) { @@ -4231,6 +4628,10 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi } else { notifEnq = notifications.NewNoopEnqueuer() } + provisionerdLogger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: ignoreLogErrors}) + if ov.provisionerdLogger != nil { + provisionerdLogger = *ov.provisionerdLogger + } daemon, err := db.UpsertProvisionerDaemon(ov.ctx, database.UpsertProvisionerDaemonParams{ Name: "test", @@ -4262,7 +4663,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi &url.URL{}, daemon.ID, defOrg.ID, - slogtest.Make(t, &slogtest.Options{IgnoreErrors: ignoreLogErrors}), + provisionerdLogger, []database.ProvisionerType{database.ProvisionerTypeEcho}, provisionerdserver.Tags(daemon.Tags), serverDB, @@ -4410,62 +4811,3 @@ func (f *fakeUsageInserter) InsertDiscreteUsageEvent(_ context.Context, _ databa f.collectedEvents = append(f.collectedEvents, event) return nil } - -func seedPreviousWorkspaceStartWithAITask(ctx context.Context, t testing.TB, db database.Store) error { - t.Helper() - // If the below looks slightly convoluted, that's because it is. - // The workspace doesn't yet have a latest build, so querying all - // workspaces will fail. - tpls, err := db.GetTemplates(ctx) - if err != nil { - return xerrors.Errorf("seedFunc: get template: %w", err) - } - if len(tpls) != 1 { - return xerrors.Errorf("seedFunc: expected exactly one template, got %d", len(tpls)) - } - ws, err := db.GetWorkspacesByTemplateID(ctx, tpls[0].ID) - if err != nil { - return xerrors.Errorf("seedFunc: get workspaces: %w", err) - } - if len(ws) != 1 { - return xerrors.Errorf("seedFunc: expected exactly one workspace, got %d", len(ws)) - } - w := ws[0] - prevJob := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{ - OrganizationID: w.OrganizationID, - InitiatorID: w.OwnerID, - Type: database.ProvisionerJobTypeWorkspaceBuild, - }) - tvs, err := db.GetTemplateVersionsByTemplateID(ctx, database.GetTemplateVersionsByTemplateIDParams{ - TemplateID: tpls[0].ID, - }) - if err != nil { - return xerrors.Errorf("seedFunc: get template version: %w", err) - } - if len(tvs) != 1 { - return xerrors.Errorf("seedFunc: expected exactly one template version, got %d", len(tvs)) - } - if tpls[0].ActiveVersionID == uuid.Nil { - return xerrors.Errorf("seedFunc: active version id is nil") - } - res := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{ - JobID: prevJob.ID, - }) - agt := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{ - ResourceID: res.ID, - }) - _ = dbgen.WorkspaceApp(t, db, database.WorkspaceApp{ - AgentID: agt.ID, - }) - _ = dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{ - BuildNumber: 1, - HasAITask: sql.NullBool{Valid: true, Bool: true}, - ID: w.ID, - InitiatorID: w.OwnerID, - JobID: prevJob.ID, - TemplateVersionID: tvs[0].ID, - Transition: database.WorkspaceTransitionStart, - WorkspaceID: w.ID, - }) - return nil -} diff --git a/coderd/provisionerdserver/upload_file_test.go b/coderd/provisionerdserver/upload_file_test.go index eb822140c40..c329afb8b79 100644 --- a/coderd/provisionerdserver/upload_file_test.go +++ b/coderd/provisionerdserver/upload_file_test.go @@ -48,7 +48,8 @@ func TestUploadFileLargeModuleFiles(t *testing.T) { require.NoError(t, err) // Convert to upload format - upload, chunks := sdkproto.BytesToDataUpload(sdkproto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, moduleData) + upload, chunks, err := sdkproto.BytesToDataUpload(sdkproto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, moduleData) + require.NoError(t, err) stream := newMockUploadStream(upload, chunks...) @@ -93,7 +94,8 @@ func TestUploadFileErrorScenarios(t *testing.T) { _, err := crand.Read(moduleData) require.NoError(t, err) - upload, chunks := sdkproto.BytesToDataUpload(sdkproto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, moduleData) + upload, chunks, err := sdkproto.BytesToDataUpload(sdkproto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, moduleData) + require.NoError(t, err) t.Run("chunk_before_upload", func(t *testing.T) { t.Parallel() diff --git a/coderd/rbac/regosql/compile_test.go b/coderd/rbac/regosql/compile_test.go index 7bea7f76fd4..f58f8085591 100644 --- a/coderd/rbac/regosql/compile_test.go +++ b/coderd/rbac/regosql/compile_test.go @@ -282,6 +282,40 @@ neq(input.object.owner, ""); p("'10d03e62-7703-4df5-a358-4f76577d4e2f' = id :: text") + " AND " + p("id :: text != ''") + " AND " + p("'' = ''"), ), }, + { + Name: "AuditLogUUID", + Queries: []string{ + `"8c0b9bdc-a013-4b14-a49b-5747bc335708" = input.object.org_owner`, + `input.object.org_owner != ""`, + `neq(input.object.org_owner, "8c0b9bdc-a013-4b14-a49b-5747bc335708")`, + `input.object.org_owner in {"8c0b9bdc-a013-4b14-a49b-5747bc335708", "05f58202-4bfc-43ce-9ba4-5ff6e0174a71"}`, + `"read" in input.object.acl_group_list[input.object.org_owner]`, + }, + ExpectedSQL: p( + p("audit_logs.organization_id = '8c0b9bdc-a013-4b14-a49b-5747bc335708'::uuid") + " OR " + + p("audit_logs.organization_id IS NOT NULL") + " OR " + + p("audit_logs.organization_id != '8c0b9bdc-a013-4b14-a49b-5747bc335708'::uuid") + " OR " + + p("audit_logs.organization_id = ANY(ARRAY ['05f58202-4bfc-43ce-9ba4-5ff6e0174a71'::uuid,'8c0b9bdc-a013-4b14-a49b-5747bc335708'::uuid])") + " OR " + + "(false)"), + VariableConverter: regosql.AuditLogConverter(), + }, + { + Name: "ConnectionLogUUID", + Queries: []string{ + `"8c0b9bdc-a013-4b14-a49b-5747bc335708" = input.object.org_owner`, + `input.object.org_owner != ""`, + `neq(input.object.org_owner, "8c0b9bdc-a013-4b14-a49b-5747bc335708")`, + `input.object.org_owner in {"8c0b9bdc-a013-4b14-a49b-5747bc335708"}`, + `"read" in input.object.acl_group_list[input.object.org_owner]`, + }, + ExpectedSQL: p( + p("connection_logs.organization_id = '8c0b9bdc-a013-4b14-a49b-5747bc335708'::uuid") + " OR " + + p("connection_logs.organization_id IS NOT NULL") + " OR " + + p("connection_logs.organization_id != '8c0b9bdc-a013-4b14-a49b-5747bc335708'::uuid") + " OR " + + p("connection_logs.organization_id = ANY(ARRAY ['8c0b9bdc-a013-4b14-a49b-5747bc335708'::uuid])") + " OR " + + "(false)"), + VariableConverter: regosql.ConnectionLogConverter(), + }, } for _, tc := range testCases { diff --git a/coderd/rbac/regosql/configs.go b/coderd/rbac/regosql/configs.go index 355a49756d5..22302a52963 100644 --- a/coderd/rbac/regosql/configs.go +++ b/coderd/rbac/regosql/configs.go @@ -53,7 +53,7 @@ func WorkspaceConverter() *sqltypes.VariableConverter { func AuditLogConverter() *sqltypes.VariableConverter { matcher := sqltypes.NewVariableConverter().RegisterMatcher( resourceIDMatcher(), - sqltypes.StringVarMatcher("COALESCE(audit_logs.organization_id :: text, '')", []string{"input", "object", "org_owner"}), + sqltypes.UUIDVarMatcher("audit_logs.organization_id", []string{"input", "object", "org_owner"}), // Audit logs have no user owner, only owner by an organization. sqltypes.AlwaysFalse(userOwnerMatcher()), ) @@ -67,7 +67,7 @@ func AuditLogConverter() *sqltypes.VariableConverter { func ConnectionLogConverter() *sqltypes.VariableConverter { matcher := sqltypes.NewVariableConverter().RegisterMatcher( resourceIDMatcher(), - sqltypes.StringVarMatcher("COALESCE(connection_logs.organization_id :: text, '')", []string{"input", "object", "org_owner"}), + sqltypes.UUIDVarMatcher("connection_logs.organization_id", []string{"input", "object", "org_owner"}), // Connection logs have no user owner, only owner by an organization. sqltypes.AlwaysFalse(userOwnerMatcher()), ) diff --git a/coderd/rbac/regosql/sqltypes/uuid.go b/coderd/rbac/regosql/sqltypes/uuid.go new file mode 100644 index 00000000000..bcf95c8411a --- /dev/null +++ b/coderd/rbac/regosql/sqltypes/uuid.go @@ -0,0 +1,114 @@ +package sqltypes + +import ( + "fmt" + "strings" + + "github.com/open-policy-agent/opa/ast" + "golang.org/x/xerrors" +) + +var ( + _ VariableMatcher = astUUIDVar{} + _ Node = astUUIDVar{} + _ SupportsEquality = astUUIDVar{} +) + +// astUUIDVar is a variable that represents a UUID column. Unlike +// astStringVar it emits native UUID comparisons (column = 'val'::uuid) +// instead of text-based ones (COALESCE(column::text, ”) = 'val'). +// This allows PostgreSQL to use indexes on UUID columns. +type astUUIDVar struct { + Source RegoSource + FieldPath []string + ColumnString string +} + +func UUIDVarMatcher(sqlColumn string, regoPath []string) VariableMatcher { + return astUUIDVar{FieldPath: regoPath, ColumnString: sqlColumn} +} + +func (astUUIDVar) UseAs() Node { return astUUIDVar{} } + +func (u astUUIDVar) ConvertVariable(rego ast.Ref) (Node, bool) { + left, err := RegoVarPath(u.FieldPath, rego) + if err == nil && len(left) == 0 { + return astUUIDVar{ + Source: RegoSource(rego.String()), + FieldPath: u.FieldPath, + ColumnString: u.ColumnString, + }, true + } + + return nil, false +} + +func (u astUUIDVar) SQLString(_ *SQLGenerator) string { + return u.ColumnString +} + +// EqualsSQLString handles equality comparisons for UUID columns. +// Rego always produces string literals, so we accept AstString and +// cast the literal to ::uuid in the output SQL. This lets PG use +// native UUID indexes instead of falling back to text comparisons. +// nolint:revive +func (u astUUIDVar) EqualsSQLString(cfg *SQLGenerator, not bool, other Node) (string, error) { + switch other.UseAs().(type) { + case AstString: + // The other side is a rego string literal like + // "8c0b9bdc-a013-4b14-a49b-5747bc335708". Emit a comparison + // that casts the literal to uuid so PG can use indexes: + // column = 'val'::uuid + // instead of the text-based: + // 'val' = COALESCE(column::text, '') + s, ok := other.(AstString) + if !ok { + return "", xerrors.Errorf("expected AstString, got %T", other) + } + if s.Value == "" { + // Empty string in rego means "no value". Compare the + // column against NULL since UUID columns represent + // absent values as NULL, not empty strings. + op := "IS NULL" + if not { + op = "IS NOT NULL" + } + return fmt.Sprintf("%s %s", u.ColumnString, op), nil + } + return fmt.Sprintf("%s %s '%s'::uuid", + u.ColumnString, equalsOp(not), s.Value), nil + case astUUIDVar: + return basicSQLEquality(cfg, not, u, other), nil + default: + return "", xerrors.Errorf("unsupported equality: %T %s %T", + u, equalsOp(not), other) + } +} + +// ContainedInSQL implements SupportsContainedIn so that a UUID column +// can appear in membership checks like `col = ANY(ARRAY[...])`. The +// array elements are rego strings, so we cast each to ::uuid. +func (u astUUIDVar) ContainedInSQL(_ *SQLGenerator, haystack Node) (string, error) { + arr, ok := haystack.(ASTArray) + if !ok { + return "", xerrors.Errorf("unsupported containedIn: %T in %T", u, haystack) + } + + if len(arr.Value) == 0 { + return "false", nil + } + + // Build ARRAY['uuid1'::uuid, 'uuid2'::uuid, ...] + values := make([]string, 0, len(arr.Value)) + for _, v := range arr.Value { + s, ok := v.(AstString) + if !ok { + return "", xerrors.Errorf("expected AstString array element, got %T", v) + } + values = append(values, fmt.Sprintf("'%s'::uuid", s.Value)) + } + + return fmt.Sprintf("%s = ANY(ARRAY [%s])", + u.ColumnString, + strings.Join(values, ",")), nil +} diff --git a/coderd/searchquery/search.go b/coderd/searchquery/search.go index 59ec3e04923..af5b82f89e6 100644 --- a/coderd/searchquery/search.go +++ b/coderd/searchquery/search.go @@ -67,7 +67,7 @@ func AuditLogs(ctx context.Context, db database.Store, query string) (database.G } // Prepare the count filter, which uses the same parameters as the GetAuditLogsOffsetParams. - // nolint:exhaustruct // UserID is not obtained from the query parameters. + // nolint:exhaustruct // UserID and CountCap are not obtained from the query parameters. countFilter := database.CountAuditLogsParams{ RequestID: filter.RequestID, ResourceID: filter.ResourceID, @@ -124,6 +124,7 @@ func ConnectionLogs(ctx context.Context, db database.Store, query string, apiKey } // This MUST be kept in sync with the above + // nolint:exhaustruct // CountCap is not obtained from the query parameters. countFilter := database.CountConnectionLogsParams{ OrganizationID: filter.OrganizationID, WorkspaceOwner: filter.WorkspaceOwner, diff --git a/coderd/tailnet.go b/coderd/tailnet.go index cdcf657fe73..e4b7a120e5b 100644 --- a/coderd/tailnet.go +++ b/coderd/tailnet.go @@ -293,6 +293,7 @@ func (s *ServerTailnet) AgentConn(ctx context.Context, agentID uuid.UUID) (works conn = workspacesdk.NewAgentConn(s.conn, workspacesdk.AgentConnOptions{ AgentID: agentID, CloseFunc: func() error { return workspacesdk.ErrSkipClose }, + Logger: s.logger, }) // Since we now have an open conn, be careful to close it if we error diff --git a/coderd/taskname/taskname.go b/coderd/taskname/taskname.go index 3aabd8bf335..321a435303f 100644 --- a/coderd/taskname/taskname.go +++ b/coderd/taskname/taskname.go @@ -14,10 +14,10 @@ import ( "github.com/anthropics/anthropic-sdk-go" anthropicoption "github.com/anthropics/anthropic-sdk-go/option" - "github.com/moby/moby/pkg/namesgenerator" "golang.org/x/xerrors" "github.com/coder/aisdk-go" + "github.com/coder/coder/v2/coderd/util/namesgenerator" strutil "github.com/coder/coder/v2/coderd/util/strings" "github.com/coder/coder/v2/codersdk" ) @@ -114,10 +114,7 @@ func generateFallback() TaskName { // We have a 32 character limit for the name. // We have a 5 character suffix `-ffff`. // This leaves us with 27 characters for the name. - // - // `namesgenerator.GetRandomName(0)` can generate names - // up to 27 characters, but we truncate defensively. - name := strings.ReplaceAll(namesgenerator.GetRandomName(0), "_", "-") + name := namesgenerator.NameWith("-") name = name[:min(len(name), 27)] name = strings.TrimSuffix(name, "-") @@ -169,7 +166,7 @@ func generateFromPrompt(prompt string) (TaskName, error) { // Ensure display name is never empty displayName = strings.ReplaceAll(name, "-", " ") } - displayName = strings.ToUpper(displayName[:1]) + displayName[1:] + displayName = strutil.Capitalize(displayName) return TaskName{ Name: taskName, @@ -261,7 +258,7 @@ func generateFromAnthropic(ctx context.Context, prompt string, apiKey string, mo // Ensure display name is never empty displayName = strings.ReplaceAll(taskNameResponse.Name, "-", " ") } - displayName = strings.ToUpper(displayName[:1]) + displayName[1:] + displayName = strutil.Capitalize(displayName) return TaskName{ Name: name, diff --git a/coderd/taskname/taskname_test.go b/coderd/taskname/taskname_test.go index 31433370924..aab53ca5f6f 100644 --- a/coderd/taskname/taskname_test.go +++ b/coderd/taskname/taskname_test.go @@ -49,6 +49,19 @@ func TestGenerate(t *testing.T) { require.NotEmpty(t, taskName.DisplayName) }) + t.Run("FromPromptMultiByte", func(t *testing.T) { + t.Setenv("ANTHROPIC_API_KEY", "") + + ctx := testutil.Context(t, testutil.WaitShort) + + taskName := taskname.Generate(ctx, testutil.Logger(t), "über cool feature") + + require.NoError(t, codersdk.NameValid(taskName.Name)) + require.True(t, len(taskName.DisplayName) > 0) + // The display name must start with "Ü", not corrupted bytes. + require.Equal(t, "Über cool feature", taskName.DisplayName) + }) + t.Run("Fallback", func(t *testing.T) { // Ensure no API key t.Setenv("ANTHROPIC_API_KEY", "") diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 13dd93d5287..d2b9a1b56fa 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -16,7 +16,6 @@ import ( "github.com/go-chi/chi/v5" "github.com/google/uuid" - "github.com/moby/moby/pkg/namesgenerator" "github.com/sqlc-dev/pqtype" "github.com/zclconf/go-cty/cty" "golang.org/x/xerrors" @@ -39,6 +38,7 @@ import ( "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/rbac/policy" "github.com/coder/coder/v2/coderd/tracing" + "github.com/coder/coder/v2/coderd/util/namesgenerator" "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/examples" @@ -1700,7 +1700,7 @@ func (api *API) postTemplateVersionsByOrganization(rw http.ResponseWriter, r *ht } if req.Name == "" { - req.Name = namesgenerator.GetRandomName(1) + req.Name = namesgenerator.NameDigitWith("_") } err = tx.InsertTemplateVersion(ctx, database.InsertTemplateVersionParams{ diff --git a/coderd/templateversions_test.go b/coderd/templateversions_test.go index f282f8420b5..5b5078b0545 100644 --- a/coderd/templateversions_test.go +++ b/coderd/templateversions_test.go @@ -693,7 +693,7 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) { } `, }, - expectError: "Undefined Parameter", + expectError: "", // Presets are not validated unless they are for a prebuild }, } { t.Run(tt.name, func(t *testing.T) { diff --git a/coderd/userauth.go b/coderd/userauth.go index 91472996737..ed4b2f8a5af 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -3,6 +3,7 @@ package coderd import ( "context" "database/sql" + "encoding/json" "errors" "fmt" "net/http" @@ -19,33 +20,33 @@ import ( "github.com/go-jose/go-jose/v4/jwt" "github.com/google/go-github/v43/github" "github.com/google/uuid" - "github.com/moby/moby/pkg/namesgenerator" "golang.org/x/oauth2" "golang.org/x/xerrors" "cdr.dev/slog" - "github.com/coder/coder/v2/coderd/cryptokeys" - "github.com/coder/coder/v2/coderd/idpsync" - "github.com/coder/coder/v2/coderd/jwtutils" - "github.com/coder/coder/v2/coderd/telemetry" - "github.com/coder/coder/v2/coderd/util/ptr" - "github.com/coder/coder/v2/coderd/apikey" "github.com/coder/coder/v2/coderd/audit" + "github.com/coder/coder/v2/coderd/cryptokeys" "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbtime" "github.com/coder/coder/v2/coderd/externalauth" "github.com/coder/coder/v2/coderd/httpapi" "github.com/coder/coder/v2/coderd/httpmw" + "github.com/coder/coder/v2/coderd/idpsync" + "github.com/coder/coder/v2/coderd/jwtutils" "github.com/coder/coder/v2/coderd/notifications" "github.com/coder/coder/v2/coderd/promoauth" "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/render" + "github.com/coder/coder/v2/coderd/telemetry" "github.com/coder/coder/v2/coderd/userpassword" + "github.com/coder/coder/v2/coderd/util/namesgenerator" + "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/cryptorand" + "github.com/coder/coder/v2/site" ) type MergedClaimsSource string @@ -1032,7 +1033,16 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) { }) return } - user, link, err := findLinkedUser(ctx, api.Database, githubLinkedID(ghUser), verifiedEmail.GetEmail()) + user, link, err := findLinkedUser(ctx, api.Database, githubLinkedID(ghUser), database.LoginTypeGithub, verifiedEmail.GetEmail()) + if errors.Is(err, errLinkedIDAlreadyBound) { + logger.Warn(ctx, "oauth2: blocked login, account already linked to different identity", + slog.F("email", verifiedEmail.GetEmail()), + ) + httpapi.Write(ctx, rw, http.StatusForbidden, codersdk.Response{ + Message: "This account is already linked to a different identity provider subject.", + }) + return + } if err != nil { logger.Error(ctx, "oauth2: unable to find linked user", slog.F("gh_user", ghUser.Name), slog.Error(err)) httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ @@ -1326,18 +1336,38 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) { return } - verifiedRaw, ok := mergedClaims["email_verified"] - if ok { - verified, ok := verifiedRaw.(bool) - if ok && !verified { - if !api.OIDCConfig.IgnoreEmailVerified { - httpapi.Write(ctx, rw, http.StatusForbidden, codersdk.Response{ - Message: fmt.Sprintf("Verify the %q email address on your OIDC provider to authenticate!", email), - }) - return - } - logger.Warn(ctx, "allowing unverified oidc email %q") + // Determine whether the email is verified. Default to unverified + // so that a missing claim or an unrecognized type is fail-closed. + emailVerified := false + verifiedRaw, hasVerifiedClaim := mergedClaims["email_verified"] + if hasVerifiedClaim { + v, coerceOK := coerceEmailVerified(verifiedRaw) + if coerceOK { + emailVerified = v + } else { + logger.Warn(ctx, "unrecognized email_verified claim type, treating as unverified", + slog.F("type", fmt.Sprintf("%T", verifiedRaw)), + slog.F("value", verifiedRaw), + ) + } + } + + if !emailVerified { + if !api.OIDCConfig.IgnoreEmailVerified { + site.RenderStaticErrorPage(rw, r, site.ErrorPageData{ + Status: http.StatusForbidden, + HideStatus: true, + Title: "Email not verified", + Description: fmt.Sprintf( + "Verify the %q email address on your OIDC provider to authenticate!", + email, + ), + AdditionalButtonLink: "/login", + AdditionalButtonText: "Back to login", + }) + return } + logger.Warn(ctx, "allowing unverified oidc email", slog.F("email", email)) } // The username is a required property in Coder. We make a best-effort @@ -1397,7 +1427,21 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) { ctx = slog.With(ctx, slog.F("email", email), slog.F("username", username), slog.F("name", name)) - user, link, err := findLinkedUser(ctx, api.Database, oidcLinkedID(idToken), email) + user, link, err := findLinkedUser(ctx, api.Database, oidcLinkedID(idToken), database.LoginTypeOIDC, email) + if errors.Is(err, errLinkedIDAlreadyBound) { + logger.Warn(ctx, "oauth2: blocked login, account already linked to different identity", + slog.F("email", email), + ) + site.RenderStaticErrorPage(rw, r, site.ErrorPageData{ + Status: http.StatusForbidden, + HideStatus: true, + Title: "Account already linked", + Description: "This account is already linked to a different identity provider subject. Contact your administrator.", + AdditionalButtonLink: "/login", + AdditionalButtonText: "Back to login", + }) + return + } if err != nil { logger.Error(ctx, "oauth2: unable to find linked user", slog.F("email", email), slog.Error(err)) httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ @@ -1711,7 +1755,7 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C validUsername bool ) for i := 0; i < 10; i++ { - alternate := fmt.Sprintf("%s-%s", original, namesgenerator.GetRandomName(1)) + alternate := fmt.Sprintf("%s-%s", original, namesgenerator.NameDigitWith("_")) params.Username = codersdk.UsernameFrom(alternate) @@ -1830,6 +1874,31 @@ func (api *API) oauthLogin(r *http.Request, params *oauthLoginParams) ([]*http.C if err != nil { return xerrors.Errorf("update user link: %w", err) } + + // Defense-in-depth: if a concurrent transaction backfilled + // linked_id between findLinkedUser and this point, reject the + // login with a 403 instead of letting it bubble up as a 500. + if link.LinkedID != "" && link.LinkedID != params.LinkedID { + return &idpsync.HTTPError{ + Code: http.StatusForbidden, + Msg: "Account already linked", + Detail: "This account is already linked to a different identity provider subject. Contact your administrator.", + RenderStaticPage: true, + } + } + + // Backfill linked_id for legacy links. + if link.LinkedID == "" && params.LinkedID != "" { + //nolint:gocritic // System needs to update the user link. + link, err = tx.UpdateUserLinkedID(dbauthz.AsSystemRestricted(ctx), database.UpdateUserLinkedIDParams{ + LinkedID: params.LinkedID, + UserID: user.ID, + LoginType: params.LoginType, + }) + if err != nil { + return xerrors.Errorf("backfill user linked id: %w", err) + } + } } err = api.IDPSync.SyncOrganizations(ctx, tx, user, params.OrganizationSync) @@ -2050,9 +2119,17 @@ func oidcLinkedID(tok *oidc.IDToken) string { return strings.Join([]string{tok.Issuer, tok.Subject}, "||") } +// errLinkedIDAlreadyBound is returned by findLinkedUser when the user +// found by email already has a user_link with a different linked_id. +var errLinkedIDAlreadyBound = xerrors.New("user account is already linked to a different identity provider subject") + // findLinkedUser tries to find a user by their unique OAuth-linked ID. -// If it doesn't not find it, it returns the user by their email. -func findLinkedUser(ctx context.Context, db database.Store, linkedID string, emails ...string) (database.User, database.UserLink, error) { +// If it does not find a match, it falls back to email-based lookup. +// The email fallback is restricted to first-time account linking and +// legacy links (empty linked_id) only. If the user found by email +// already has a link with a different linked_id, errLinkedIDAlreadyBound +// is returned to prevent account takeover via IdP email reuse. +func findLinkedUser(ctx context.Context, db database.Store, linkedID string, loginType database.LoginType, emails ...string) (database.User, database.UserLink, error) { var ( user database.User link database.UserLink @@ -2097,12 +2174,19 @@ func findLinkedUser(ctx context.Context, db database.Store, linkedID string, ema // possible that a user_link exists without a populated 'linked_id'. link, err = db.GetUserLinkByUserIDLoginType(ctx, database.GetUserLinkByUserIDLoginTypeParams{ UserID: user.ID, - LoginType: user.LoginType, + LoginType: loginType, }) if err != nil && !errors.Is(err, sql.ErrNoRows) { return database.User{}, database.UserLink{}, xerrors.Errorf("get user link by user id and login type: %w", err) } + // Block email fallback when an existing link has a different linked_id. + // Prevents account takeover via IdP email reuse; first-time and legacy + // (empty linked_id) links pass through. + if err == nil && link.LinkedID != "" && link.LinkedID != linkedID { + return database.User{}, database.UserLink{}, errLinkedIDAlreadyBound + } + return user, link, nil } @@ -2131,3 +2215,39 @@ func wrongLoginTypeHTTPError(user database.LoginType, params database.LoginType) params, user, addedMsg), } } + +// coerceEmailVerified attempts to convert an OIDC email_verified claim to a +// boolean. Some IdPs (e.g. SAML-to-OIDC bridges, certain Azure AD B2C +// configurations) return email_verified as a string ("true"/"false") or a +// number (1/0) rather than a native JSON boolean. This function handles +// those variants so that non-bool representations cannot silently bypass +// the verification check. +// +// Returns (value, true) on successful coercion, or (false, false) if the +// value is nil or an unrecognized type. +func coerceEmailVerified(v interface{}) (verified bool, ok bool) { + switch val := v.(type) { + case bool: + return val, true + case string: + b, err := strconv.ParseBool(val) + if err != nil { + return false, false + } + return b, true + case json.Number: + n, err := val.Int64() + if err != nil { + return false, false + } + return n != 0, true + case float64: + return val != 0, true + case int64: + return val != 0, true + case int: + return val != 0, true + default: + return false, false + } +} diff --git a/coderd/userauth_internal_test.go b/coderd/userauth_internal_test.go new file mode 100644 index 00000000000..47e1883b52b --- /dev/null +++ b/coderd/userauth_internal_test.go @@ -0,0 +1,65 @@ +package coderd + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCoerceEmailVerified(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + input interface{} + wantBool bool + wantOK bool + }{ + // Native booleans + {name: "BoolTrue", input: true, wantBool: true, wantOK: true}, + {name: "BoolFalse", input: false, wantBool: false, wantOK: true}, + + // Strings + {name: "StringTrue", input: "true", wantBool: true, wantOK: true}, + {name: "StringFalse", input: "false", wantBool: false, wantOK: true}, + {name: "StringOne", input: "1", wantBool: true, wantOK: true}, + {name: "StringZero", input: "0", wantBool: false, wantOK: true}, + {name: "StringTRUE", input: "TRUE", wantBool: true, wantOK: true}, + {name: "StringFALSE", input: "FALSE", wantBool: false, wantOK: true}, + {name: "StringT", input: "t", wantBool: true, wantOK: true}, + {name: "StringF", input: "f", wantBool: false, wantOK: true}, + {name: "StringInvalid", input: "invalid", wantBool: false, wantOK: false}, + {name: "StringEmpty", input: "", wantBool: false, wantOK: false}, + + // json.Number (when decoder uses UseNumber) + {name: "JSONNumberOne", input: json.Number("1"), wantBool: true, wantOK: true}, + {name: "JSONNumberZero", input: json.Number("0"), wantBool: false, wantOK: true}, + {name: "JSONNumberInvalid", input: json.Number("abc"), wantBool: false, wantOK: false}, + + // float64 (default JSON numeric type) + {name: "Float64One", input: float64(1), wantBool: true, wantOK: true}, + {name: "Float64Zero", input: float64(0), wantBool: false, wantOK: true}, + + // Integer types + {name: "IntOne", input: int(1), wantBool: true, wantOK: true}, + {name: "IntZero", input: int(0), wantBool: false, wantOK: true}, + {name: "Int64One", input: int64(1), wantBool: true, wantOK: true}, + {name: "Int64Zero", input: int64(0), wantBool: false, wantOK: true}, + + // Nil and unsupported types + {name: "Nil", input: nil, wantBool: false, wantOK: false}, + {name: "Slice", input: []string{}, wantBool: false, wantOK: false}, + {name: "Map", input: map[string]string{}, wantBool: false, wantOK: false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + gotBool, gotOK := coerceEmailVerified(tc.input) + assert.Equal(t, tc.wantBool, gotBool, "bool value mismatch") + assert.Equal(t, tc.wantOK, gotOK, "ok value mismatch") + }) + } +} diff --git a/coderd/userauth_test.go b/coderd/userauth_test.go index 86fe30bf3c0..1d508058e63 100644 --- a/coderd/userauth_test.go +++ b/coderd/userauth_test.go @@ -386,6 +386,67 @@ func TestUserOAuth2Github(t *testing.T) { require.Equal(t, http.StatusForbidden, resp.StatusCode) }) + t.Run("EmailFallbackBlockedByExistingLink", func(t *testing.T) { + t.Parallel() + + // A victim already has a GitHub link bound to a specific GitHub user + // ID. An attacker authenticates with a different GitHub user ID but + // the victim's verified email. The email fallback must not hand the + // attacker the victim's account, even with signups enabled. + owner, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{ + GithubOAuth2Config: &coderd.GithubOAuth2Config{ + OAuth2Config: &testutil.OAuth2Config{}, + AllowSignups: true, + AllowEveryone: true, + ListOrganizationMemberships: func(_ context.Context, _ *http.Client) ([]*github.Membership, error) { + return []*github.Membership{}, nil + }, + TeamMembership: func(_ context.Context, _ *http.Client, _, _, _ string) (*github.Membership, error) { + return nil, xerrors.New("no teams") + }, + AuthenticatedUser: func(_ context.Context, _ *http.Client) (*github.User, error) { + // Attacker's GitHub ID differs from the victim's link. + return &github.User{ + ID: github.Int64(200), + Login: github.String("attacker"), + Name: github.String("Attacker"), + }, nil + }, + ListEmails: func(_ context.Context, _ *http.Client) ([]*github.UserEmail, error) { + return []*github.UserEmail{{ + Email: github.String("victim@coder.com"), + Verified: github.Bool(true), + Primary: github.Bool(true), + }}, nil + }, + }, + }) + + // Seed the victim with an existing GitHub link (a different linked_id). + victim := dbgen.User(t, db, database.User{ + Email: "victim@coder.com", + LoginType: database.LoginTypeGithub, + }) + const victimLinkedID = "100" + dbgen.UserLink(t, db, database.UserLink{ + UserID: victim.ID, + LoginType: database.LoginTypeGithub, + LinkedID: victimLinkedID, + }) + + resp := oauth2Callback(t, owner) + require.Equal(t, http.StatusForbidden, resp.StatusCode, + "attacker with a different GitHub ID must not authenticate as the victim") + + // The victim's link must be untouched. + victimLink, err := db.GetUserLinkByUserIDLoginType(dbauthz.AsSystemRestricted(context.Background()), database.GetUserLinkByUserIDLoginTypeParams{ + UserID: victim.ID, + LoginType: database.LoginTypeGithub, + }) + require.NoError(t, err) + require.Equal(t, victimLinkedID, victimLink.LinkedID, + "victim's linked_id must remain unchanged") + }) t.Run("Signup", func(t *testing.T) { t.Parallel() auditor := audit.NewMock() @@ -1062,7 +1123,8 @@ func TestUserOIDC(t *testing.T) { "sub": uuid.NewString(), }, AccessTokenClaims: jwt.MapClaims{ - "email": "kyle@kwc.io", + "email": "kyle@kwc.io", + "email_verified": true, }, IgnoreUserInfo: true, AllowSignups: true, @@ -1085,8 +1147,22 @@ func TestUserOIDC(t *testing.T) { { Name: "EmailOnly", IDTokenClaims: jwt.MapClaims{ - "email": "kyle@kwc.io", - "sub": uuid.NewString(), + "email": "kyle@kwc.io", + "email_verified": true, + "sub": uuid.NewString(), + }, + AllowSignups: true, + StatusCode: http.StatusOK, + AssertUser: func(t testing.TB, u codersdk.User) { + assert.Equal(t, "kyle", u.Username) + }, + }, + { + Name: "EmailVerifiedAsStringTrue", + IDTokenClaims: jwt.MapClaims{ + "email": "kyle@kwc.io", + "email_verified": "true", + "sub": uuid.NewString(), }, AllowSignups: true, StatusCode: http.StatusOK, @@ -1094,6 +1170,16 @@ func TestUserOIDC(t *testing.T) { assert.Equal(t, "kyle", u.Username) }, }, + { + Name: "EmailVerifiedAsStringFalse", + IDTokenClaims: jwt.MapClaims{ + "email": "kyle@kwc.io", + "email_verified": "false", + "sub": uuid.NewString(), + }, + AllowSignups: true, + StatusCode: http.StatusForbidden, + }, { Name: "EmailNotVerified", IDTokenClaims: jwt.MapClaims{ @@ -1316,6 +1402,7 @@ func TestUserOIDC(t *testing.T) { // See: https://github.com/coder/coder/issues/4472 Name: "UsernameIsEmail", IDTokenClaims: jwt.MapClaims{ + "email_verified": true, "preferred_username": "kyle@kwc.io", "sub": uuid.NewString(), }, @@ -1365,9 +1452,10 @@ func TestUserOIDC(t *testing.T) { { Name: "GroupsDoesNothing", IDTokenClaims: jwt.MapClaims{ - "email": "coolin@coder.com", - "groups": []string{"pingpong"}, - "sub": uuid.NewString(), + "email": "coolin@coder.com", + "email_verified": true, + "groups": []string{"pingpong"}, + "sub": uuid.NewString(), }, AllowSignups: true, StatusCode: http.StatusOK, @@ -1540,6 +1628,57 @@ func TestUserOIDC(t *testing.T) { }) } + // Absent email_verified claim tests use a FakeIDP that suppresses the + // default email_verified=true injection so the handler's absent-claim + // branch is exercised end-to-end. + t.Run("EmailVerifiedMissing", func(t *testing.T) { + t.Parallel() + fake := oidctest.NewFakeIDP(t, + oidctest.WithRefresh(func(_ string) error { + return xerrors.New("refreshing token should never occur") + }), + oidctest.WithServing(), + oidctest.WithOmitEmailVerifiedDefault(), + ) + cfg := fake.OIDCConfig(t, nil, func(cfg *coderd.OIDCConfig) { + cfg.AllowSignups = true + }) + client := coderdtest.New(t, &coderdtest.Options{ + OIDCConfig: cfg, + }) + _, resp := fake.AttemptLogin(t, client, jwt.MapClaims{ + "email": "kyle@kwc.io", + "sub": uuid.NewString(), + }) + require.Equal(t, http.StatusForbidden, resp.StatusCode) + }) + + t.Run("EmailVerifiedMissingIgnored", func(t *testing.T) { + t.Parallel() + fake := oidctest.NewFakeIDP(t, + oidctest.WithRefresh(func(_ string) error { + return xerrors.New("refreshing token should never occur") + }), + oidctest.WithServing(), + oidctest.WithOmitEmailVerifiedDefault(), + ) + cfg := fake.OIDCConfig(t, nil, func(cfg *coderd.OIDCConfig) { + cfg.AllowSignups = true + cfg.IgnoreEmailVerified = true + }) + client := coderdtest.New(t, &coderdtest.Options{ + OIDCConfig: cfg, + }) + userClient, _ := fake.Login(t, client, jwt.MapClaims{ + "email": "kyle@kwc.io", + "sub": uuid.NewString(), + }) + ctx := testutil.Context(t, testutil.WaitShort) + user, err := userClient.User(ctx, "me") + require.NoError(t, err) + require.Equal(t, "kyle", user.Username) + }) + t.Run("OIDCDormancy", func(t *testing.T) { t.Parallel() ctx := testutil.Context(t, testutil.WaitShort) @@ -1569,8 +1708,9 @@ func TestUserOIDC(t *testing.T) { auditor.ResetLogs() client, resp := fake.AttemptLogin(t, owner, jwt.MapClaims{ - "email": user.Email, - "sub": uuid.NewString(), + "email": user.Email, + "email_verified": true, + "sub": uuid.NewString(), }) require.Equal(t, http.StatusOK, resp.StatusCode) @@ -1584,6 +1724,244 @@ func TestUserOIDC(t *testing.T) { require.Equal(t, codersdk.UserStatusActive, me.Status) }) + // Tests that an attacker with a different OIDC subject but the same + // email cannot hijack an existing linked account. The email fallback + // must be restricted to first-time linking only. + t.Run("OIDCEmailFallbackBlockedByExistingLink", func(t *testing.T) { + t.Parallel() + + fake := oidctest.NewFakeIDP(t, + oidctest.WithRefresh(func(_ string) error { + return xerrors.New("refreshing token should never occur") + }), + oidctest.WithServing(), + ) + + for _, tc := range []struct { + name string + allowSignups bool + }{ + {"SignupsDisabled", false}, + {"SignupsEnabled", true}, + } { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + cfg := fake.OIDCConfig(t, nil, func(cfg *coderd.OIDCConfig) { + cfg.AllowSignups = tc.allowSignups + }) + + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) + owner, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{ + OIDCConfig: cfg, + Logger: &logger, + }) + + // Create a victim user with an existing OIDC link. + // Use the fake IDP's issuer so the linked_id format is + // realistic (same issuer, different subject). + victim := dbgen.User(t, db, database.User{ + LoginType: database.LoginTypeOIDC, + }) + victimLinkedID := fake.IssuerURL().String() + "||" + "victim-subject" + dbgen.UserLink(t, db, database.UserLink{ + UserID: victim.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: victimLinkedID, + }) + + // Attacker tries to login with a different subject but the + // same email. The email fallback is blocked because the victim + // already has a user_link with a different linked_id. + _, resp := fake.AttemptLogin(t, owner, jwt.MapClaims{ + "email": victim.Email, + "sub": "attacker-subject", + }) + require.Equal(t, http.StatusForbidden, resp.StatusCode, + "attacker must not authenticate as the victim") + + // Verify the victim's link is unchanged. + victimLink, err := db.GetUserLinkByUserIDLoginType(dbauthz.AsSystemRestricted(context.Background()), database.GetUserLinkByUserIDLoginTypeParams{ + UserID: victim.ID, + LoginType: database.LoginTypeOIDC, + }) + require.NoError(t, err) + require.Equal(t, victimLinkedID, victimLink.LinkedID, + "victim's linked_id must remain unchanged") + }) + } + }) + + // Tests that a first-time OIDC user can still link via email when no + // user_link exists (e.g. a dormant OIDC user created via SCIM or API). + t.Run("OIDCFirstTimeLinkByEmailAllowed", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + fake := oidctest.NewFakeIDP(t, + oidctest.WithRefresh(func(_ string) error { + return xerrors.New("refreshing token should never occur") + }), + oidctest.WithServing(), + ) + cfg := fake.OIDCConfig(t, nil, func(cfg *coderd.OIDCConfig) { + cfg.AllowSignups = true + }) + + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) + owner, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{ + OIDCConfig: cfg, + Logger: &logger, + }) + + // Create a user with OIDC login type but NO user_link. + // This simulates a user created via SCIM or the API. + user := dbgen.User(t, db, database.User{ + LoginType: database.LoginTypeOIDC, + }) + + // Login with a new OIDC subject and matching email. + // This should succeed because no user_link exists. + sub := uuid.NewString() + client, resp := fake.AttemptLogin(t, owner, jwt.MapClaims{ + "email": user.Email, + "sub": sub, + }) + require.Equal(t, http.StatusOK, resp.StatusCode) + + me, err := client.User(ctx, "me") + require.NoError(t, err) + require.Equal(t, user.ID, me.ID, + "should authenticate as the existing user") + + // Verify the created link has a populated linked_id. + link, err := db.GetUserLinkByUserIDLoginType( + dbauthz.AsSystemRestricted(context.Background()), + database.GetUserLinkByUserIDLoginTypeParams{ + UserID: user.ID, + LoginType: database.LoginTypeOIDC, + }) + require.NoError(t, err) + expectedLinkedID := fake.IssuerURL().String() + "||" + sub + require.Equal(t, expectedLinkedID, link.LinkedID, + "link should have the correct linked_id after first-time linking") + }) + + // Tests that a legacy user with an empty linked_id can still login + // and that their linked_id is backfilled with the correct value. + t.Run("OIDCLegacyLinkBackfill", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + fake := oidctest.NewFakeIDP(t, + oidctest.WithRefresh(func(_ string) error { + return xerrors.New("refreshing token should never occur") + }), + oidctest.WithServing(), + ) + cfg := fake.OIDCConfig(t, nil, func(cfg *coderd.OIDCConfig) { + cfg.AllowSignups = true + }) + + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) + owner, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{ + OIDCConfig: cfg, + Logger: &logger, + }) + + // Create a legacy user with an empty linked_id. + user := dbgen.User(t, db, database.User{ + LoginType: database.LoginTypeOIDC, + }) + dbgen.UserLink(t, db, database.UserLink{ + UserID: user.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: "", // Legacy: empty linked_id + }) + + sub := uuid.NewString() + client, resp := fake.AttemptLogin(t, owner, jwt.MapClaims{ + "email": user.Email, + "sub": sub, + }) + require.Equal(t, http.StatusOK, resp.StatusCode) + + me, err := client.User(ctx, "me") + require.NoError(t, err) + require.Equal(t, user.ID, me.ID, + "legacy user should still be able to login via email fallback") + + // Verify the linked_id was backfilled with the correct value. + link, err := db.GetUserLinkByUserIDLoginType( + dbauthz.AsSystemRestricted(context.Background()), + database.GetUserLinkByUserIDLoginTypeParams{ + UserID: user.ID, + LoginType: database.LoginTypeOIDC, + }) + require.NoError(t, err) + expectedLinkedID := fake.IssuerURL().String() + "||" + sub + require.Equal(t, expectedLinkedID, link.LinkedID, + "linked_id should be backfilled with the correct value after login") + }) + + // Tests that changing the OIDC issuer URL blocks an existing user whose + // linked_id was recorded under the old issuer. This is a deliberate + // breaking change: before this fix the email fallback silently rescued + // such users. Now the login is rejected because the existing link's + // linked_id (old issuer) differs from the newly computed one (new issuer). + t.Run("OIDCEmailFallbackBlockedByIssuerChange", func(t *testing.T) { + t.Parallel() + ctx := testutil.Context(t, testutil.WaitShort) + + fake := oidctest.NewFakeIDP(t, + oidctest.WithRefresh(func(_ string) error { + return xerrors.New("refreshing token should never occur") + }), + oidctest.WithServing(), + ) + cfg := fake.OIDCConfig(t, nil, func(cfg *coderd.OIDCConfig) { + cfg.AllowSignups = true + }) + + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) + owner, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{ + OIDCConfig: cfg, + Logger: &logger, + }) + + // Seed a user whose link was created under a different (old) issuer + // but with the same subject the IdP presents on login. + user := dbgen.User(t, db, database.User{ + LoginType: database.LoginTypeOIDC, + }) + const sub = "stable-subject" + oldLinkedID := "https://old-issuer.example.com||" + sub + dbgen.UserLink(t, db, database.UserLink{ + UserID: user.ID, + LoginType: database.LoginTypeOIDC, + LinkedID: oldLinkedID, + }) + + // Login presents the same subject but the current issuer, so the + // computed linked_id differs from the stored one and is blocked. + _, resp := fake.AttemptLogin(t, owner, jwt.MapClaims{ + "email": user.Email, + "sub": sub, + }) + require.Equal(t, http.StatusForbidden, resp.StatusCode, + "issuer change must block the email fallback for an existing link") + + // The stored link must remain unchanged. + link, err := db.GetUserLinkByUserIDLoginType(dbauthz.AsSystemRestricted(ctx), database.GetUserLinkByUserIDLoginTypeParams{ + UserID: user.ID, + LoginType: database.LoginTypeOIDC, + }) + require.NoError(t, err) + require.Equal(t, oldLinkedID, link.LinkedID, + "linked_id must not be modified when the login is blocked") + }) + t.Run("OIDCConvert", func(t *testing.T) { t.Parallel() @@ -1608,8 +1986,9 @@ func TestUserOIDC(t *testing.T) { require.Equal(t, codersdk.LoginTypePassword, userData.LoginType) claims := jwt.MapClaims{ - "email": userData.Email, - "sub": uuid.NewString(), + "email": userData.Email, + "email_verified": true, + "sub": uuid.NewString(), } var err error user.HTTPClient.Jar, err = cookiejar.New(nil) @@ -1679,8 +2058,9 @@ func TestUserOIDC(t *testing.T) { user, userData := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID) claims := jwt.MapClaims{ - "email": userData.Email, - "sub": uuid.NewString(), + "email": userData.Email, + "email_verified": true, + "sub": uuid.NewString(), } user.HTTPClient.Jar, err = cookiejar.New(nil) require.NoError(t, err) @@ -1750,8 +2130,9 @@ func TestUserOIDC(t *testing.T) { numLogs := len(auditor.AuditLogs()) claims := jwt.MapClaims{ - "email": "jon@coder.com", - "sub": uuid.NewString(), + "email": "jon@coder.com", + "email_verified": true, + "sub": uuid.NewString(), } userClient, _ := fake.Login(t, client, claims) @@ -1765,8 +2146,9 @@ func TestUserOIDC(t *testing.T) { // Pass a different subject field so that we prompt creating a // new user userClient, _ = fake.Login(t, client, jwt.MapClaims{ - "email": "jon@example2.com", - "sub": "diff", + "email": "jon@example2.com", + "email_verified": true, + "sub": "diff", }) numLogs++ // add an audit log for login @@ -2117,9 +2499,10 @@ func TestOIDCSkipIssuer(t *testing.T) { ctx := testutil.Context(t, testutil.WaitShort) //nolint:bodyclose userClient, _ := fake.Login(t, owner, jwt.MapClaims{ - "iss": secondaryURLString, - "email": "alice@coder.com", - "sub": uuid.NewString(), + "iss": secondaryURLString, + "email": "alice@coder.com", + "email_verified": true, + "sub": uuid.NewString(), }) found, err := userClient.User(ctx, "me") require.NoError(t, err) diff --git a/coderd/userpassword/userpassword.go b/coderd/userpassword/userpassword.go index 2fb01a76d25..8221ab8343b 100644 --- a/coderd/userpassword/userpassword.go +++ b/coderd/userpassword/userpassword.go @@ -39,11 +39,12 @@ var ( // used. defaultSaltSize = 16 - // The simulated hash is used when trying to simulate password checks for - // users that don't exist. It's meant to preserve the timing of the hash - // comparison. + // The simulated hash is used when comparing against an empty stored hash + // (e.g. nonexistent or SSO users). It hashes a random value generated on + // first use, so no attacker-supplied password can ever match it. It exists + // purely to keep failed comparisons constant-time. simulatedHash = lazy.New(func() string { - h, err := Hash("hunter2") + h, err := Hash(rand.Text()) if err != nil { panic(err) } @@ -72,10 +73,10 @@ func init() { // uses pbkdf2 to ensure FIPS 140-2 compliance. See: // https://csrc.nist.gov/csrc/media/templates/cryptographic-module-validation-program/documents/security-policies/140sp2261.pdf func Compare(hashed string, password string) (bool, error) { - // If the hased password provided is empty, simulate comparing a real hash. + // If the hashed password provided is empty, simulate comparing a real hash + // to preserve timing. The simulated hash is derived from a random value, so + // the comparison below can never succeed. if hashed == "" { - // TODO: this seems ripe for creating a vulnerability where - // hunter2 can log into any account. hashed = simulatedHash.Load() } diff --git a/coderd/userpassword/userpassword_test.go b/coderd/userpassword/userpassword_test.go index 83a3bb532e6..67d66642d5b 100644 --- a/coderd/userpassword/userpassword_test.go +++ b/coderd/userpassword/userpassword_test.go @@ -89,6 +89,30 @@ func TestUserPasswordCompare(t *testing.T) { wantErr: true, wantEqual: false, }, + { + name: "EmptyHashHunter2", + passwordToValidate: "", + password: "hunter2", + shouldHash: false, + wantErr: false, + wantEqual: false, + }, + { + name: "EmptyHashEmptyPassword", + passwordToValidate: "", + password: "", + shouldHash: false, + wantErr: false, + wantEqual: false, + }, + { + name: "EmptyHashArbitraryPassword", + passwordToValidate: "", + password: "anyOtherPassword", + shouldHash: false, + wantErr: false, + wantEqual: false, + }, } for _, tt := range tests { diff --git a/coderd/users.go b/coderd/users.go index 3ac05ff7689..8e614ff10a6 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -1077,6 +1077,74 @@ func (api *API) putUserAppearanceSettings(rw http.ResponseWriter, r *http.Reques }) } +// @Summary Get user preference settings +// @ID get-user-preference-settings +// @Security CoderSessionToken +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Success 200 {object} codersdk.UserPreferenceSettings +// @Router /users/{user}/preferences [get] +func (api *API) userPreferenceSettings(rw http.ResponseWriter, r *http.Request) { + var ( + ctx = r.Context() + user = httpmw.UserParam(r) + ) + + taskAlertDismissed, err := api.Database.GetUserTaskNotificationAlertDismissed(ctx, user.ID) + if err != nil { + if !errors.Is(err, sql.ErrNoRows) { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Error reading user preference settings.", + Detail: err.Error(), + }) + return + } + } + + httpapi.Write(ctx, rw, http.StatusOK, codersdk.UserPreferenceSettings{ + TaskNotificationAlertDismissed: taskAlertDismissed, + }) +} + +// @Summary Update user preference settings +// @ID update-user-preference-settings +// @Security CoderSessionToken +// @Accept json +// @Produce json +// @Tags Users +// @Param user path string true "User ID, name, or me" +// @Param request body codersdk.UpdateUserPreferenceSettingsRequest true "New preference settings" +// @Success 200 {object} codersdk.UserPreferenceSettings +// @Router /users/{user}/preferences [put] +func (api *API) putUserPreferenceSettings(rw http.ResponseWriter, r *http.Request) { + var ( + ctx = r.Context() + user = httpmw.UserParam(r) + ) + + var params codersdk.UpdateUserPreferenceSettingsRequest + if !httpapi.Read(ctx, rw, r, ¶ms) { + return + } + + updatedTaskAlertDismissed, err := api.Database.UpdateUserTaskNotificationAlertDismissed(ctx, database.UpdateUserTaskNotificationAlertDismissedParams{ + UserID: user.ID, + TaskNotificationAlertDismissed: params.TaskNotificationAlertDismissed, + }) + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Internal error updating user task notification alert dismissed.", + Detail: err.Error(), + }) + return + } + + httpapi.Write(ctx, rw, http.StatusOK, codersdk.UserPreferenceSettings{ + TaskNotificationAlertDismissed: updatedTaskAlertDismissed, + }) +} + func isValidFontName(font codersdk.TerminalFontName) bool { return slices.Contains(codersdk.TerminalFontNames, font) } @@ -1112,6 +1180,24 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) { return } + // Only owners can change the password of another owner. + if apiKey.UserID != user.ID && slices.Contains(user.RBACRoles, rbac.RoleOwner().String()) { + actingUser, err := api.Database.GetUserByID(ctx, apiKey.UserID) + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Internal error fetching acting user.", + Detail: err.Error(), + }) + return + } + if !slices.Contains(actingUser.RBACRoles, rbac.RoleOwner().String()) { + httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ + Message: "Only owners can change the password of an owner.", + }) + return + } + } + if !httpapi.Read(ctx, rw, r, ¶ms) { return } diff --git a/coderd/users_test.go b/coderd/users_test.go index b8d74272edd..74c4f3fbbfa 100644 --- a/coderd/users_test.go +++ b/coderd/users_test.go @@ -167,6 +167,59 @@ func TestPostLogin(t *testing.T) { require.Equal(t, database.AuditActionLogin, auditor.AuditLogs()[numLogs-1].Action) }) + // "hunter2" was the input of the previous hardcoded simulated hash, which + // an empty stored hash wrongly matched; this is a regression test. + t.Run("NonexistentUser401", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + _, err := client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{ + Email: "does-not-exist@coder.com", + Password: "hunter2", + }) + var apiErr *codersdk.Error + require.ErrorAs(t, err, &apiErr) + require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) + require.Equal(t, "Incorrect email or password.", apiErr.Message) + }) + + // Attempting built-in login as an SSO user returns a 401 to avoid + // divulging login type. + t.Run("SSOReturns401", func(t *testing.T) { + t.Parallel() + client, db := coderdtest.NewWithDatabase(t, nil) + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + // An SSO user has no password hash stored. Create one directly in the + // database since the API requires OIDC to be configured. dbgen.User + // substitutes a random hash for an empty one, so clear it explicitly. + ssoUser := dbgen.User(t, db, database.User{ + Email: "sso-user@coder.com", + LoginType: database.LoginTypeOIDC, + }) + //nolint:gocritic // Test setup requires a system context to clear the hash. + err := db.UpdateUserHashedPassword(dbauthz.AsSystemRestricted(ctx), database.UpdateUserHashedPasswordParams{ + ID: ssoUser.ID, + HashedPassword: []byte{}, + }) + require.NoError(t, err) + + anonClient := codersdk.New(client.URL) + _, err = anonClient.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{ + Email: ssoUser.Email, + Password: "hunter2", + }) + var apiErr *codersdk.Error + require.ErrorAs(t, err, &apiErr) + require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) + require.Equal(t, "Incorrect email or password.", apiErr.Message) + // The login type must not be leaked. + require.NotContains(t, apiErr.Message, string(codersdk.LoginTypeOIDC)) + }) + t.Run("Suspended", func(t *testing.T) { t.Parallel() auditor := audit.NewMock() @@ -875,8 +928,9 @@ func TestPostUsers(t *testing.T) { // Try to log in with OIDC. userClient, _ := fake.Login(t, client, jwt.MapClaims{ - "email": email, - "sub": uuid.NewString(), + "email": email, + "email_verified": true, + "sub": uuid.NewString(), }) found, err := userClient.User(ctx, "me") @@ -1413,6 +1467,57 @@ func TestUpdateUserPassword(t *testing.T) { require.Equal(t, http.StatusNotFound, cerr.StatusCode()) }) + t.Run("UserAdminCannotResetOwnerPassword", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + owner := coderdtest.CreateFirstUser(t, client) + userAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleUserAdmin()) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + err := userAdmin.UpdateUserPassword(ctx, owner.UserID.String(), codersdk.UpdateUserPasswordRequest{ + Password: "SomeNewStrongPassword!", + }) + require.Error(t, err, "user-admin should not be able to reset owner password") + var apiErr *codersdk.Error + require.ErrorAs(t, err, &apiErr) + require.Equal(t, http.StatusBadRequest, apiErr.StatusCode()) + require.Contains(t, apiErr.Message, "Only owners can change the password of an owner") + }) + + t.Run("OwnerCanResetOwnerPassword", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + owner := coderdtest.CreateFirstUser(t, client) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + anotherOwner, err := client.CreateUserWithOrgs(ctx, codersdk.CreateUserRequestWithOrgs{ + Email: "another-owner@coder.com", + Username: "another-owner", + Password: "SomeStrongPassword!", + OrganizationIDs: []uuid.UUID{owner.OrganizationID}, + }) + require.NoError(t, err) + _, err = client.UpdateUserRoles(ctx, anotherOwner.ID.String(), codersdk.UpdateRoles{ + Roles: []string{rbac.RoleOwner().String()}, + }) + require.NoError(t, err) + + err = client.UpdateUserPassword(ctx, anotherOwner.ID.String(), codersdk.UpdateUserPasswordRequest{ + Password: "SomeNewStrongPassword!", + }) + require.NoError(t, err, "owner should be able to reset another owner's password") + + _, err = client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{ + Email: "another-owner@coder.com", + Password: "SomeNewStrongPassword!", + }) + require.NoError(t, err, "other owner should login with the new password") + }) + t.Run("PasswordsMustDiffer", func(t *testing.T) { t.Parallel() @@ -2192,16 +2297,16 @@ func TestUserTerminalFont(t *testing.T) { firstUser := coderdtest.CreateFirstUser(t, adminClient) client, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID) - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort) defer cancel() // given - initial, err := client.GetUserAppearanceSettings(ctx, "me") + initial, err := client.GetUserAppearanceSettings(ctx, codersdk.Me) require.NoError(t, err) require.Equal(t, codersdk.TerminalFontName(""), initial.TerminalFont) // when - updated, err := client.UpdateUserAppearanceSettings(ctx, "me", codersdk.UpdateUserAppearanceSettingsRequest{ + updated, err := client.UpdateUserAppearanceSettings(ctx, codersdk.Me, codersdk.UpdateUserAppearanceSettingsRequest{ ThemePreference: "light", TerminalFont: "fira-code", }) @@ -2218,16 +2323,16 @@ func TestUserTerminalFont(t *testing.T) { firstUser := coderdtest.CreateFirstUser(t, adminClient) client, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID) - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort) defer cancel() // given - initial, err := client.GetUserAppearanceSettings(ctx, "me") + initial, err := client.GetUserAppearanceSettings(ctx, codersdk.Me) require.NoError(t, err) require.Equal(t, codersdk.TerminalFontName(""), initial.TerminalFont) // when - _, err = client.UpdateUserAppearanceSettings(ctx, "me", codersdk.UpdateUserAppearanceSettingsRequest{ + _, err = client.UpdateUserAppearanceSettings(ctx, codersdk.Me, codersdk.UpdateUserAppearanceSettingsRequest{ ThemePreference: "light", TerminalFont: "foobar", }) @@ -2243,16 +2348,16 @@ func TestUserTerminalFont(t *testing.T) { firstUser := coderdtest.CreateFirstUser(t, adminClient) client, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID) - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort) defer cancel() // given - initial, err := client.GetUserAppearanceSettings(ctx, "me") + initial, err := client.GetUserAppearanceSettings(ctx, codersdk.Me) require.NoError(t, err) require.Equal(t, codersdk.TerminalFontName(""), initial.TerminalFont) // when - _, err = client.UpdateUserAppearanceSettings(ctx, "me", codersdk.UpdateUserAppearanceSettingsRequest{ + _, err = client.UpdateUserAppearanceSettings(ctx, codersdk.Me, codersdk.UpdateUserAppearanceSettingsRequest{ ThemePreference: "light", TerminalFont: "", }) @@ -2262,6 +2367,75 @@ func TestUserTerminalFont(t *testing.T) { }) } +func TestUserTaskNotificationAlertDismissed(t *testing.T) { + t.Parallel() + + t.Run("defaults to false", func(t *testing.T) { + t.Parallel() + + adminClient := coderdtest.New(t, nil) + firstUser := coderdtest.CreateFirstUser(t, adminClient) + client, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort) + defer cancel() + + // When: getting user preference settings for a user + settings, err := client.GetUserPreferenceSettings(ctx, codersdk.Me) + require.NoError(t, err) + + // Then: the task notification alert dismissed should default to false + require.False(t, settings.TaskNotificationAlertDismissed) + }) + + t.Run("update to true", func(t *testing.T) { + t.Parallel() + + adminClient := coderdtest.New(t, nil) + firstUser := coderdtest.CreateFirstUser(t, adminClient) + client, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort) + defer cancel() + + // When: user dismisses the task notification alert + updated, err := client.UpdateUserPreferenceSettings(ctx, codersdk.Me, codersdk.UpdateUserPreferenceSettingsRequest{ + TaskNotificationAlertDismissed: true, + }) + require.NoError(t, err) + + // Then: the setting is updated to true + require.True(t, updated.TaskNotificationAlertDismissed) + }) + + t.Run("update to false", func(t *testing.T) { + t.Parallel() + + adminClient := coderdtest.New(t, nil) + firstUser := coderdtest.CreateFirstUser(t, adminClient) + client, _ := coderdtest.CreateAnotherUser(t, adminClient, firstUser.OrganizationID) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort) + defer cancel() + + // Given: user has dismissed the task notification alert + _, err := client.UpdateUserPreferenceSettings(ctx, codersdk.Me, codersdk.UpdateUserPreferenceSettingsRequest{ + TaskNotificationAlertDismissed: true, + }) + require.NoError(t, err) + + // When: the task notification alert dismissal is cleared + // (e.g., when user enables a task notification in the UI settings) + updated, err := client.UpdateUserPreferenceSettings(ctx, codersdk.Me, codersdk.UpdateUserPreferenceSettingsRequest{ + TaskNotificationAlertDismissed: false, + }) + require.NoError(t, err) + + // Then: the setting is updated to false + require.False(t, updated.TaskNotificationAlertDismissed) + }) +} + func TestWorkspacesByUser(t *testing.T) { t.Parallel() t.Run("Empty", func(t *testing.T) { diff --git a/coderd/util/namesgenerator/namesgenerator.go b/coderd/util/namesgenerator/namesgenerator.go new file mode 100644 index 00000000000..23bd9e9a6ec --- /dev/null +++ b/coderd/util/namesgenerator/namesgenerator.go @@ -0,0 +1,82 @@ +// Package namesgenerator generates random names. +// +// This package provides functions for generating random names in the format +// "adjective_surname" with various options for delimiters and uniqueness. +// +// For identifiers that must be unique within a process, use UniqueName or +// UniqueNameWith. For display purposes where uniqueness is not required, +// use NameWith. +package namesgenerator + +import ( + "math/rand/v2" + "strconv" + "strings" + "sync/atomic" + + "github.com/brianvoe/gofakeit/v7" +) + +// maxNameLen is the maximum length for names. Many places in Coder have a 32 +// character limit for names (e.g. usernames, workspace names). +const maxNameLen = 32 + +// counter provides unique suffixes for UniqueName functions. +var counter atomic.Int64 + +// NameWith returns a random name with a custom delimiter. +// Names are not guaranteed to be unique. +func NameWith(delim string) string { + const seed = 0 // gofakeit will use a random crypto seed. + faker := gofakeit.New(seed) + adjective := strings.ToLower(faker.AdjectiveDescriptive()) + last := strings.ToLower(faker.LastName()) + return adjective + delim + last +} + +// NameDigitWith returns a random name with a single random digit suffix (1-9), +// in the format "[adjective][delim][surname][digit]" e.g. "happy_smith9". +// Provides some collision resistance while keeping names short and clean. +// Not guaranteed to be unique. +func NameDigitWith(delim string) string { + const ( + minDigit = 1 + maxDigit = 9 + ) + //nolint:gosec // The random digit doesn't need to be cryptographically secure. + return NameWith(delim) + strconv.Itoa(rand.IntN(maxDigit-minDigit+1)) +} + +// UniqueName returns a random name with a monotonically increasing suffix, +// guaranteeing uniqueness within the process. The name is truncated to 32 +// characters if necessary, preserving the numeric suffix. +func UniqueName() string { + return UniqueNameWith("_") +} + +// UniqueNameWith returns a unique name with a custom delimiter. +// See UniqueName for details on uniqueness guarantees. +func UniqueNameWith(delim string) string { + name := NameWith(delim) + strconv.FormatInt(counter.Add(1), 10) + return truncate(name, maxNameLen) +} + +// truncate truncates a name to maxLen characters. It assumes the name ends with +// a numeric suffix and preserves it, truncating the base name portion instead. +func truncate(name string, maxLen int) string { + if len(name) <= maxLen { + return name + } + // Find where the numeric suffix starts. + suffixStart := len(name) + for suffixStart > 0 && name[suffixStart-1] >= '0' && name[suffixStart-1] <= '9' { + suffixStart-- + } + base := name[:suffixStart] + suffix := name[suffixStart:] + truncateAt := maxLen - len(suffix) + if truncateAt <= 0 { + return strconv.Itoa(maxLen) // Fallback, shouldn't happen in practice. + } + return base[:truncateAt] + suffix +} diff --git a/coderd/util/namesgenerator/namesgenerator_internal_test.go b/coderd/util/namesgenerator/namesgenerator_internal_test.go new file mode 100644 index 00000000000..a69b66bdaaf --- /dev/null +++ b/coderd/util/namesgenerator/namesgenerator_internal_test.go @@ -0,0 +1,103 @@ +package namesgenerator + +import ( + "strings" + "testing" + "unicode" + + "github.com/stretchr/testify/assert" +) + +func TestTruncate(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + input string + maxLen int + want string + }{ + { + name: "no truncation needed", + input: "foo1", + maxLen: 10, + want: "foo1", + }, + { + name: "exact fit", + input: "foo1", + maxLen: 4, + want: "foo1", + }, + { + name: "truncate base", + input: "foobar42", + maxLen: 5, + want: "foo42", + }, + { + name: "truncate more", + input: "foobar3", + maxLen: 3, + want: "fo3", + }, + { + name: "long suffix", + input: "foo123456", + maxLen: 8, + want: "fo123456", + }, + { + name: "realistic name", + input: "condescending_proskuriakova999999", + maxLen: 32, + want: "condescending_proskuriakov999999", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := truncate(tt.input, tt.maxLen) + assert.Equal(t, tt.want, got) + assert.LessOrEqual(t, len(got), tt.maxLen) + }) + } +} + +func TestUniqueNameLength(t *testing.T) { + t.Parallel() + + // Generate many names to exercise the truncation logic. + const iter = 10000 + for range iter { + name := UniqueName() + assert.LessOrEqual(t, len(name), maxNameLen) + assert.Contains(t, name, "_") + assert.Equal(t, name, strings.ToLower(name)) + verifyNoWhitespace(t, name) + } +} + +func TestUniqueNameWithLength(t *testing.T) { + t.Parallel() + + // Generate many names with hyphen delimiter. + const iter = 10000 + for range iter { + name := UniqueNameWith("-") + assert.LessOrEqual(t, len(name), maxNameLen) + assert.Contains(t, name, "-") + assert.Equal(t, name, strings.ToLower(name)) + verifyNoWhitespace(t, name) + } +} + +func verifyNoWhitespace(t *testing.T, s string) { + t.Helper() + for _, r := range s { + if unicode.IsSpace(r) { + t.Fatalf("found whitespace in string %q: %v", s, r) + } + } +} diff --git a/coderd/util/strings/strings.go b/coderd/util/strings/strings.go index f320142da55..d2594b80a09 100644 --- a/coderd/util/strings/strings.go +++ b/coderd/util/strings/strings.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" "unicode" + "unicode/utf8" "github.com/acarl005/stripansi" "github.com/microcosm-cc/bluemonday" @@ -53,7 +54,7 @@ const ( TruncateWithFullWords TruncateOption = 1 << 1 ) -// Truncate truncates s to n characters. +// Truncate truncates s to n runes. // Additional behaviors can be specified using TruncateOptions. func Truncate(s string, n int, opts ...TruncateOption) string { var options TruncateOption @@ -63,7 +64,8 @@ func Truncate(s string, n int, opts ...TruncateOption) string { if n < 1 { return "" } - if len(s) <= n { + runes := []rune(s) + if len(runes) <= n { return s } @@ -72,18 +74,18 @@ func Truncate(s string, n int, opts ...TruncateOption) string { maxLen-- } var sb strings.Builder - // If we need to truncate to full words, find the last word boundary before n. if options&TruncateWithFullWords != 0 { - lastWordBoundary := strings.LastIndexFunc(s[:maxLen], unicode.IsSpace) + // Convert the rune-safe prefix to a string, then find + // the last word boundary (byte offset within that prefix). + truncated := string(runes[:maxLen]) + lastWordBoundary := strings.LastIndexFunc(truncated, unicode.IsSpace) if lastWordBoundary < 0 { - // We cannot find a word boundary. At this point, we'll truncate the string. - // It's better than nothing. - _, _ = sb.WriteString(s[:maxLen]) - } else { // lastWordBoundary <= maxLen - _, _ = sb.WriteString(s[:lastWordBoundary]) + _, _ = sb.WriteString(truncated) + } else { + _, _ = sb.WriteString(truncated[:lastWordBoundary]) } } else { - _, _ = sb.WriteString(s[:maxLen]) + _, _ = sb.WriteString(string(runes[:maxLen])) } if options&TruncateWithEllipsis != 0 { @@ -126,3 +128,13 @@ func UISanitize(in string) string { } return strings.TrimSpace(b.String()) } + +// Capitalize returns s with its first rune upper-cased. It is safe for +// multi-byte UTF-8 characters, unlike naive byte-slicing approaches. +func Capitalize(s string) string { + r, size := utf8.DecodeRuneInString(s) + if size == 0 { + return s + } + return string(unicode.ToUpper(r)) + s[size:] +} diff --git a/coderd/util/strings/strings_test.go b/coderd/util/strings/strings_test.go index 000fa9efa11..494246c6cf1 100644 --- a/coderd/util/strings/strings_test.go +++ b/coderd/util/strings/strings_test.go @@ -57,6 +57,17 @@ func TestTruncate(t *testing.T) { {"foo bar", 1, "…", []strings.TruncateOption{strings.TruncateWithFullWords, strings.TruncateWithEllipsis}}, {"foo bar", 0, "", []strings.TruncateOption{strings.TruncateWithFullWords, strings.TruncateWithEllipsis}}, {"This is a very long task prompt that should be truncated to 160 characters. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", 160, "This is a very long task prompt that should be truncated to 160 characters. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor…", []strings.TruncateOption{strings.TruncateWithFullWords, strings.TruncateWithEllipsis}}, + // Multi-byte rune handling. + {"日本語テスト", 3, "日本語", nil}, + {"日本語テスト", 4, "日本語テ", nil}, + {"日本語テスト", 6, "日本語テスト", nil}, + {"日本語テスト", 4, "日本語…", []strings.TruncateOption{strings.TruncateWithEllipsis}}, + {"🎉🎊🎈🎁", 2, "🎉🎊", nil}, + {"🎉🎊🎈🎁", 3, "🎉🎊…", []strings.TruncateOption{strings.TruncateWithEllipsis}}, + // Multi-byte with full-word truncation. + {"hello 日本語", 7, "hello…", []strings.TruncateOption{strings.TruncateWithFullWords, strings.TruncateWithEllipsis}}, + {"hello 日本語", 8, "hello 日…", []strings.TruncateOption{strings.TruncateWithEllipsis}}, + {"日本語 テスト", 4, "日本語", []strings.TruncateOption{strings.TruncateWithFullWords}}, } { tName := fmt.Sprintf("%s_%d", tt.s, tt.n) for _, opt := range tt.options { @@ -107,3 +118,24 @@ func TestUISanitize(t *testing.T) { }) } } + +func TestCapitalize(t *testing.T) { + t.Parallel() + + tests := []struct { + input string + expected string + }{ + {"", ""}, + {"hello", "Hello"}, + {"über", "Über"}, + {"Hello", "Hello"}, + {"a", "A"}, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("%q", tt.input), func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.expected, strings.Capitalize(tt.input)) + }) + } +} diff --git a/coderd/util/xurl/xurl.go b/coderd/util/xurl/xurl.go new file mode 100644 index 00000000000..2db01ec82e3 --- /dev/null +++ b/coderd/util/xurl/xurl.go @@ -0,0 +1,51 @@ +// Package xurl contains small helpers extending the standard net/url +// package. +package xurl + +import ( + "net/url" + "strings" + "unicode" +) + +// ContainsEncodedPath reports whether u's path is unsafe to forward to +// another server with its escaping intact, because that server could resolve +// it to a different path than the one it was matched on. +// +// [net/http.ServeMux] matches routes on the escaped path and only cleans +// literal dot segments, while upstreams decode percent-encoded unreserved +// characters (RFC 3986 section 6.2.2.2) and then remove dot segments. A path +// such as /v1/models/%2e%2e/files therefore matches a /v1/models/ route +// locally but is served as /v1/files upstream. +// +// Each escaped segment is decoded exactly once. It returns true if a segment +// fails to decode, or if a decoded segment: +// - contains a "." or ".." element, also when split on an escaped "/" or "\" +// for components that decode reserved characters, +// - still contains "%", which only arises from multiple encoding and would +// let a component that decodes twice produce a dot segment, +// - contains control characters. +// +// Escaped separators in ordinary segments, such as /models/org%2Fmodel, are +// allowed. +func ContainsEncodedPath(u *url.URL) bool { + for _, seg := range strings.Split(u.EscapedPath(), "/") { + decoded, err := url.PathUnescape(seg) + if err != nil { + return true + } + if strings.ContainsRune(decoded, '%') || strings.ContainsFunc(decoded, unicode.IsControl) { + return true + } + for _, part := range strings.FieldsFunc(decoded, isPathSeparator) { + if part == "." || part == ".." { + return true + } + } + } + return false +} + +func isPathSeparator(r rune) bool { + return r == '/' || r == '\\' +} diff --git a/coderd/util/xurl/xurl_test.go b/coderd/util/xurl/xurl_test.go new file mode 100644 index 00000000000..7c81661ff0f --- /dev/null +++ b/coderd/util/xurl/xurl_test.go @@ -0,0 +1,57 @@ +package xurl_test + +import ( + "net/url" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/coder/coder/v2/coderd/util/xurl" +) + +func TestContainsEncodedPath(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + name string + path string + want bool + }{ + {name: "Plain", path: "/v1/models"}, + {name: "ModelID", path: "/v1/models/gpt-4.1"}, + {name: "ResourceID", path: "/v1/responses/resp_abc123"}, + {name: "TrailingSlash", path: "/v1/models/"}, + {name: "EscapedSeparator", path: "/v1/models/org%2Fmodel"}, + {name: "EscapedSpace", path: "/v1/models/a%20b"}, + {name: "DotsInName", path: "/v1/models/..model"}, + {name: "LiteralDotDot", path: "/v1/models/../files", want: true}, + {name: "LiteralDot", path: "/v1/./models", want: true}, + {name: "EncodedDotDot", path: "/v1/models/%2e%2e/files", want: true}, + {name: "EncodedDotDotUpper", path: "/v1/models/%2E%2E/files", want: true}, + {name: "MixedDotDot", path: "/v1/models/.%2e/files", want: true}, + {name: "EncodedDot", path: "/v1/%2e/models", want: true}, + {name: "DotDotEscapedSlash", path: "/v1/models/..%2F..%2Ffiles", want: true}, + {name: "DotDotInsideSegment", path: "/v1/models/a%2F..%2Fb", want: true}, + {name: "DotDotEscapedBackslash", path: "/v1/models/..%5Cfiles", want: true}, + {name: "DoubleEncodedDotDot", path: "/v1/models/%252e%252e/files", want: true}, + {name: "TripleEncodedDotDot", path: "/v1/models/%25252e%25252e/files", want: true}, + {name: "EncodedNUL", path: "/v1/models/a%00b", want: true}, + {name: "EncodedNewline", path: "/v1/models/a%0Ab", want: true}, + {name: "InvalidEscape", path: "/v1/models/%zz", want: true}, + } { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Build the URL the way the server does for an incoming + // request line, preserving the client's escaping. + u, err := url.ParseRequestURI(tc.path) + if err != nil { + // Paths the server itself refuses to parse never reach + // the handler. + require.True(t, tc.want, "unexpected parse error: %v", err) + return + } + require.Equal(t, tc.want, xurl.ContainsEncodedPath(u)) + }) + } +} diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index d3cca070665..b50410ea7d6 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -1134,6 +1134,12 @@ func (api *API) workspaceAgentListContainers(rw http.ResponseWriter, r *http.Req func (api *API) workspaceAgentRecreateDevcontainer(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceAgent := httpmw.WorkspaceAgentParam(r) + workspace := httpmw.WorkspaceParam(r) + + if !api.Authorize(r, policy.ActionUpdate, workspace) { + httpapi.Forbidden(rw) + return + } devcontainer := chi.URLParam(r, "devcontainer") if devcontainer == "" { diff --git a/coderd/workspaceagents_test.go b/coderd/workspaceagents_test.go index 6c12f91d373..7f7b1e6132c 100644 --- a/coderd/workspaceagents_test.go +++ b/coderd/workspaceagents_test.go @@ -1571,6 +1571,57 @@ func TestWorkspaceAgentRecreateDevcontainer(t *testing.T) { }) } +func TestWorkspaceAgentRecreateDevcontainerAuthorization(t *testing.T) { + t.Parallel() + + for _, tc := range []struct { + name string + role func(uuid.UUID) rbac.RoleIdentifier + }{ + { + name: "TemplateAdmin", + role: func(uuid.UUID) rbac.RoleIdentifier { + return rbac.RoleTemplateAdmin() + }, + }, + { + name: "OrgTemplateAdmin", + role: rbac.ScopedRoleOrgTemplateAdmin, + }, + } { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + var ( + ctx = testutil.Context(t, testutil.WaitMedium) + client, db = coderdtest.NewWithDatabase(t, nil) + admin = coderdtest.CreateFirstUser(t, client) + _, workspaceOwner = coderdtest.CreateAnotherUser(t, client, admin.OrganizationID) + templateAdminClient, _ = coderdtest.CreateAnotherUser(t, client, admin.OrganizationID, tc.role(admin.OrganizationID)) + ) + r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{ + OrganizationID: admin.OrganizationID, + OwnerID: workspaceOwner.ID, + }).WithAgent(func(agents []*proto.Agent) []*proto.Agent { + return agents + }).Do() + + ws, err := client.Workspace(ctx, r.Workspace.ID) + require.NoError(t, err) + require.NotEmpty(t, ws.LatestBuild.Resources) + require.NotEmpty(t, ws.LatestBuild.Resources[0].Agents) + agentID := ws.LatestBuild.Resources[0].Agents[0].ID + + _, err = templateAdminClient.WorkspaceAgentRecreateDevcontainer(ctx, agentID, uuid.NewString()) + require.Error(t, err) + + var sdkErr *codersdk.Error + require.ErrorAs(t, err, &sdkErr) + require.Equal(t, http.StatusForbidden, sdkErr.StatusCode()) + }) + } +} + func TestWorkspaceAgentAppHealth(t *testing.T) { t.Parallel() client, db := coderdtest.NewWithDatabase(t, nil) diff --git a/coderd/workspaceagentsrpc.go b/coderd/workspaceagentsrpc.go index d5757786e47..79de64d998b 100644 --- a/coderd/workspaceagentsrpc.go +++ b/coderd/workspaceagentsrpc.go @@ -132,7 +132,7 @@ func (api *API) workspaceAgentRPC(rw http.ResponseWriter, r *http.Request) { WorkspaceID: workspace.ID, OrganizationID: workspace.OrganizationID, - Ctx: api.ctx, + AuthenticatedCtx: ctx, Log: logger, Clock: api.Clock, Database: api.Database, @@ -146,6 +146,7 @@ func (api *API) workspaceAgentRPC(rw http.ResponseWriter, r *http.Request) { PublishWorkspaceUpdateFn: api.publishWorkspaceUpdate, PublishWorkspaceAgentLogsUpdateFn: api.publishWorkspaceAgentLogsUpdate, NetworkTelemetryHandler: api.NetworkTelemetryBatcher.Handler, + PortSharer: &api.PortSharer, AccessURL: api.AccessURL, AppHostname: api.AppHostname, diff --git a/coderd/workspaceagentsrpc_test.go b/coderd/workspaceagentsrpc_test.go index 525b8a981db..550e613d5f6 100644 --- a/coderd/workspaceagentsrpc_test.go +++ b/coderd/workspaceagentsrpc_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "storj.io/drpc/drpcerr" agentproto "github.com/coder/coder/v2/agent/proto" "github.com/coder/coder/v2/coderd/coderdtest" @@ -16,6 +17,8 @@ import ( "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/codersdk/agentsdk" "github.com/coder/coder/v2/provisionersdk/proto" + "github.com/coder/coder/v2/tailnet" + tailnetproto "github.com/coder/coder/v2/tailnet/proto" "github.com/coder/coder/v2/testutil" ) @@ -108,6 +111,47 @@ func TestWorkspaceAgentReportStats(t *testing.T) { } } +func TestWorkspaceAgentRPC_TailnetMethods(t *testing.T) { + t.Parallel() + + ctx := testutil.Context(t, testutil.WaitLong) + client, db := coderdtest.NewWithDatabase(t, nil) + user := coderdtest.CreateFirstUser(t, client) + workspace := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{ + OrganizationID: user.OrganizationID, + OwnerID: user.UserID, + }).WithAgent().Do() + + agentClient := agentsdk.New(client.URL, agentsdk.WithFixedToken(workspace.AgentToken)) + conn, err := agentClient.ConnectRPC(ctx) + require.NoError(t, err) + t.Cleanup(func() { + _ = conn.Close() + }) + + tailnetClient := tailnetproto.NewDRPCTailnetClient(conn) + _, err = tailnetClient.RefreshResumeToken(ctx, &tailnetproto.RefreshResumeTokenRequest{}) + require.Error(t, err) + require.EqualValues(t, drpcerr.Unimplemented, drpcerr.Code(err)) + + updates, err := tailnetClient.WorkspaceUpdates(ctx, &tailnetproto.WorkspaceUpdatesRequest{ + WorkspaceOwnerId: tailnet.UUIDToByteSlice(user.UserID), + }) + if err == nil { + _, err = updates.Recv() + } + require.Error(t, err) + require.EqualValues(t, drpcerr.Unimplemented, drpcerr.Code(err)) + + telemetry, err := tailnetClient.PostTelemetry(ctx, &tailnetproto.TelemetryRequest{}) + require.NoError(t, err) + require.NotNil(t, telemetry) + + agentAPI := agentproto.NewDRPCAgentClient(conn) + _, err = agentAPI.GetManifest(ctx, &agentproto.GetManifestRequest{}) + require.NoError(t, err) +} + func TestAgentAPI_LargeManifest(t *testing.T) { t.Parallel() diff --git a/coderd/workspaceapps/apptest/apptest.go b/coderd/workspaceapps/apptest/apptest.go index 07b54b7b3f3..565582108f0 100644 --- a/coderd/workspaceapps/apptest/apptest.go +++ b/coderd/workspaceapps/apptest/apptest.go @@ -1247,6 +1247,34 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) { assertWorkspaceLastUsedAtNotUpdated(ctx, t, appDetails) }) + // Security (PLAT-260): must 404 when the URL username segment + // names a different owner than the resolved workspace. + t.Run("WorkspaceUUIDOwnerMismatchShould404", func(t *testing.T) { + t.Parallel() + + appDetails := setupProxyTest(t, nil) + otherUserClient, otherUser := coderdtest.CreateAnotherUser(t, appDetails.SDKClient, appDetails.FirstUser.OrganizationID, rbac.RoleMember()) + appClient := appDetails.AppClient(t) + appClient.SetSessionToken(otherUserClient.SessionToken()) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + forgedApp := appDetails.Apps.Public + forgedApp.Username = otherUser.Username + forgedApp.WorkspaceName = appDetails.Workspace.ID.String() + + resp, err := requestWithRetries(ctx, t, appClient, http.MethodGet, appDetails.SubdomainAppURL(forgedApp).String(), nil) + require.NoError(t, err) + defer resp.Body.Close() + require.Equal(t, http.StatusNotFound, resp.StatusCode) + + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + require.Contains(t, string(body), "404 - Application Not Found") + assertWorkspaceLastUsedAtNotUpdated(ctx, t, appDetails) + }) + t.Run("RedirectsWithSlash", func(t *testing.T) { t.Parallel() diff --git a/coderd/workspaceapps/db_test.go b/coderd/workspaceapps/db_test.go index a7ad1a85e55..e7e86e5f133 100644 --- a/coderd/workspaceapps/db_test.go +++ b/coderd/workspaceapps/db_test.go @@ -916,6 +916,50 @@ func Test_ResolveRequest(t *testing.T) { require.Len(t, connLogger.ConnectionLogs(), 0) }) + // Security (PLAT-260): a UUID workspace lookup must reject when + // the URL's username segment names a different owner. Otherwise a + // same-owner origin can be spoofed for credentialed cross-origin + // reads. + t.Run("WorkspaceUUIDOwnerMismatch", func(t *testing.T) { + t.Parallel() + + req := (workspaceapps.Request{ + AccessMethod: workspaceapps.AccessMethodPath, + BasePath: "/app", + UsernameOrID: secondUser.Username, + WorkspaceNameOrID: workspace.ID.String(), + AgentNameOrID: agentName, + AppSlugOrPort: appNamePublic, + }).Normalize() + + connLogger := connectionlog.NewFake() + auditableIP := testutil.RandomIPv6(t) + + rw := httptest.NewRecorder() + r := httptest.NewRequest("GET", "/app", nil) + r.Header.Set(codersdk.SessionTokenHeader, client.SessionToken()) + r.RemoteAddr = auditableIP + + token, ok := workspaceappsResolveRequest(t, connLogger, rw, r, workspaceapps.ResolveRequestOptions{ + Logger: api.Logger, + SignedTokenProvider: api.WorkspaceAppsProvider, + DashboardURL: api.AccessURL, + PathAppBaseURL: api.AccessURL, + AppHostname: api.AppHostname, + AppRequest: req, + }) + require.False(t, ok) + require.Nil(t, token) + + w := rw.Result() + defer w.Body.Close() + b, err := io.ReadAll(w.Body) + require.NoError(t, err) + require.Contains(t, string(b), "404 - Application Not Found") + require.Equal(t, http.StatusNotFound, w.StatusCode) + require.Len(t, connLogger.ConnectionLogs(), 0) + }) + t.Run("RedirectSubdomainAuth", func(t *testing.T) { t.Parallel() @@ -1015,7 +1059,7 @@ func Test_ResolveRequest(t *testing.T) { w := rw.Result() defer w.Body.Close() - require.Equal(t, http.StatusBadGateway, w.StatusCode) + require.Equal(t, http.StatusNotFound, w.StatusCode) assertConnLogContains(t, rw, r, connLogger, workspace, agentNameUnhealthy, appNameAgentUnhealthy, database.ConnectionTypeWorkspaceApp, me.ID) require.Len(t, connLogger.ConnectionLogs(), 1) diff --git a/coderd/workspaceapps/errors.go b/coderd/workspaceapps/errors.go index 64d61de3678..1d4bd061edc 100644 --- a/coderd/workspaceapps/errors.go +++ b/coderd/workspaceapps/errors.go @@ -68,7 +68,7 @@ func WriteWorkspaceApp500(log slog.Logger, accessURL *url.URL, rw http.ResponseW }) } -// WriteWorkspaceAppOffline writes a HTML 502 error page for a workspace app. If +// WriteWorkspaceAppOffline writes a HTML 404 error page for a workspace app. If // appReq is not nil, it will be used to log the request details at debug level. func WriteWorkspaceAppOffline(log slog.Logger, accessURL *url.URL, rw http.ResponseWriter, r *http.Request, appReq *Request, msg string) { if appReq != nil { @@ -85,7 +85,7 @@ func WriteWorkspaceAppOffline(log slog.Logger, accessURL *url.URL, rw http.Respo } site.RenderStaticErrorPage(rw, r, site.ErrorPageData{ - Status: http.StatusBadGateway, + Status: http.StatusNotFound, Title: "Application Unavailable", Description: msg, RetryEnabled: true, diff --git a/coderd/workspaceapps/proxy.go b/coderd/workspaceapps/proxy.go index 981bba45849..ffdbec40627 100644 --- a/coderd/workspaceapps/proxy.go +++ b/coderd/workspaceapps/proxy.go @@ -417,7 +417,7 @@ func (s *Server) HandleSubdomain(middlewares ...func(http.Handler) http.Handler) } // Step 2: Get the request Host. - host := httpapi.RequestHost(r) + host := httpmw.EffectiveHost(s.RealIPConfig, r) if host == "" { if r.URL.Path == "/derp" { // The /derp endpoint is used by wireguard clients to tunnel diff --git a/coderd/workspaceapps/proxy_test.go b/coderd/workspaceapps/proxy_test.go index 5c71f15ffa6..8678614243c 100644 --- a/coderd/workspaceapps/proxy_test.go +++ b/coderd/workspaceapps/proxy_test.go @@ -1,3 +1,90 @@ package workspaceapps_test // App tests can be found in the apptest package. + +import ( + "context" + "net" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/coder/coder/v2/coderd/httpapi" + "github.com/coder/coder/v2/coderd/httpmw" + "github.com/coder/coder/v2/coderd/workspaceapps" + "github.com/coder/coder/v2/coderd/workspaceapps/appurl" + "github.com/coder/coder/v2/testutil" +) + +type fakeSignedTokenProvider struct { + fromRequestCalls int + issueCalls int +} + +func (s *fakeSignedTokenProvider) FromRequest(_ *http.Request) (*workspaceapps.SignedToken, bool) { + s.fromRequestCalls++ + return nil, false +} + +func (s *fakeSignedTokenProvider) Issue(_ context.Context, _ http.ResponseWriter, _ *http.Request, _ workspaceapps.IssueTokenRequest) (*workspaceapps.SignedToken, string, bool) { + s.issueCalls++ + return nil, "", false +} + +func TestHandleSubdomain_IgnoresUntrustedForwardedHost(t *testing.T) { + t.Parallel() + + hostnamePattern := "*--apps.test.coder.com" + hostnameRegex, err := appurl.CompileHostnamePattern(hostnamePattern) + require.NoError(t, err) + + dashboardURL, err := url.Parse("https://dashboard.test.coder.com") + require.NoError(t, err) + + provider := &fakeSignedTokenProvider{} + srv := workspaceapps.NewServer(workspaceapps.ServerOptions{ + Logger: testutil.Logger(t), + DashboardURL: dashboardURL, + AccessURL: dashboardURL, + Hostname: hostnamePattern, + HostnameRegex: hostnameRegex, + RealIPConfig: &httpmw.RealIPConfig{ + TrustedOrigins: []*net.IPNet{{ + IP: net.ParseIP("10.0.0.1"), + Mask: net.CIDRMask(32, 32), + }}, + }, + SignedTokenProvider: provider, + }) + + forgedHost := appurl.ApplicationURL{ + AppSlugOrPort: "app", + WorkspaceName: "workspace", + Username: "victim", + }.String() + "--apps.test.coder.com" + + nextCalled := false + next := http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { + nextCalled = true + }) + + // Given: a request with a forged X-Forwarded-Host set to a valid + // app hostname, and an immediate peer outside the trusted proxy + // config. + req := httptest.NewRequest(http.MethodGet, "https://dashboard.test.coder.com/", nil) + req.Header.Set(httpapi.XForwardedHostHeader, forgedHost) + req.RemoteAddr = "17.18.19.20:1234" + + // When: HandleSubdomain runs. + srv.HandleSubdomain()(next).ServeHTTP(httptest.NewRecorder(), req) + + // Then: it ignores untrusted X-Forwarded-Host, so the received + // dashboard host is used, the request falls through to the next + // handler, and the signed app token provider is never called. + require.True(t, nextCalled) + require.Zero(t, provider.fromRequestCalls) + require.Zero(t, provider.issueCalls) +} diff --git a/coderd/workspaceapps/request.go b/coderd/workspaceapps/request.go index aa90ead2cdd..1b53c4754a4 100644 --- a/coderd/workspaceapps/request.go +++ b/coderd/workspaceapps/request.go @@ -249,6 +249,9 @@ func (r Request) getDatabase(ctx context.Context, db database.Store) (*databaseR ) if workspaceID, uuidErr := uuid.Parse(r.WorkspaceNameOrID); uuidErr == nil { workspace, workspaceErr = db.GetWorkspaceByID(ctx, workspaceID) + if workspaceErr == nil && workspace.OwnerID != user.ID { + workspaceErr = sql.ErrNoRows + } } else { workspace, workspaceErr = db.GetWorkspaceByOwnerIDAndName(ctx, database.GetWorkspaceByOwnerIDAndNameParams{ OwnerID: user.ID, diff --git a/coderd/workspaceapps_test.go b/coderd/workspaceapps_test.go index 8db2858e01e..db6f77ab951 100644 --- a/coderd/workspaceapps_test.go +++ b/coderd/workspaceapps_test.go @@ -115,6 +115,15 @@ func TestWorkspaceApplicationAuth(t *testing.T) { redirectURI: "https://proxy.test.coder.com/path", expectRedirect: "https://proxy.test.coder.com/path", }, + { + name: "RejectProxyAccessURLPrefix", + accessURL: "https://test.coder.com", + appHostname: "*.test.coder.com", + proxyURL: "https://proxy.test.coder.com", + proxyAppHostname: "*.proxy.test.coder.com", + redirectURI: "https://proxy.test.coder/path", + expectRedirect: "", + }, { name: "ProxySubdomainOK", accessURL: "https://test.coder.com", diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 0c58b902e21..f5eedea9fac 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -849,6 +849,63 @@ func (api *API) workspaceBuildState(rw http.ResponseWriter, r *http.Request) { _, _ = rw.Write(workspaceBuild.ProvisionerState) } +// @Summary Update workspace build state +// @ID update-workspace-build-state +// @Security CoderSessionToken +// @Accept json +// @Tags Builds +// @Param workspacebuild path string true "Workspace build ID" format(uuid) +// @Param request body codersdk.UpdateWorkspaceBuildStateRequest true "Request body" +// @Success 204 +// @Router /workspacebuilds/{workspacebuild}/state [put] +func (api *API) workspaceBuildUpdateState(rw http.ResponseWriter, r *http.Request) { + ctx := r.Context() + workspaceBuild := httpmw.WorkspaceBuildParam(r) + workspace, err := api.Database.GetWorkspaceByID(ctx, workspaceBuild.WorkspaceID) + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "No workspace exists for this job.", + }) + return + } + template, err := api.Database.GetTemplateByID(ctx, workspace.TemplateID) + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Failed to get template", + Detail: err.Error(), + }) + return + } + + // You must have update permissions on the template to update the state. + if !api.Authorize(r, policy.ActionUpdate, template.RBACObject()) { + httpapi.ResourceNotFound(rw) + return + } + + var req codersdk.UpdateWorkspaceBuildStateRequest + if !httpapi.Read(ctx, rw, r, &req) { + return + } + + // Use system context since we've already verified authorization via template permissions. + // nolint:gocritic // System access required for provisioner state update. + err = api.Database.UpdateWorkspaceBuildProvisionerStateByID(dbauthz.AsSystemRestricted(ctx), database.UpdateWorkspaceBuildProvisionerStateByIDParams{ + ID: workspaceBuild.ID, + ProvisionerState: req.State, + UpdatedAt: dbtime.Now(), + }) + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Failed to update workspace build state.", + Detail: err.Error(), + }) + return + } + + rw.WriteHeader(http.StatusNoContent) +} + // @Summary Get workspace build timings by ID // @ID get-workspace-build-timings-by-id // @Security CoderSessionToken diff --git a/coderd/workspaceresourceauth.go b/coderd/workspaceresourceauth.go index 3642822b18d..2f51745369a 100644 --- a/coderd/workspaceresourceauth.go +++ b/coderd/workspaceresourceauth.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" + "cdr.dev/slog" "github.com/coder/coder/v2/coderd/awsidentity" "github.com/coder/coder/v2/coderd/azureidentity" "github.com/coder/coder/v2/coderd/database" @@ -35,13 +36,19 @@ func (api *API) postWorkspaceAuthAzureInstanceIdentity(rw http.ResponseWriter, r if !httpapi.Read(ctx, rw, r, &req) { return } - instanceID, err := azureidentity.Validate(r.Context(), req.Signature, azureidentity.Options{ - VerifyOptions: api.AzureCertificates, - }) + instanceID, err := azureidentity.Validate(r.Context(), req.Signature, api.AzureCertificates) if err != nil { + // Log the full error for operators but return only a + // generic message to the caller. Errors from the + // certificate fetch path may contain fragments of + // internal HTTP responses, so exposing them would be + // an information disclosure risk. + api.Logger.Warn(ctx, "azure identity validation failed", + slog.Error(err), + ) httpapi.Write(ctx, rw, http.StatusUnauthorized, codersdk.Response{ Message: "Invalid Azure identity.", - Detail: err.Error(), + Detail: "Signature verification failed.", }) return } @@ -98,8 +105,7 @@ func (api *API) postWorkspaceAuthGoogleInstanceIdentity(rw http.ResponseWriter, return } - // We leave the audience blank. It's not important we validate who made the token. - payload, err := api.GoogleTokenValidator.Validate(ctx, req.JSONWebToken, "") + payload, err := api.GoogleTokenValidator.Validate(ctx, req.JSONWebToken, "coder") if err != nil { httpapi.Write(ctx, rw, http.StatusUnauthorized, codersdk.Response{ Message: "Invalid GCP identity.", diff --git a/coderd/workspaceresourceauth_test.go b/coderd/workspaceresourceauth_test.go index 73524a63ade..dbae993f5ff 100644 --- a/coderd/workspaceresourceauth_test.go +++ b/coderd/workspaceresourceauth_test.go @@ -5,9 +5,11 @@ import ( "net/http" "testing" + "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/coder/coder/v2/coderd/coderdtest" + "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk/agentsdk" "github.com/coder/coder/v2/provisioner/echo" @@ -106,8 +108,8 @@ func TestPostWorkspaceAuthGoogleInstanceIdentity(t *testing.T) { t.Parallel() t.Run("Expired", func(t *testing.T) { t.Parallel() - instanceID := "instanceidentifier" - validator, metadata := coderdtest.NewGoogleInstanceIdentity(t, instanceID, true) + instanceID := uuid.NewString() + validator, metadata := coderdtest.NewGoogleInstanceIdentity(t, instanceID, true, "coder") client := coderdtest.New(t, &coderdtest.Options{ GoogleTokenValidator: validator, }) @@ -124,8 +126,8 @@ func TestPostWorkspaceAuthGoogleInstanceIdentity(t *testing.T) { t.Run("InstanceNotFound", func(t *testing.T) { t.Parallel() - instanceID := "instanceidentifier" - validator, metadata := coderdtest.NewGoogleInstanceIdentity(t, instanceID, false) + instanceID := uuid.NewString() + validator, metadata := coderdtest.NewGoogleInstanceIdentity(t, instanceID, false, "coder") client := coderdtest.New(t, &coderdtest.Options{ GoogleTokenValidator: validator, }) @@ -140,44 +142,62 @@ func TestPostWorkspaceAuthGoogleInstanceIdentity(t *testing.T) { require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) }) - t.Run("Success", func(t *testing.T) { - t.Parallel() - instanceID := "instanceidentifier" - validator, metadata := coderdtest.NewGoogleInstanceIdentity(t, instanceID, false) - client := coderdtest.New(t, &coderdtest.Options{ - GoogleTokenValidator: validator, - IncludeProvisionerDaemon: true, - }) - user := coderdtest.CreateFirstUser(t, client) - version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ - Parse: echo.ParseComplete, - ProvisionApply: []*proto.Response{{ - Type: &proto.Response_Apply{ - Apply: &proto.ApplyComplete{ - Resources: []*proto.Resource{{ - Name: "somename", - Type: "someinstance", - Agents: []*proto.Agent{{ - Name: "dev", - Auth: &proto.Agent_InstanceId{ - InstanceId: instanceID, - }, + for _, tc := range []struct { + name string + audience string + wantUnauthorized bool + }{ + {name: "Success", audience: "coder"}, + {name: "ForeignAudience", audience: "other-service", wantUnauthorized: true}, + } { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + instanceID := uuid.NewString() + validator, metadata := coderdtest.NewGoogleInstanceIdentity(t, instanceID, false, tc.audience) + client, store := coderdtest.NewWithDatabase(t, &coderdtest.Options{ + GoogleTokenValidator: validator, + IncludeProvisionerDaemon: true, + }) + user := coderdtest.CreateFirstUser(t, client) + version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionApply: []*proto.Response{{ + Type: &proto.Response_Apply{ + Apply: &proto.ApplyComplete{ + Resources: []*proto.Resource{{ + Name: "somename", + Type: "someinstance", + Agents: []*proto.Agent{{ + Name: "dev", + Auth: &proto.Agent_InstanceId{ + InstanceId: instanceID, + }, + }}, }}, - }}, + }, }, - }, - }}, + }}, + }) + template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) + coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) + workspace := coderdtest.CreateWorkspace(t, client, template.ID) + coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + agentClient := agentsdk.New(client.URL, agentsdk.WithGoogleInstanceIdentity("", metadata)) + err := agentClient.RefreshToken(ctx) + if tc.wantUnauthorized { + var apiErr *codersdk.Error + require.ErrorAs(t, err, &apiErr) + require.Equal(t, http.StatusUnauthorized, apiErr.StatusCode()) + return + } + require.NoError(t, err) + expectedAgent, err := store.GetWorkspaceAgentByInstanceID(dbauthz.AsSystemRestricted(ctx), instanceID) + require.NoError(t, err) + require.Equal(t, expectedAgent.AuthToken.String(), agentClient.SDK.SessionToken()) }) - template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID) - coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) - workspace := coderdtest.CreateWorkspace(t, client, template.ID) - coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID) - - ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) - defer cancel() - - agentClient := agentsdk.New(client.URL, agentsdk.WithGoogleInstanceIdentity("", metadata)) - err := agentClient.RefreshToken(ctx) - require.NoError(t, err) - }) + } } diff --git a/coderd/workspaces.go b/coderd/workspaces.go index a02c16ec895..bb6983cbfee 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -937,7 +937,7 @@ func claimPrebuild( nextStartAt sql.NullTime, ttl sql.NullInt64, ) (*database.Workspace, error) { - claimedID, err := claimer.Claim(ctx, now, owner.ID, name, templateVersionPresetID, autostartSchedule, nextStartAt, ttl) + claimedID, err := claimer.Claim(ctx, db, now, owner.ID, name, templateVersionPresetID, autostartSchedule, nextStartAt, ttl) if err != nil { // TODO: enhance this by clarifying whether this *specific* prebuild failed or whether there are none to claim. return nil, xerrors.Errorf("claim prebuild: %w", err) diff --git a/coderd/wsbuilder/wsbuilder.go b/coderd/wsbuilder/wsbuilder.go index 6aef8c2c2aa..0eeddd2ba29 100644 --- a/coderd/wsbuilder/wsbuilder.go +++ b/coderd/wsbuilder/wsbuilder.go @@ -6,7 +6,6 @@ import ( "context" "database/sql" "encoding/json" - "errors" "fmt" "net/http" "time" @@ -87,13 +86,15 @@ type Builder struct { templateVersionPresetParameterValues *[]database.TemplateVersionPresetParameter parameterRender dynamicparameters.Renderer workspaceTags *map[string]string + task *database.Task + hasTask *bool // A workspace without a task will have a nil `task` and false `hasTask`. prebuiltWorkspaceBuildStage sdkproto.PrebuiltWorkspaceBuildStage verifyNoLegacyParametersOnce bool } type UsageChecker interface { - CheckBuildUsage(ctx context.Context, store database.Store, templateVersion *database.TemplateVersion) (UsageCheckResponse, error) + CheckBuildUsage(ctx context.Context, store database.Store, templateVersion *database.TemplateVersion, task *database.Task, transition database.WorkspaceTransition) (UsageCheckResponse, error) } type UsageCheckResponse struct { @@ -105,7 +106,7 @@ type NoopUsageChecker struct{} var _ UsageChecker = NoopUsageChecker{} -func (NoopUsageChecker) CheckBuildUsage(_ context.Context, _ database.Store, _ *database.TemplateVersion) (UsageCheckResponse, error) { +func (NoopUsageChecker) CheckBuildUsage(_ context.Context, _ database.Store, _ *database.TemplateVersion, _ *database.Task, _ database.WorkspaceTransition) (UsageCheckResponse, error) { return UsageCheckResponse{ Permitted: true, }, nil @@ -489,8 +490,12 @@ func (b *Builder) buildTx(authFunc func(action policy.Action, object rbac.Object return BuildError{code, "insert workspace build", err} } + task, err := b.getWorkspaceTask() + if err != nil { + return BuildError{http.StatusInternalServerError, "get task by workspace id", err} + } // If this is a task workspace, link it to the latest workspace build. - if task, err := store.GetTaskByWorkspaceID(b.ctx, b.workspace.ID); err == nil { + if task != nil { _, err = store.UpsertTaskWorkspaceApp(b.ctx, database.UpsertTaskWorkspaceAppParams{ TaskID: task.ID, WorkspaceBuildNumber: buildNum, @@ -500,8 +505,6 @@ func (b *Builder) buildTx(authFunc func(action policy.Action, object rbac.Object if err != nil { return BuildError{http.StatusInternalServerError, "upsert task workspace app", err} } - } else if !errors.Is(err, sql.ErrNoRows) { - return BuildError{http.StatusInternalServerError, "get task by workspace id", err} } err = store.InsertWorkspaceBuildParameters(b.ctx, database.InsertWorkspaceBuildParametersParams{ @@ -634,6 +637,27 @@ func (b *Builder) getTemplateVersionID() (uuid.UUID, error) { return bld.TemplateVersionID, nil } +// getWorkspaceTask returns the task associated with the workspace, if any. +// If no task exists, it returns (nil, nil). +func (b *Builder) getWorkspaceTask() (*database.Task, error) { + if b.hasTask != nil { + return b.task, nil + } + t, err := b.store.GetTaskByWorkspaceID(b.ctx, b.workspace.ID) + if err != nil { + if xerrors.Is(err, sql.ErrNoRows) { + b.hasTask = ptr.Ref(false) + //nolint:nilnil // No task exists. + return nil, nil + } + return nil, xerrors.Errorf("get task: %w", err) + } + + b.task = &t + b.hasTask = ptr.Ref(true) + return b.task, nil +} + func (b *Builder) getTemplateTerraformValues() (*database.TemplateVersionTerraformValue, error) { if b.terraformValues != nil { return b.terraformValues, nil @@ -1307,7 +1331,12 @@ func (b *Builder) checkUsage() error { return BuildError{http.StatusInternalServerError, "Failed to fetch template version", err} } - resp, err := b.usageChecker.CheckBuildUsage(b.ctx, b.store, templateVersion) + task, err := b.getWorkspaceTask() + if err != nil { + return BuildError{http.StatusInternalServerError, "Failed to fetch workspace task", err} + } + + resp, err := b.usageChecker.CheckBuildUsage(b.ctx, b.store, templateVersion, task, b.trans) if err != nil { return BuildError{http.StatusInternalServerError, "Failed to check build usage", err} } diff --git a/coderd/wsbuilder/wsbuilder_test.go b/coderd/wsbuilder/wsbuilder_test.go index 3a8921dd6dc..38f88f7508a 100644 --- a/coderd/wsbuilder/wsbuilder_test.go +++ b/coderd/wsbuilder/wsbuilder_test.go @@ -570,6 +570,7 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) { mDB := expectDB(t, // Inputs withTemplate, + withNoTask, withInactiveVersionNoParams(), withLastBuildFound, withTemplateVersionVariables(inactiveVersionID, nil), @@ -605,6 +606,7 @@ func TestWorkspaceBuildWithRichParameters(t *testing.T) { withTemplate, withInactiveVersion(richParameters), withLastBuildFound, + withNoTask, withTemplateVersionVariables(inactiveVersionID, nil), withRichParameters(initialBuildParameters), withParameterSchemas(inactiveJobID, nil), @@ -1049,7 +1051,7 @@ func TestWorkspaceBuildUsageChecker(t *testing.T) { var calls int64 fakeUsageChecker := &fakeUsageChecker{ - checkBuildUsageFunc: func(_ context.Context, _ database.Store, templateVersion *database.TemplateVersion) (wsbuilder.UsageCheckResponse, error) { + checkBuildUsageFunc: func(_ context.Context, _ database.Store, _ *database.TemplateVersion, _ *database.Task, _ database.WorkspaceTransition) (wsbuilder.UsageCheckResponse, error) { atomic.AddInt64(&calls, 1) return wsbuilder.UsageCheckResponse{Permitted: true}, nil }, @@ -1126,7 +1128,7 @@ func TestWorkspaceBuildUsageChecker(t *testing.T) { var calls int64 fakeUsageChecker := &fakeUsageChecker{ - checkBuildUsageFunc: func(_ context.Context, _ database.Store, templateVersion *database.TemplateVersion) (wsbuilder.UsageCheckResponse, error) { + checkBuildUsageFunc: func(_ context.Context, _ database.Store, _ *database.TemplateVersion, _ *database.Task, _ database.WorkspaceTransition) (wsbuilder.UsageCheckResponse, error) { atomic.AddInt64(&calls, 1) return c.response, c.responseErr }, @@ -1134,6 +1136,7 @@ func TestWorkspaceBuildUsageChecker(t *testing.T) { mDB := expectDB(t, withTemplate, + withNoTask, withInactiveVersionNoParams(), ) fc := files.New(prometheus.NewRegistry(), &coderdtest.FakeAuthorizer{}) @@ -1577,11 +1580,11 @@ func expectFindMatchingPresetID(id uuid.UUID, err error) func(mTx *dbmock.MockSt } type fakeUsageChecker struct { - checkBuildUsageFunc func(ctx context.Context, store database.Store, templateVersion *database.TemplateVersion) (wsbuilder.UsageCheckResponse, error) + checkBuildUsageFunc func(ctx context.Context, store database.Store, templateVersion *database.TemplateVersion, task *database.Task, transition database.WorkspaceTransition) (wsbuilder.UsageCheckResponse, error) } -func (f *fakeUsageChecker) CheckBuildUsage(ctx context.Context, store database.Store, templateVersion *database.TemplateVersion) (wsbuilder.UsageCheckResponse, error) { - return f.checkBuildUsageFunc(ctx, store, templateVersion) +func (f *fakeUsageChecker) CheckBuildUsage(ctx context.Context, store database.Store, templateVersion *database.TemplateVersion, task *database.Task, transition database.WorkspaceTransition) (wsbuilder.UsageCheckResponse, error) { + return f.checkBuildUsageFunc(ctx, store, templateVersion, task, transition) } func withNoTask(mTx *dbmock.MockStore) { diff --git a/codersdk/audit.go b/codersdk/audit.go index 0b2eca7d79d..434adde89f0 100644 --- a/codersdk/audit.go +++ b/codersdk/audit.go @@ -209,6 +209,7 @@ type AuditLogsRequest struct { type AuditLogResponse struct { AuditLogs []AuditLog `json:"audit_logs"` Count int64 `json:"count"` + CountCap int64 `json:"count_cap"` } type CreateTestAuditLogRequest struct { diff --git a/codersdk/connectionlog.go b/codersdk/connectionlog.go index 3e2acec6df6..61e1ccbb307 100644 --- a/codersdk/connectionlog.go +++ b/codersdk/connectionlog.go @@ -96,6 +96,7 @@ type ConnectionLogsRequest struct { type ConnectionLogResponse struct { ConnectionLogs []ConnectionLog `json:"connection_logs"` Count int64 `json:"count"` + CountCap int64 `json:"count_cap"` } func (c *Client) ConnectionLogs(ctx context.Context, req ConnectionLogsRequest) (ConnectionLogResponse, error) { diff --git a/codersdk/deployment.go b/codersdk/deployment.go index a51e3e9247e..7943e8f24fd 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -13,6 +13,7 @@ import ( "strconv" "strings" "time" + "unicode" "github.com/google/uuid" "golang.org/x/mod/semver" @@ -242,10 +243,6 @@ type Feature struct { // Below is only for features that use usage periods. - // SoftLimit is the soft limit of the feature, and is only used for showing - // included limits in the dashboard. No license validation or warnings are - // generated from this value. - SoftLimit *int64 `json:"soft_limit,omitempty"` // UsagePeriod denotes that the usage is a counter that accumulates over // this period (and most likely resets with the issuance of the next // license). @@ -536,18 +533,115 @@ func (c SSHConfig) ParseOptions() (map[string]string, error) { return m, nil } -// ParseSSHConfigOption parses a single ssh config option into it's key/value pair. +// ParseSSHConfigOption parses a single ssh config option into its key/value pair. func ParseSSHConfigOption(opt string) (key string, value string, err error) { - // An equal sign or whitespace is the separator between the key and value. + if strings.ContainsAny(opt, "\r\n\x00") { + return "", "", xerrors.Errorf("config-ssh option %q must not contain carriage return, newline, or NUL characters", opt) + } + + // An equal sign or a space is the separator between the key and value. idx := strings.IndexFunc(opt, func(r rune) bool { return r == ' ' || r == '=' }) if idx == -1 { - return "", "", xerrors.Errorf("invalid config-ssh option %q", opt) + return "", "", xerrors.Errorf("config-ssh option %q is missing a key/value separator ('=' or ' ')", opt) } return opt[:idx], opt[idx+1:], nil } +// isSingleHostPatternToken reports whether s is safe to write as a single SSH +// host pattern token. Whitespace or control characters could break out into +// additional SSH config directives. +func isSingleHostPatternToken(s string) bool { + return !strings.ContainsFunc(s, func(r rune) bool { + return unicode.IsSpace(r) || unicode.IsControl(r) + }) +} + +// ValidateWorkspaceHostnameSuffix validates a deployment-provided SSH hostname +// suffix before it is made available to clients. +func ValidateWorkspaceHostnameSuffix(suffix string) error { + // The suffix is implicitly prefixed with a dot when matching, so a leading + // dot is a config error: it forces the suffix to be a separate DNS label + // rather than an ordinary string suffix. E.g. "coder" matches "en.coder" + // but not "encoder". + if strings.HasPrefix(suffix, ".") { + return xerrors.Errorf("workspace hostname suffix %q must not start with a leading dot", suffix) + } + if strings.ContainsAny(suffix, "*?") { + return xerrors.Errorf("workspace hostname suffix %q must not contain glob characters", suffix) + } + if !isSingleHostPatternToken(suffix) { + return xerrors.Errorf("workspace hostname suffix %q must not contain whitespace or control characters", suffix) + } + return nil +} + +// ValidateWorkspaceHostnamePrefix validates a deployment-provided SSH hostname +// prefix before it is made available to clients. Unlike the suffix, a prefix +// may legitimately contain a trailing dot (the default is "coder."), so only +// the single-token requirement is enforced. +func ValidateWorkspaceHostnamePrefix(prefix string) error { + if !isSingleHostPatternToken(prefix) { + return xerrors.Errorf("workspace hostname prefix %q must not contain whitespace or control characters", prefix) + } + return nil +} + +// ValidateSSHConfigOptions validates deployment SSH settings before they are +// written to users' local SSH configs. +func ValidateSSHConfigOptions(options map[string]string) error { + // Sort the keys so that, when several options are invalid, the surfaced + // error is deterministic across restarts rather than dependent on map + // iteration order. + keys := make([]string, 0, len(options)) + for key := range options { + keys = append(keys, key) + } + slices.Sort(keys) + for _, key := range keys { + if err := ValidateSSHConfigOption(key, options[key]); err != nil { + return err + } + } + return nil +} + +// ValidateSSHConfigOption validates one deployment SSH option before it is +// written to users' local SSH configs. +func ValidateSSHConfigOption(key, value string) error { + if key == "" { + return xerrors.New("ssh config option key must not be empty") + } + if strings.ContainsAny(key, "=\r\n\x00") || strings.ContainsFunc(key, unicode.IsSpace) { + return xerrors.Errorf("ssh config option key %q is invalid", key) + } + // These options are rejected because, written into a user's SSH config by a + // deployment, they can execute code, load shared libraries, or override + // Coder's managed SSH settings on the client machine. When extending this + // list, classify the directive against these categories; the newline and + // whitespace checks above already prevent multi-line injection, so only + // single-line dangerous directives belong here. + switch strings.ToLower(key) { + // Structural directives that escape Coder's managed block. + case "host", "match", "include", + // Directives that run an attacker-supplied command string. + "proxycommand", "localcommand", "permitlocalcommand", "remotecommand", "knownhostscommand", + // Directives that dlopen an attacker-controlled shared library. + "pkcs11provider", "securitykeyprovider", "smartcarddevice", + // Directives that execute a command for X11 authentication. + "xauthlocation": + return xerrors.Errorf("ssh config option %q is not allowed: it can execute code, load shared libraries, or override Coder's managed SSH settings on client machines", key) + // ProxyJump conflicts with Coder's managed ProxyCommand. + case "proxyjump": + return xerrors.Errorf("ssh config option %q is not allowed: it conflicts with Coder's managed ProxyCommand", key) + } + if strings.ContainsAny(value, "\r\n\x00") { + return xerrors.Errorf("ssh config option %q must not contain carriage return, newline, or NUL characters", key) + } + return nil +} + // SessionLifetime refers to "sessions" authenticating into Coderd. Coder has // multiple different session types: api keys, tokens, workspace app tokens, // agent tokens, etc. This configuration struct should be used to group all @@ -675,6 +769,8 @@ type OIDCConfig struct { IconURL serpent.URL `json:"icon_url" typescript:",notnull"` SignupsDisabledText serpent.String `json:"signups_disabled_text" typescript:",notnull"` SkipIssuerChecks serpent.Bool `json:"skip_issuer_checks" typescript:",notnull"` + + AutoRepairLinks serpent.Bool `json:"auto_repair_links" typescript:",notnull"` } type TelemetryConfig struct { @@ -2154,6 +2250,23 @@ func (c *DeploymentValues) Options() serpent.OptionSet { Group: &deploymentGroupOIDC, YAML: "dangerousSkipIssuerChecks", }, + { + Name: "OIDC Auto Repair Links", + Description: "OIDC based users require the IdP issuer and subject in the claims to be static. " + + "If a new provider is configured, this option is required to be 'true'. It will reset any existing users to the " + + "previous provider, and match by email on their next login.", + Required: false, + Default: "true", + Flag: "oidc-repair-links", + Env: "CODER_OIDC_REPAIR_LINKS", + YAML: "oidc-repair-links", + Value: &c.OIDC.AutoRepairLinks, + Group: &deploymentGroupOIDC, + UseInstead: nil, + // This flag should be removed after validation in real deployments. Leaving it + // as a flag as an escape hatch for now. + Hidden: true, + }, // Telemetry settings telemetryEnable, { @@ -2516,7 +2629,7 @@ func (c *DeploymentValues) Options() serpent.OptionSet { Name: "Proxy Trusted Origins", Flag: "proxy-trusted-origins", Env: "CODER_PROXY_TRUSTED_ORIGINS", - Description: "Origin addresses to respect \"proxy-trusted-headers\". e.g. 192.168.1.0/24.", + Description: "Origin addresses to respect \"proxy-trusted-headers\" and X-Forwarded-Host for subdomain app routing. e.g. 192.168.1.0/24.", Value: &c.ProxyTrustedOrigins, Group: &deploymentGroupNetworking, YAML: "proxyTrustedOrigins", @@ -2751,7 +2864,7 @@ func (c *DeploymentValues) Options() serpent.OptionSet { }, { Name: "Workspace Hostname Suffix", - Description: "Workspace hostnames use this suffix in SSH config and Coder Connect on Coder Desktop. By default it is coder, resulting in names like myworkspace.coder.", + Description: "Workspace hostnames use this suffix in SSH config and Coder Connect on Coder Desktop. By default it is coder, resulting in names like myworkspace.coder. The suffix must not start with a dot, and must not contain spaces, newlines, or glob characters (* and ?).", Flag: "workspace-hostname-suffix", Env: "CODER_WORKSPACE_HOSTNAME_SUFFIX", YAML: "workspaceHostnameSuffix", @@ -2763,8 +2876,13 @@ func (c *DeploymentValues) Options() serpent.OptionSet { { Name: "SSH Config Options", Description: "These SSH config options will override the default SSH config options. " + - "Provide options in \"key=value\" or \"key value\" format separated by commas." + - "Using this incorrectly can break SSH to your deployment, use cautiously.", + "Provide options in \"key=value\" or \"key value\" format separated by commas. " + + "Using this incorrectly can break SSH to your deployment, use cautiously. " + + "The following options are not allowed: " + + "Host, Match, Include, ProxyCommand, ProxyJump, LocalCommand, PermitLocalCommand, " + + "RemoteCommand, KnownHostsCommand, PKCS11Provider, SecurityKeyProvider, " + + "SmartcardDevice, XAuthLocation. " + + "Option values must not contain newline, carriage return, or NUL characters.", Flag: "ssh-config-options", Env: "CODER_SSH_CONFIG_OPTIONS", YAML: "sshConfigOptions", @@ -3893,6 +4011,23 @@ type SSHConfigResponse struct { SSHConfigOptions map[string]string `json:"ssh_config_options"` } +// Validate checks that the deployment-provided SSH configuration is safe to +// write into a user's local SSH config. Validating here ensures a deployment +// can never serve config that the client would reject. +func (r SSHConfigResponse) Validate() error { + if r.HostnamePrefix != "" { + if err := ValidateWorkspaceHostnamePrefix(r.HostnamePrefix); err != nil { + return err + } + } + if r.HostnameSuffix != "" { + if err := ValidateWorkspaceHostnameSuffix(r.HostnameSuffix); err != nil { + return err + } + } + return ValidateSSHConfigOptions(r.SSHConfigOptions) +} + // SSHConfiguration returns information about the SSH configuration for the // Coder instance. func (c *Client) SSHConfiguration(ctx context.Context) (SSHConfigResponse, error) { diff --git a/codersdk/deployment_test.go b/codersdk/deployment_test.go index ee3f7365d62..52335128790 100644 --- a/codersdk/deployment_test.go +++ b/codersdk/deployment_test.go @@ -134,6 +134,270 @@ func TestDeploymentValues_HighlyConfigurable(t *testing.T) { } } +func TestParseSSHConfigOption(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + option string + wantKey string + wantValue string + wantErr bool + }{ + { + name: "ProxyCommandWithSpaces", + option: "ProxyCommand=ssh -W %h:%p bastion", + wantKey: "ProxyCommand", + wantValue: "ssh -W %h:%p bastion", + }, + { + name: "SetEnvWithEquals", + option: "SetEnv=FOO=bar BAZ=qux", + wantKey: "SetEnv", + wantValue: "FOO=bar BAZ=qux", + }, + { + name: "SetEnvWithSpaceSeparator", + option: "SetEnv FOO=bar BAZ=qux", + wantKey: "SetEnv", + wantValue: "FOO=bar BAZ=qux", + }, + { + name: "HostName", + option: "HostName example.com", + wantKey: "HostName", + wantValue: "example.com", + }, + { + name: "NewlineInValue", + option: "ProxyCommand=echo hi\nHost *", + wantErr: true, + }, + { + name: "CarriageReturnInValue", + option: "ProxyCommand=echo hi\rHost *", + wantErr: true, + }, + { + name: "NULInValue", + option: "ProxyCommand=echo hi\x00Host *", + wantErr: true, + }, + { + name: "NewlineInKey", + option: "Proxy\nCommand=value", + wantErr: true, + }, + { + name: "CarriageReturnInKey", + option: "Proxy\rCommand=value", + wantErr: true, + }, + { + name: "NULInKey", + option: "Proxy\x00Command=value", + wantErr: true, + }, + { + name: "MissingSeparator", + option: "JustAKeyNoValue", + wantErr: true, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + key, value, err := codersdk.ParseSSHConfigOption(tt.option) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, tt.wantKey, key) + require.Equal(t, tt.wantValue, value) + }) + } +} + +func TestValidateWorkspaceHostnameSuffix(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + suffix string + wantErr bool + }{ + {name: "Coder", suffix: "coder"}, + {name: "Example", suffix: "example"}, + {name: "Dotted", suffix: "coder.example.com"}, + {name: "Empty", suffix: ""}, + {name: "LeadingDot", suffix: ".coder", wantErr: true}, + {name: "Newline", suffix: "coder\nHost *\n\tProxyCommand evil", wantErr: true}, + {name: "CarriageReturn", suffix: "coder\r\nHost *", wantErr: true}, + {name: "Space", suffix: "coder Host *", wantErr: true}, + {name: "Tab", suffix: "coder\t*", wantErr: true}, + {name: "NUL", suffix: "coder\x00", wantErr: true}, + {name: "NonBreakingSpace", suffix: "coder\u00A0suffix", wantErr: true}, + {name: "Glob", suffix: "*", wantErr: true}, + {name: "GlobPrefix", suffix: "*.*", wantErr: true}, + {name: "QuestionMark", suffix: "code?", wantErr: true}, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + err := codersdk.ValidateWorkspaceHostnameSuffix(tt.suffix) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) + } +} + +func TestValidateWorkspaceHostnamePrefix(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + prefix string + wantErr bool + }{ + {name: "Default", prefix: "coder."}, + {name: "NoDot", prefix: "coder"}, + {name: "Empty", prefix: ""}, + {name: "LeadingDot", prefix: ".coder"}, + {name: "Newline", prefix: "coder.\nHost *\n\tProxyCommand evil", wantErr: true}, + {name: "CarriageReturn", prefix: "coder.\r\nHost *", wantErr: true}, + {name: "Space", prefix: "coder. Host *", wantErr: true}, + {name: "Tab", prefix: "coder.\t*", wantErr: true}, + {name: "NUL", prefix: "coder.\x00", wantErr: true}, + {name: "NonBreakingSpace", prefix: "coder.\u00A0x", wantErr: true}, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + err := codersdk.ValidateWorkspaceHostnamePrefix(tt.prefix) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) + } +} + +func TestValidateSSHConfigOptions(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + options map[string]string + wantErr bool + }{ + {name: "HostName", options: map[string]string{"HostName": "example.com"}}, + {name: "User", options: map[string]string{"User": "coder"}}, + {name: "Port", options: map[string]string{"Port": "22"}}, + {name: "SetEnv", options: map[string]string{"SetEnv": "FOO=bar BAZ=qux"}}, + {name: "UserKnownHostsFile", options: map[string]string{"UserKnownHostsFile": "/tmp/coder_known_hosts"}}, + {name: "EmptyKey", options: map[string]string{"": "value"}, wantErr: true}, + {name: "NewlineInKey", options: map[string]string{"User\nProxyCommand": "evil"}, wantErr: true}, + {name: "CarriageReturnInKey", options: map[string]string{"User\rProxyCommand": "evil"}, wantErr: true}, + {name: "NULInKey", options: map[string]string{"User\x00ProxyCommand": "evil"}, wantErr: true}, + {name: "SpaceInKey", options: map[string]string{"User ProxyCommand": "evil"}, wantErr: true}, + {name: "EqualsInKey", options: map[string]string{"User=ProxyCommand": "evil"}, wantErr: true}, + {name: "Host", options: map[string]string{"Host": "*"}, wantErr: true}, + {name: "HostCaseInsensitive", options: map[string]string{"hOsT": "*"}, wantErr: true}, + {name: "Match", options: map[string]string{"Match": "all"}, wantErr: true}, + {name: "Include", options: map[string]string{"Include": "~/.ssh/config.d/*"}, wantErr: true}, + {name: "ProxyCommand", options: map[string]string{"ProxyCommand": "ssh -W %h:%p bastion"}, wantErr: true}, + {name: "ProxyCommandCaseInsensitive", options: map[string]string{"proxycommand": "ssh -W %h:%p bastion"}, wantErr: true}, + {name: "LocalCommand", options: map[string]string{"LocalCommand": "echo pwned"}, wantErr: true}, + {name: "PermitLocalCommand", options: map[string]string{"PermitLocalCommand": "yes"}, wantErr: true}, + {name: "RemoteCommand", options: map[string]string{"RemoteCommand": "some-command"}, wantErr: true}, + {name: "KnownHostsCommand", options: map[string]string{"KnownHostsCommand": "echo key"}, wantErr: true}, + {name: "PKCS11Provider", options: map[string]string{"PKCS11Provider": "/tmp/evil.so"}, wantErr: true}, + {name: "PKCS11ProviderCaseInsensitive", options: map[string]string{"pkcs11provider": "/tmp/evil.so"}, wantErr: true}, + {name: "SecurityKeyProvider", options: map[string]string{"SecurityKeyProvider": "/tmp/evil.so"}, wantErr: true}, + {name: "NewlineInValue", options: map[string]string{"UserKnownHostsFile": "/tmp/known_hosts\nHost *\nProxyCommand evil"}, wantErr: true}, + {name: "CarriageReturnInValue", options: map[string]string{"UserKnownHostsFile": "/tmp/known_hosts\r\nHost *"}, wantErr: true}, + {name: "NULInValue", options: map[string]string{"UserKnownHostsFile": "/tmp/known_hosts\x00suffix"}, wantErr: true}, + {name: "SmartcardDevice", options: map[string]string{"SmartcardDevice": "/path/to/lib"}, wantErr: true}, + {name: "XAuthLocation", options: map[string]string{"XAuthLocation": "/usr/bin/xauth"}, wantErr: true}, + {name: "ProxyJump", options: map[string]string{"ProxyJump": "bastion.example.com"}, wantErr: true}, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + err := codersdk.ValidateSSHConfigOptions(tt.options) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) + } +} + +func TestSSHConfigResponse_Validate(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + response codersdk.SSHConfigResponse + wantErr string + }{ + { + name: "Valid", + response: codersdk.SSHConfigResponse{ + HostnamePrefix: "coder.", + HostnameSuffix: "coder", + SSHConfigOptions: map[string]string{"HostName": "example.com"}, + }, + }, + { + name: "Empty", + response: codersdk.SSHConfigResponse{}, + }, + { + name: "PrefixUnsafe", + response: codersdk.SSHConfigResponse{HostnamePrefix: "coder.\nHost *"}, + wantErr: "workspace hostname prefix", + }, + { + name: "SuffixUnsafe", + response: codersdk.SSHConfigResponse{HostnameSuffix: "coder\nHost *"}, + wantErr: "workspace hostname suffix", + }, + { + name: "OptionUnsafe", + response: codersdk.SSHConfigResponse{SSHConfigOptions: map[string]string{"ProxyCommand": "ssh -W %h:%p bastion"}}, + wantErr: `ssh config option "ProxyCommand" is not allowed`, + }, + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + err := tt.response.Validate() + if tt.wantErr != "" { + require.ErrorContains(t, err, tt.wantErr) + return + } + require.NoError(t, err) + }) + } +} + func TestSSHConfig_ParseOptions(t *testing.T) { t.Parallel() diff --git a/codersdk/drpcsdk/server.go b/codersdk/drpcsdk/server.go new file mode 100644 index 00000000000..a649b742840 --- /dev/null +++ b/codersdk/drpcsdk/server.go @@ -0,0 +1,39 @@ +package drpcsdk + +import ( + "runtime/debug" + + "storj.io/drpc" + "storj.io/drpc/drpcserver" + + "cdr.dev/slog" +) + +// NewServer constructs a dRPC server that recovers panics from RPC handlers. +func NewServer(logger slog.Logger, handler drpc.Handler, options drpcserver.Options) *drpcserver.Server { + return drpcserver.NewWithOptions(&recoverHandler{ + logger: logger, + handler: handler, + }, options) +} + +type recoverHandler struct { + logger slog.Logger + handler drpc.Handler +} + +func (h *recoverHandler) HandleRPC(stream drpc.Stream, rpc string) (err error) { + defer func() { + if r := recover(); r != nil { + h.logger.Error(stream.Context(), + "panic serving dRPC request (recovered)", + slog.F("rpc", rpc), + slog.F("panic", r), + slog.F("stack", string(debug.Stack())), + ) + err = drpc.InternalError.New("panic serving dRPC request") + } + }() + + return h.handler.HandleRPC(stream, rpc) +} diff --git a/codersdk/drpcsdk/server_internal_test.go b/codersdk/drpcsdk/server_internal_test.go new file mode 100644 index 00000000000..f242f1608a6 --- /dev/null +++ b/codersdk/drpcsdk/server_internal_test.go @@ -0,0 +1,98 @@ +package drpcsdk + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + "golang.org/x/xerrors" + "storj.io/drpc" + + "cdr.dev/slog" +) + +func TestRecoverHandler(t *testing.T) { + t.Parallel() + + t.Run("Panic", func(t *testing.T) { + t.Parallel() + + const panicValue = "sensitive panic details" + sink := &recordingSink{} + handler := &recoverHandler{ + logger: slog.Make(sink), + handler: handlerFunc(func(drpc.Stream, string) error { + panic(panicValue) + }), + } + + err := handler.HandleRPC(contextStream{ctx: t.Context()}, "/test.Service/Panic") + require.Error(t, err) + require.True(t, drpc.InternalError.Has(err)) + require.NotContains(t, err.Error(), panicValue) + + entries := sink.Entries() + require.Len(t, entries, 1) + require.Equal(t, slog.LevelError, entries[0].Level) + require.Equal(t, "panic serving dRPC request (recovered)", entries[0].Message) + require.Equal(t, "/test.Service/Panic", fieldValue(entries[0].Fields, "rpc")) + require.Equal(t, panicValue, fieldValue(entries[0].Fields, "panic")) + stackValue := fieldValue(entries[0].Fields, "stack") + stack, ok := stackValue.(string) + require.True(t, ok, "stack field must be a string, got %T", stackValue) + require.Contains(t, stack, "goroutine ") + }) + + t.Run("Error", func(t *testing.T) { + t.Parallel() + + expected := xerrors.New("handler error") + handler := &recoverHandler{ + handler: handlerFunc(func(drpc.Stream, string) error { + return expected + }), + } + + err := handler.HandleRPC(contextStream{ctx: t.Context()}, "/test.Service/Error") + require.ErrorIs(t, err, expected) + }) +} + +type recordingSink struct { + entries []slog.SinkEntry +} + +func (s *recordingSink) LogEntry(_ context.Context, entry slog.SinkEntry) { + s.entries = append(s.entries, entry) +} + +func (*recordingSink) Sync() {} + +func (s *recordingSink) Entries() []slog.SinkEntry { + return append([]slog.SinkEntry(nil), s.entries...) +} + +type handlerFunc func(drpc.Stream, string) error + +func (f handlerFunc) HandleRPC(stream drpc.Stream, rpc string) error { + return f(stream, rpc) +} + +type contextStream struct { + ctx context.Context +} + +func (s contextStream) Context() context.Context { return s.ctx } +func (contextStream) MsgSend(drpc.Message, drpc.Encoding) error { return nil } +func (contextStream) MsgRecv(drpc.Message, drpc.Encoding) error { return nil } +func (contextStream) CloseSend() error { return nil } +func (contextStream) Close() error { return nil } + +func fieldValue(fields slog.Map, name string) any { + for _, field := range fields { + if field.Name == name { + return field.Value + } + } + return nil +} diff --git a/codersdk/drpcsdk/server_test.go b/codersdk/drpcsdk/server_test.go new file mode 100644 index 00000000000..34034fb8e37 --- /dev/null +++ b/codersdk/drpcsdk/server_test.go @@ -0,0 +1,95 @@ +package drpcsdk_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + "golang.org/x/xerrors" + "storj.io/drpc" + "storj.io/drpc/drpcserver" + + "cdr.dev/slog/sloggers/slogtest" + "github.com/coder/coder/v2/codersdk/drpcsdk" + "github.com/coder/coder/v2/testutil" +) + +func TestNewServerRecoversPanics(t *testing.T) { + t.Parallel() + + const ( + panicRPC = "/test.Service/Panic" + echoRPC = "/test.Service/Echo" + panicValue = "sensitive panic details" + ) + + ctx := testutil.Context(t, testutil.WaitShort) + serverCtx, cancel := context.WithCancel(ctx) + defer cancel() + + client, listener := drpcsdk.MemTransportPipe() + defer func() { + _ = client.Close() + _ = listener.Close() + }() + + handler := testHandlerFunc(func(stream drpc.Stream, rpc string) error { + switch rpc { + case panicRPC: + panic(panicValue) + case echoRPC: + var message string + if err := stream.MsgRecv(&message, stringEncoding{}); err != nil { + return err + } + return stream.MsgSend(&message, stringEncoding{}) + default: + return xerrors.Errorf("unexpected RPC %q", rpc) + } + }) + server := drpcsdk.NewServer(slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}), handler, drpcserver.Options{ + Manager: drpcsdk.DefaultDRPCOptions(nil), + }) + serverDone := make(chan error, 1) + go func() { + serverDone <- server.Serve(serverCtx, listener) + }() + + request, response := "request", "" + err := client.Invoke(ctx, panicRPC, stringEncoding{}, &request, &response) + require.EqualError(t, err, "internal error: panic serving dRPC request") + require.NotContains(t, err.Error(), panicValue) + + request, response = "healthy", "" + err = client.Invoke(ctx, echoRPC, stringEncoding{}, &request, &response) + require.NoError(t, err) + require.Equal(t, request, response) + + cancel() + require.NoError(t, testutil.RequireReceive(ctx, t, serverDone)) +} + +type testHandlerFunc func(drpc.Stream, string) error + +func (f testHandlerFunc) HandleRPC(stream drpc.Stream, rpc string) error { + return f(stream, rpc) +} + +type stringEncoding struct{} + +func (stringEncoding) Marshal(message drpc.Message) ([]byte, error) { + value, ok := message.(*string) + if !ok { + return nil, xerrors.Errorf("marshal %T: expected *string", message) + } + return []byte(*value), nil +} + +func (stringEncoding) Unmarshal(data []byte, message drpc.Message) error { + value, ok := message.(*string) + if !ok { + return xerrors.Errorf("unmarshal %T: expected *string", message) + } + *value = string(data) + return nil +} diff --git a/codersdk/licenses.go b/codersdk/licenses.go index 4863aad60c6..da90f92543f 100644 --- a/codersdk/licenses.go +++ b/codersdk/licenses.go @@ -12,8 +12,9 @@ import ( ) const ( - LicenseExpiryClaim = "license_expires" - LicenseTelemetryRequiredErrorText = "License requires telemetry but telemetry is disabled" + LicenseExpiryClaim = "license_expires" + LicenseTelemetryRequiredErrorText = "License requires telemetry but telemetry is disabled" + LicenseManagedAgentLimitExceededWarningText = "You have built more workspaces with managed agents than your license allows." ) type AddLicenseRequest struct { diff --git a/codersdk/name.go b/codersdk/name.go index 8942e08cafe..b044fee7e83 100644 --- a/codersdk/name.go +++ b/codersdk/name.go @@ -5,8 +5,9 @@ import ( "regexp" "strings" - "github.com/moby/moby/pkg/namesgenerator" "golang.org/x/xerrors" + + "github.com/coder/coder/v2/coderd/util/namesgenerator" ) var ( @@ -35,7 +36,7 @@ func UsernameFrom(str string) string { if valid := NameValid(str); valid == nil { return str } - return strings.ReplaceAll(namesgenerator.GetRandomName(1), "_", "-") + return namesgenerator.NameDigitWith("-") } // NameValid returns whether the input string is a valid name. diff --git a/codersdk/users.go b/codersdk/users.go index 44464f9476d..1bf09370d9a 100644 --- a/codersdk/users.go +++ b/codersdk/users.go @@ -217,6 +217,14 @@ type UpdateUserAppearanceSettingsRequest struct { TerminalFont TerminalFontName `json:"terminal_font" validate:"required"` } +type UserPreferenceSettings struct { + TaskNotificationAlertDismissed bool `json:"task_notification_alert_dismissed"` +} + +type UpdateUserPreferenceSettingsRequest struct { + TaskNotificationAlertDismissed bool `json:"task_notification_alert_dismissed"` +} + type UpdateUserPasswordRequest struct { OldPassword string `json:"old_password" validate:""` Password string `json:"password" validate:"required"` @@ -514,6 +522,34 @@ func (c *Client) UpdateUserAppearanceSettings(ctx context.Context, user string, return resp, json.NewDecoder(res.Body).Decode(&resp) } +// GetUserPreferenceSettings fetches the preference settings for a user. +func (c *Client) GetUserPreferenceSettings(ctx context.Context, user string) (UserPreferenceSettings, error) { + res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/preferences", user), nil) + if err != nil { + return UserPreferenceSettings{}, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return UserPreferenceSettings{}, ReadBodyAsError(res) + } + var resp UserPreferenceSettings + return resp, json.NewDecoder(res.Body).Decode(&resp) +} + +// UpdateUserPreferenceSettings updates the preference settings for a user. +func (c *Client) UpdateUserPreferenceSettings(ctx context.Context, user string, req UpdateUserPreferenceSettingsRequest) (UserPreferenceSettings, error) { + res, err := c.Request(ctx, http.MethodPut, fmt.Sprintf("/api/v2/users/%s/preferences", user), req) + if err != nil { + return UserPreferenceSettings{}, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return UserPreferenceSettings{}, ReadBodyAsError(res) + } + var resp UserPreferenceSettings + return resp, json.NewDecoder(res.Body).Decode(&resp) +} + // UpdateUserPassword updates a user password. // It calls PUT /users/{user}/password func (c *Client) UpdateUserPassword(ctx context.Context, user string, req UpdateUserPasswordRequest) error { diff --git a/codersdk/workspacebuilds.go b/codersdk/workspacebuilds.go index a91148ab2ad..78efbb4eaa7 100644 --- a/codersdk/workspacebuilds.go +++ b/codersdk/workspacebuilds.go @@ -188,6 +188,28 @@ func (c *Client) WorkspaceBuildState(ctx context.Context, build uuid.UUID) ([]by return io.ReadAll(res.Body) } +// UpdateWorkspaceBuildStateRequest is the request body for updating the +// provisioner state of a workspace build. +type UpdateWorkspaceBuildStateRequest struct { + State []byte `json:"state"` +} + +// UpdateWorkspaceBuildState updates the provisioner state of the build without +// triggering a new build. This is useful for state-only migrations. +func (c *Client) UpdateWorkspaceBuildState(ctx context.Context, build uuid.UUID, state []byte) error { + res, err := c.Request(ctx, http.MethodPut, fmt.Sprintf("/api/v2/workspacebuilds/%s/state", build), UpdateWorkspaceBuildStateRequest{ + State: state, + }) + if err != nil { + return err + } + defer res.Body.Close() + if res.StatusCode != http.StatusNoContent { + return ReadBodyAsError(res) + } + return nil +} + func (c *Client) WorkspaceBuildByUsernameAndWorkspaceNameAndBuildNumber(ctx context.Context, username string, workspaceName string, buildNumber string) (WorkspaceBuild, error) { res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/workspace/%s/builds/%s", username, workspaceName, buildNumber), nil) if err != nil { diff --git a/codersdk/workspacesdk/agentconn.go b/codersdk/workspacesdk/agentconn.go index dbfb833e445..6a336fe828c 100644 --- a/codersdk/workspacesdk/agentconn.go +++ b/codersdk/workspacesdk/agentconn.go @@ -53,6 +53,7 @@ type AgentConn interface { DebugMagicsock(ctx context.Context) ([]byte, error) DebugManifest(ctx context.Context) ([]byte, error) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) + AppHTTPClient() *http.Client GetPeerDiagnostics() tailnet.PeerDiagnostics ListContainers(ctx context.Context) (codersdk.WorkspaceAgentListContainersResponse, error) ListeningPorts(ctx context.Context) (codersdk.WorkspaceAgentListeningPortsResponse, error) @@ -88,6 +89,7 @@ func (c *agentConn) TailnetConn() *tailnet.Conn { type AgentConnOptions struct { AgentID uuid.UUID CloseFunc func() error + Logger slog.Logger } func (c *agentConn) agentAddress() netip.Addr { @@ -300,6 +302,24 @@ func (c *agentConn) DialContext(ctx context.Context, network string, addr string } } +// AppHTTPClient returns an HTTP client for reaching HTTP apps served by this +// workspace agent. Redirects are blocked to prevent misuse. +func (c *agentConn) AppHTTPClient() *http.Client { + return &http.Client{ + CheckRedirect: func(*http.Request, []*http.Request) error { + return http.ErrUseLastResponse + }, + Transport: &http.Transport{ + // Disable keep-alives so these short-lived clients don't leave + // idle connections (and their goroutines) lingering after they're + // discarded. + DisableKeepAlives: true, + // Host locked to agent, port from URL. + DialContext: c.DialContext, + }, + } +} + // ListeningPorts lists the ports that are currently in use by the workspace. func (c *agentConn) ListeningPorts(ctx context.Context) (codersdk.WorkspaceAgentListeningPortsResponse, error) { ctx, span := tracing.StartSpan(ctx) @@ -654,7 +674,12 @@ func (c *agentConn) apiRequest(ctx context.Context, method, path string, body in // apiClient returns an HTTP client that can be used to make // requests to the workspace agent's HTTP API server. func (c *agentConn) apiClient() *http.Client { + agentAddr := netip.AddrPortFrom(c.agentAddress(), AgentHTTPAPIServerPort) return &http.Client{ + // Redirects are blocked to prevent misuse. + CheckRedirect: func(*http.Request, []*http.Request) error { + return http.ErrUseLastResponse + }, Transport: &http.Transport{ // Disable keep alives as we're usually only making a single // request, and this triggers goleak in tests @@ -674,16 +699,21 @@ func (c *agentConn) apiClient() *http.Client { return nil, xerrors.Errorf("request %q does not appear to be for http api", addr) } - if !c.AwaitReachable(ctx) { - return nil, xerrors.Errorf("workspace agent not reachable in time: %v", ctx.Err()) + if reqAddr, err := netip.ParseAddr(host); err != nil || reqAddr != agentAddr.Addr() { + c.opts.Logger.Warn(ctx, "blocked workspace agent API request to unintended host", + slog.F("agent_id", c.opts.AgentID), + slog.F("request_host", host), + slog.F("intended_agent_addr", agentAddr.Addr()), + ) + return nil, xerrors.Errorf("request host %q does not match intended agent %q", host, agentAddr.Addr()) } - ipAddr, err := netip.ParseAddr(host) - if err != nil { - return nil, xerrors.Errorf("parse host addr: %w", err) + if !c.AwaitReachable(ctx) { + return nil, xerrors.Errorf("workspace agent not reachable in time: %v", ctx.Err()) } - conn, err := c.Conn.DialContextTCP(ctx, netip.AddrPortFrom(ipAddr, AgentHTTPAPIServerPort)) + // Always dial the pinned agent address, never the request host. + conn, err := c.Conn.DialContextTCP(ctx, agentAddr) if err != nil { return nil, xerrors.Errorf("dial http api: %w", err) } diff --git a/codersdk/workspacesdk/agentconn_test.go b/codersdk/workspacesdk/agentconn_test.go new file mode 100644 index 00000000000..7407be46f22 --- /dev/null +++ b/codersdk/workspacesdk/agentconn_test.go @@ -0,0 +1,212 @@ +package workspacesdk_test + +import ( + "context" + "errors" + "fmt" + "net" + "net/http" + "net/netip" + "strings" + "sync/atomic" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "tailscale.com/tailcfg" + + "github.com/coder/coder/v2/codersdk/workspacesdk" + "github.com/coder/coder/v2/tailnet" + "github.com/coder/coder/v2/tailnet/proto" + "github.com/coder/coder/v2/tailnet/tailnettest" + "github.com/coder/coder/v2/testutil" +) + +func TestAgentConnRejectsCrossAgentRedirects(t *testing.T) { + t.Parallel() + + derpMap, _ := tailnettest.RunDERPAndSTUN(t) + cases := []struct { + name string + status int + invoke func(context.Context, workspacesdk.AgentConn) error + }{ + { + name: "get 302", + status: http.StatusFound, + invoke: func(ctx context.Context, conn workspacesdk.AgentConn) error { + _, err := conn.ListeningPorts(ctx) + return err + }, + }, + { + name: "post 307", + status: http.StatusTemporaryRedirect, + invoke: func(ctx context.Context, conn workspacesdk.AgentConn) error { + return conn.WriteFile(ctx, "/tmp/attacker", strings.NewReader("redirect-body")) + }, + }, + { + name: "post 308", + status: http.StatusPermanentRedirect, + invoke: func(ctx context.Context, conn workspacesdk.AgentConn) error { + return conn.WriteFile(ctx, "/tmp/attacker", strings.NewReader("redirect-body")) + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + ctx := testutil.Context(t, testutil.WaitMedium) + + clientID := uuid.New() + attackerID := uuid.New() + victimID := uuid.New() + clientConn, _ := newTailnetConn(t, derpMap, clientID, "client") + attackerConn, attackerIP := newTailnetConn(t, derpMap, attackerID, "attacker") + victimConn, victimIP := newTailnetConn(t, derpMap, victimID, "victim") + stitchTailnet(t, map[uuid.UUID]*tailnet.Conn{ + clientID: clientConn, + attackerID: attackerConn, + victimID: victimConn, + }) + + var victimHit atomic.Bool + victimRouter := http.NewServeMux() + victimRouter.HandleFunc("/api/v0/listening-ports", func(rw http.ResponseWriter, _ *http.Request) { + victimHit.Store(true) + rw.Header().Set("Content-Type", "application/json") + _, _ = rw.Write([]byte(`{"ports":[]}`)) + }) + victimRouter.HandleFunc("/api/v0/write-file", func(rw http.ResponseWriter, _ *http.Request) { + victimHit.Store(true) + rw.WriteHeader(http.StatusOK) + }) + serveTailnetHTTP(t, victimConn, victimRouter) + + victimBaseURL := fmt.Sprintf("http://[%s]:%d", victimIP, workspacesdk.AgentHTTPAPIServerPort) + attackerRouter := http.NewServeMux() + attackerRouter.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) { + http.Redirect(rw, r, victimBaseURL+r.URL.RequestURI(), tc.status) + }) + serveTailnetHTTP(t, attackerConn, attackerRouter) + + require.True(t, clientConn.AwaitReachable(ctx, attackerIP)) + require.True(t, clientConn.AwaitReachable(ctx, victimIP)) + + conn := workspacesdk.NewAgentConn(clientConn, workspacesdk.AgentConnOptions{ + AgentID: attackerID, + }) + + err := tc.invoke(ctx, conn) + require.Error(t, err) + require.False(t, victimHit.Load()) + }) + } +} + +// TestAgentConnAppHTTPClientRefusesRedirects verifies the app HTTP client does +// not follow redirects. +func TestAgentConnAppHTTPClientRefusesRedirects(t *testing.T) { + t.Parallel() + + tailnetConn, err := tailnet.NewConn(&tailnet.Options{ + Addresses: []netip.Prefix{tailnet.TailscaleServicePrefix.RandomPrefix()}, + Logger: testutil.Logger(t), + }) + require.NoError(t, err) + t.Cleanup(func() { + _ = tailnetConn.Close() + }) + + conn := workspacesdk.NewAgentConn(tailnetConn, workspacesdk.AgentConnOptions{ + AgentID: uuid.New(), + }) + + client := conn.AppHTTPClient() + require.NotNil(t, client.CheckRedirect) + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "http://example.invalid/", nil) + require.NoError(t, err) + require.ErrorIs(t, client.CheckRedirect(req, nil), http.ErrUseLastResponse) +} + +func newTailnetConn(t *testing.T, derpMap *tailcfg.DERPMap, id uuid.UUID, name string) (*tailnet.Conn, netip.Addr) { + t.Helper() + + addr := tailnet.TailscaleServicePrefix.AddrFromUUID(id) + conn, err := tailnet.NewConn(&tailnet.Options{ + ID: id, + Addresses: []netip.Prefix{netip.PrefixFrom(addr, 128)}, + Logger: testutil.Logger(t).Named(name), + DERPMap: derpMap, + }) + require.NoError(t, err) + t.Cleanup(func() { + assert.NoError(t, conn.Close()) + }) + + return conn, addr +} + +func serveTailnetHTTP(t *testing.T, conn *tailnet.Conn, handler http.Handler) { + t.Helper() + + ln, err := conn.Listen("tcp", fmt.Sprintf(":%d", workspacesdk.AgentHTTPAPIServerPort)) + require.NoError(t, err) + + server := &http.Server{Handler: handler, ReadHeaderTimeout: testutil.WaitShort} + t.Cleanup(func() { + assert.NoError(t, server.Close()) + assert.NoError(t, ln.Close()) + }) + + go func() { + err := server.Serve(ln) + if err != nil && !errors.Is(err, net.ErrClosed) && !errors.Is(err, http.ErrServerClosed) { + assert.NoError(t, err) + } + }() +} + +// stitchTailnet cross-programs every conn's node into every other conn, the +// N-peer analog of tailnet's stitch test helper, so the peers can reach each +// other without a coordinator. +func stitchTailnet(t *testing.T, conns map[uuid.UUID]*tailnet.Conn) { + t.Helper() + + sendNode := func(srcID uuid.UUID, node *tailnet.Node) { + protoNode, err := tailnet.NodeToProto(node) + if !assert.NoError(t, err) { + return + } + for dstID, dst := range conns { + if dstID == srcID { + continue + } + err = dst.UpdatePeers([]*proto.CoordinateResponse_PeerUpdate{{ + Id: srcID[:], + Node: protoNode, + Kind: proto.CoordinateResponse_PeerUpdate_NODE, + }}) + assert.NoError(t, err) + } + } + + for srcID, src := range conns { + src.SetNodeCallback(func(node *tailnet.Node) { + sendNode(srcID, node) + }) + if node := src.Node(); node != nil { + sendNode(srcID, node) + } + } + + t.Cleanup(func() { + for _, conn := range conns { + conn.SetNodeCallback(nil) + } + }) +} diff --git a/codersdk/workspacesdk/agentconnmock/agentconnmock.go b/codersdk/workspacesdk/agentconnmock/agentconnmock.go index cf6b4c72bea..46cf6bcf09b 100644 --- a/codersdk/workspacesdk/agentconnmock/agentconnmock.go +++ b/codersdk/workspacesdk/agentconnmock/agentconnmock.go @@ -13,6 +13,7 @@ import ( context "context" io "io" net "net" + http "net/http" reflect "reflect" time "time" @@ -53,6 +54,20 @@ func (m *MockAgentConn) EXPECT() *MockAgentConnMockRecorder { return m.recorder } +// AppHTTPClient mocks base method. +func (m *MockAgentConn) AppHTTPClient() *http.Client { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AppHTTPClient") + ret0, _ := ret[0].(*http.Client) + return ret0 +} + +// AppHTTPClient indicates an expected call of AppHTTPClient. +func (mr *MockAgentConnMockRecorder) AppHTTPClient() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppHTTPClient", reflect.TypeOf((*MockAgentConn)(nil).AppHTTPClient)) +} + // AwaitReachable mocks base method. func (m *MockAgentConn) AwaitReachable(ctx context.Context) bool { m.ctrl.T.Helper() diff --git a/codersdk/workspacesdk/workspacesdk.go b/codersdk/workspacesdk/workspacesdk.go index 29ddbd1f530..d7c1b6500a4 100644 --- a/codersdk/workspacesdk/workspacesdk.go +++ b/codersdk/workspacesdk/workspacesdk.go @@ -299,6 +299,7 @@ func (c *Client) DialAgent(dialCtx context.Context, agentID uuid.UUID, options * <-controller.Closed() return conn.Close() }, + Logger: options.Logger, }) if !agentConn.AwaitReachable(dialCtx) { diff --git a/docs/about/contributing/modules.md b/docs/about/contributing/modules.md index 05d06e9299f..835729c3065 100644 --- a/docs/about/contributing/modules.md +++ b/docs/about/contributing/modules.md @@ -342,7 +342,7 @@ Use the version bump script to update versions: ```bash git add . - git commit -m "feat(git-clone):add git-clone module" + git commit -m "feat(git-clone): add git-clone module" ``` 4. **Open a pull request**: diff --git a/docs/admin/infrastructure/scale-utility.md b/docs/admin/infrastructure/scale-utility.md index 6945b54bf55..0761b23a0ff 100644 --- a/docs/admin/infrastructure/scale-utility.md +++ b/docs/admin/infrastructure/scale-utility.md @@ -130,7 +130,7 @@ wish to clean up all workspaces, you can run the following command: ```shell coder exp scaletest cleanup \ --cleanup-job-timeout 2h \ - --cleanup-timeout 15min + --cleanup-timeout 15m ``` This will delete all workspaces and users with the prefix `scaletest-`. diff --git a/docs/admin/integrations/dx-data-cloud.md b/docs/admin/integrations/dx-data-cloud.md index 3556370535f..51ae222f961 100644 --- a/docs/admin/integrations/dx-data-cloud.md +++ b/docs/admin/integrations/dx-data-cloud.md @@ -30,10 +30,11 @@ If your organization already uses the Coder-DX integration, you can find a list ### CLI -Use `users list` to export the list of users to a CSV file: +Use `users list` with `jq` to export the list of users to a CSV file: ```shell -coder users list > users.csv +coder users list --output json | \ + jq -r '["username","email","created_at","status"], (.[] | [.username, .email, .created_at, .status]) | @csv' > users.csv ``` Visit the [users list](../../reference/cli/users_list.md) documentation for more options. diff --git a/docs/admin/monitoring/notifications/slack.md b/docs/admin/monitoring/notifications/slack.md index 99d5045656b..c521a546c83 100644 --- a/docs/admin/monitoring/notifications/slack.md +++ b/docs/admin/monitoring/notifications/slack.md @@ -196,7 +196,7 @@ To enable webhook integration in Coder, define the POST webhook endpoint matching the deployed Slack bot: ```bash -export CODER_NOTIFICATIONS_WEBHOOK_ENDPOINT=http://localhost:6000/v1/webhook` +export CODER_NOTIFICATIONS_WEBHOOK_ENDPOINT=http://localhost:6000/v1/webhook ``` Finally, go to the **Notification Settings** in Coder and switch the notifier to diff --git a/docs/admin/networking/port-forwarding.md b/docs/admin/networking/port-forwarding.md index 4f117775a4e..a028b17aadd 100644 --- a/docs/admin/networking/port-forwarding.md +++ b/docs/admin/networking/port-forwarding.md @@ -60,7 +60,7 @@ where each segment of hostnames must not exceed 63 characters. If your app name, agent name, workspace name and username exceed 63 characters in the hostname, port forwarding via the dashboard will not work. -### From an coder_app resource +### From a coder_app resource One way to port forward is to configure a `coder_app` resource in the workspace's template. This approach shows a visual application icon in the diff --git a/docs/admin/networking/wildcard-access-url.md b/docs/admin/networking/wildcard-access-url.md index 44afba2e5bb..fc9e9173319 100644 --- a/docs/admin/networking/wildcard-access-url.md +++ b/docs/admin/networking/wildcard-access-url.md @@ -56,6 +56,12 @@ Use a reverse proxy to handle TLS termination with automatic certificate managem - [Apache with Let's Encrypt](../../tutorials/reverse-proxy-apache.md) - [Caddy reverse proxy](../../tutorials/reverse-proxy-caddy.md) +If your reverse proxy rewrites the request `Host` and forwards the original +host in `X-Forwarded-Host`, configure +[`CODER_PROXY_TRUSTED_ORIGINS`](../../reference/cli/server.md#--proxy-trusted-origins) +to trust that proxy's address. Otherwise Coder will ignore `X-Forwarded-Host` +for subdomain app routing. + ### DNS Setup You'll need to configure DNS to point wildcard subdomains to your Coder server: diff --git a/docs/admin/setup/index.md b/docs/admin/setup/index.md index ea36467cfa1..df68b1efcbc 100644 --- a/docs/admin/setup/index.md +++ b/docs/admin/setup/index.md @@ -94,12 +94,14 @@ working directory prior to step 1. ```yaml coder: tls: - secretName: + secretNames: - coder-tls # Alternatively, if you use an Ingress controller to terminate TLS, # set the following values: ingress: + enable: true + tls: enable: true secretName: coder-tls wildcardSecretName: coder-tls diff --git a/docs/admin/templates/extending-templates/variables.md b/docs/admin/templates/extending-templates/variables.md index 3c1d02f0baf..7776b355a3c 100644 --- a/docs/admin/templates/extending-templates/variables.md +++ b/docs/admin/templates/extending-templates/variables.md @@ -51,7 +51,7 @@ and predictability. If you encounter a situation where you need to override template settings for variables, you can employ a straightforward solution: -1. Create a `terraform.tfvars` file in in the template directory: +1. Create a `terraform.tfvars` file in the template directory: ```tf coder_image = newimage:tag diff --git a/docs/admin/templates/open-in-coder.md b/docs/admin/templates/open-in-coder.md index a15838c7392..0365075af7b 100644 --- a/docs/admin/templates/open-in-coder.md +++ b/docs/admin/templates/open-in-coder.md @@ -115,6 +115,25 @@ specified in your template in the `disable_params` search params list [![Open in Coder](https://YOUR_ACCESS_URL/open-in-coder.svg)](https://YOUR_ACCESS_URL/templates/YOUR_TEMPLATE/workspace?disable_params=first_parameter,second_parameter) ``` +### Security: consent dialog for automatic creation + +When using `mode=auto` with prefilled `param.*` values, Coder displays a +security consent dialog before creating the workspace. This protects users +from malicious links that could provision workspaces with untrusted +configurations, such as dotfiles or startup scripts from unknown sources. + +The dialog shows: + +- A warning that a workspace is about to be created automatically from a link +- All prefilled `param.*` values from the URL +- **Confirm and Create** and **Cancel** buttons + +The workspace is only created if the user explicitly clicks **Confirm and +Create**. Clicking **Cancel** falls back to the standard creation form where +all parameters can be reviewed manually. + +![Consent dialog for automatic workspace creation](../../images/templates/auto-create-consent-dialog.png) + ### Example: Kubernetes For a full example of the Open in Coder flow in Kubernetes, check out diff --git a/docs/admin/users/idp-sync.md b/docs/admin/users/idp-sync.md index 3c7ec708be3..824d66635fd 100644 --- a/docs/admin/users/idp-sync.md +++ b/docs/admin/users/idp-sync.md @@ -241,7 +241,7 @@ role sync at the organization level. 1. Confirm you have the [Coder CLI](../../install/index.md) installed and are logged in with a user who is an Owner or has an Organization Admin role. -1. To fetch the current group sync settings for an organization, run the +1. To fetch the current role sync settings for an organization, run the following: ```sh @@ -388,7 +388,7 @@ settings, a user's memberships will update when they log out and log back in. "cbdcf774-4123-4118-8cd9-b3f502c84dfb" ], "sales": [ - "d79144d9-b30a-555a-9af8-7dac83b2q4ec", + "d79144d9-b30a-555a-9af8-7dac83b2q4ec" ] }, "organization_assign_default": true diff --git a/docs/admin/users/index.md b/docs/admin/users/index.md index 4f6f5049d34..536300c4f7e 100644 --- a/docs/admin/users/index.md +++ b/docs/admin/users/index.md @@ -215,10 +215,11 @@ You can use the Coder CLI or API to retrieve your list of users. ### CLI -Use `users list` to export the list of users to a CSV file: +Use `users list` with `jq` to export the list of users to a CSV file: ```shell -coder users list > users.csv +coder users list --output json | \ + jq -r '["username","email","created_at","status"], (.[] | [.username, .email, .created_at, .status]) | @csv' > users.csv ``` Visit the [users list](../../reference/cli/users_list.md) documentation for more options. diff --git a/docs/ai-coder/ai-bridge/setup.md b/docs/ai-coder/ai-bridge/setup.md index 3c0a4f7a839..b9ddbd56cdf 100644 --- a/docs/ai-coder/ai-bridge/setup.md +++ b/docs/ai-coder/ai-bridge/setup.md @@ -58,6 +58,34 @@ Set the following when routing [Amazon Bedrock](https://coder.com/docs/reference - `CODER_AIBRIDGE_BEDROCK_MODEL` or `--aibridge-bedrock-model` - `CODER_AIBRIDGE_BEDROCK_SMALL_FAST_MODEL` or `--aibridge-bedrock-small-fast-model` +#### Obtaining Bedrock credentials + +1. **Choose a region** where you want to use Bedrock. + +2. **Generate API keys** in the [AWS Bedrock console](https://us-east-1.console.aws.amazon.com/bedrock/home?region=us-east-1#/api-keys/long-term/create) (replace `us-east-1` in the URL with your chosen region): + - Choose an expiry period for the key. + - Click **Generate**. + - This creates an IAM user with strictly-scoped permissions for Bedrock access. + +3. **Create an access key** for the IAM user: + - After generating the API key, click **"You can directly modify permissions for the IAM user associated"**. + - In the IAM user page, navigate to the **Security credentials** tab. + - Under **Access keys**, click **Create access key**. + - Select **"Application running outside AWS"** as the use case. + - Click **Next**. + - Add a description like "Coder AI Bridge token". + - Click **Create access key**. + - Save both the access key ID and secret access key securely. + +4. **Configure your Coder deployment** with the credentials: + + ```sh + export CODER_AIBRIDGE_BEDROCK_REGION=us-east-1 + export CODER_AIBRIDGE_BEDROCK_ACCESS_KEY= + export CODER_AIBRIDGE_BEDROCK_ACCESS_KEY_SECRET= + coder server + ``` + ### Additional providers and Model Proxies AI Bridge can relay traffic to other OpenAI- or Anthropic-compatible services or model proxies like LiteLLM by pointing the base URL variables above at the provider you operate. Share feedback or follow along in the [`aibridge`](https://github.com/coder/aibridge) issue tracker as we expand support for additional providers. diff --git a/docs/ai-coder/tasks-migration.md b/docs/ai-coder/tasks-migration.md index 6cd02ba2e7b..e3c21409f4b 100644 --- a/docs/ai-coder/tasks-migration.md +++ b/docs/ai-coder/tasks-migration.md @@ -68,7 +68,7 @@ Below is a minimal illustrative example of a Coder Tasks template pre-2.28.0. terraform { required_providers { coder = { - source = "coder/coder + source = "coder/coder" } } } @@ -124,8 +124,8 @@ Example (**not** a full template): terraform { required_providers { coder = { - source = "coder/coder - version = ">= 2.13.0 + source = "coder/coder" + version = ">= 2.13.0" } } } diff --git a/docs/images/templates/auto-create-consent-dialog.png b/docs/images/templates/auto-create-consent-dialog.png new file mode 100644 index 00000000000..a7b4ac070d2 Binary files /dev/null and b/docs/images/templates/auto-create-consent-dialog.png differ diff --git a/docs/install/airgap.md b/docs/install/airgap.md index cb2f2340a63..c2d2b587f75 100644 --- a/docs/install/airgap.md +++ b/docs/install/airgap.md @@ -53,7 +53,7 @@ RUN mkdir -p /opt/terraform # The below step is optional if you wish to keep the existing version. # See https://github.com/coder/coder/blob/main/provisioner/terraform/install.go#L23-L24 # for supported Terraform versions. -ARG TERRAFORM_VERSION=1.11.0 +ARG TERRAFORM_VERSION=1.16.6 RUN apk update && \ curl -LOs https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip \ && unzip -o terraform_${TERRAFORM_VERSION}_linux_amd64.zip \ diff --git a/docs/manifest.json b/docs/manifest.json index 56b583eb284..f62e212f323 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1691,6 +1691,11 @@ "description": "Rotate database encryption keys.", "path": "reference/cli/server_dbcrypt_rotate.md" }, + { + "title": "server fix-oidc-links", + "description": "Reset OIDC linked IDs that do not match the expected issuer, allowing users to re-authenticate.", + "path": "reference/cli/server_fix-oidc-links.md" + }, { "title": "server postgres-builtin-serve", "description": "Run the built-in PostgreSQL deployment.", diff --git a/docs/reference/api/audit.md b/docs/reference/api/audit.md index c717a75d51e..9ec39fbf69f 100644 --- a/docs/reference/api/audit.md +++ b/docs/reference/api/audit.md @@ -88,7 +88,8 @@ curl -X GET http://coder-server:8080/api/v2/audit?limit=0 \ "user_agent": "string" } ], - "count": 0 + "count": 0, + "count_cap": 0 } ``` diff --git a/docs/reference/api/builds.md b/docs/reference/api/builds.md index dd7323886e1..e540012982c 100644 --- a/docs/reference/api/builds.md +++ b/docs/reference/api/builds.md @@ -1213,6 +1213,44 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Update workspace build state + +### Code samples + +```shell +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/state \ + -H 'Content-Type: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /workspacebuilds/{workspacebuild}/state` + +> Body parameter + +```json +{ + "state": [ + 0 + ] +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|------------------|------|--------------------------------------------------------------------------------------------------|----------|--------------------| +| `workspacebuild` | path | string(uuid) | true | Workspace build ID | +| `body` | body | [codersdk.UpdateWorkspaceBuildStateRequest](schemas.md#codersdkupdateworkspacebuildstaterequest) | true | Request body | + +### Responses + +| Status | Meaning | Description | Schema | +|--------|-----------------------------------------------------------------|-------------|--------| +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Get workspace build timings by ID ### Code samples diff --git a/docs/reference/api/enterprise.md b/docs/reference/api/enterprise.md index dfdeeb1756e..199c6f2e3aa 100644 --- a/docs/reference/api/enterprise.md +++ b/docs/reference/api/enterprise.md @@ -289,7 +289,8 @@ curl -X GET http://coder-server:8080/api/v2/connectionlog?limit=0 \ "workspace_owner_username": "string" } ], - "count": 0 + "count": 0, + "count_cap": 0 } ``` @@ -329,7 +330,6 @@ curl -X GET http://coder-server:8080/api/v2/entitlements \ "enabled": true, "entitlement": "entitled", "limit": 0, - "soft_limit": 0, "usage_period": { "end": "2019-08-24T14:15:22Z", "issued_at": "2019-08-24T14:15:22Z", @@ -341,7 +341,6 @@ curl -X GET http://coder-server:8080/api/v2/entitlements \ "enabled": true, "entitlement": "entitled", "limit": 0, - "soft_limit": 0, "usage_period": { "end": "2019-08-24T14:15:22Z", "issued_at": "2019-08-24T14:15:22Z", diff --git a/docs/reference/api/general.md b/docs/reference/api/general.md index 2f30b4c1d67..544d7e24edb 100644 --- a/docs/reference/api/general.md +++ b/docs/reference/api/general.md @@ -371,6 +371,7 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \ "oidc": { "allow_signups": true, "auth_url_params": {}, + "auto_repair_links": true, "client_cert_file": "string", "client_id": "string", "client_key_file": "string", diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index f3347ab3231..5f229de8209 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -1417,7 +1417,8 @@ "user_agent": "string" } ], - "count": 0 + "count": 0, + "count_cap": 0 } ``` @@ -1427,6 +1428,7 @@ |--------------|-------------------------------------------------|----------|--------------|-------------| | `audit_logs` | array of [codersdk.AuditLog](#codersdkauditlog) | false | | | | `count` | integer | false | | | +| `count_cap` | integer | false | | | ## codersdk.AuthMethod @@ -1845,7 +1847,8 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in "workspace_owner_username": "string" } ], - "count": 0 + "count": 0, + "count_cap": 0 } ``` @@ -1855,6 +1858,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in |-------------------|-----------------------------------------------------------|----------|--------------|-------------| | `connection_logs` | array of [codersdk.ConnectionLog](#codersdkconnectionlog) | false | | | | `count` | integer | false | | | +| `count_cap` | integer | false | | | ## codersdk.ConnectionLogSSHInfo @@ -3055,6 +3059,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "oidc": { "allow_signups": true, "auth_url_params": {}, + "auto_repair_links": true, "client_cert_file": "string", "client_id": "string", "client_key_file": "string", @@ -3571,6 +3576,7 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "oidc": { "allow_signups": true, "auth_url_params": {}, + "auto_repair_links": true, "client_cert_file": "string", "client_id": "string", "client_key_file": "string", @@ -4011,7 +4017,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "enabled": true, "entitlement": "entitled", "limit": 0, - "soft_limit": 0, "usage_period": { "end": "2019-08-24T14:15:22Z", "issued_at": "2019-08-24T14:15:22Z", @@ -4023,7 +4028,6 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o "enabled": true, "entitlement": "entitled", "limit": 0, - "soft_limit": 0, "usage_period": { "end": "2019-08-24T14:15:22Z", "issued_at": "2019-08-24T14:15:22Z", @@ -4309,7 +4313,6 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith "enabled": true, "entitlement": "entitled", "limit": 0, - "soft_limit": 0, "usage_period": { "end": "2019-08-24T14:15:22Z", "issued_at": "2019-08-24T14:15:22Z", @@ -4320,13 +4323,12 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith ### Properties -| Name | Type | Required | Restrictions | Description | -|---------------|----------------------------------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `actual` | integer | false | | | -| `enabled` | boolean | false | | | -| `entitlement` | [codersdk.Entitlement](#codersdkentitlement) | false | | | -| `limit` | integer | false | | | -| `soft_limit` | integer | false | | Soft limit is the soft limit of the feature, and is only used for showing included limits in the dashboard. No license validation or warnings are generated from this value. | +| Name | Type | Required | Restrictions | Description | +|---------------|----------------------------------------------|----------|--------------|-------------| +| `actual` | integer | false | | | +| `enabled` | boolean | false | | | +| `entitlement` | [codersdk.Entitlement](#codersdkentitlement) | false | | | +| `limit` | integer | false | | | |`usage_period`|[codersdk.UsagePeriod](#codersdkusageperiod)|false||Usage period denotes that the usage is a counter that accumulates over this period (and most likely resets with the issuance of the next license). These dates are determined from the license that this entitlement comes from, see enterprise/coderd/license/license.go. Only certain features set these fields: - FeatureManagedAgentLimit| @@ -5771,6 +5773,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit| { "allow_signups": true, "auth_url_params": {}, + "auto_repair_links": true, "client_cert_file": "string", "client_id": "string", "client_key_file": "string", @@ -5828,6 +5831,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit| |--------------------------------------|----------------------------------|----------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `allow_signups` | boolean | false | | | | `auth_url_params` | object | false | | | +| `auto_repair_links` | boolean | false | | | | `client_cert_file` | string | false | | | | `client_id` | string | false | | | | `client_key_file` | string | false | | Client key file & ClientCertFile are used in place of ClientSecret for PKI auth. | @@ -9358,6 +9362,20 @@ Restarts will only happen on weekdays in this list on weeks which line up with W | `old_password` | string | false | | | | `password` | string | true | | | +## codersdk.UpdateUserPreferenceSettingsRequest + +```json +{ + "task_notification_alert_dismissed": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|-------------------------------------|---------|----------|--------------|-------------| +| `task_notification_alert_dismissed` | boolean | false | | | + ## codersdk.UpdateUserProfileRequest ```json @@ -9442,6 +9460,22 @@ If the schedule is empty, the user will be updated to use the default schedule.| |------------|--------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `schedule` | string | false | | Schedule is expected to be of the form `CRON_TZ= * * ` Example: `CRON_TZ=US/Central 30 9 * * 1-5` represents 0930 in the timezone US/Central on weekdays (Mon-Fri). `CRON_TZ` defaults to UTC if not present. | +## codersdk.UpdateWorkspaceBuildStateRequest + +```json +{ + "state": [ + 0 + ] +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|---------|------------------|----------|--------------|-------------| +| `state` | array of integer | false | | | + ## codersdk.UpdateWorkspaceDormancy ```json @@ -9846,6 +9880,20 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `name` | string | false | | | | `value` | string | false | | | +## codersdk.UserPreferenceSettings + +```json +{ + "task_notification_alert_dismissed": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|-------------------------------------|---------|----------|--------------|-------------| +| `task_notification_alert_dismissed` | boolean | false | | | + ## codersdk.UserQuietHoursScheduleConfig ```json diff --git a/docs/reference/api/users.md b/docs/reference/api/users.md index 857d619398f..c69c57af859 100644 --- a/docs/reference/api/users.md +++ b/docs/reference/api/users.md @@ -1241,6 +1241,90 @@ curl -X PUT http://coder-server:8080/api/v2/users/{user}/password \ To perform this operation, you must be authenticated. [Learn more](authentication.md). +## Get user preference settings + +### Code samples + +```shell +# Example request using curl +curl -X GET http://coder-server:8080/api/v2/users/{user}/preferences \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`GET /users/{user}/preferences` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------|----------|----------------------| +| `user` | path | string | true | User ID, name, or me | + +### Example responses + +> 200 Response + +```json +{ + "task_notification_alert_dismissed": true +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserPreferenceSettings](schemas.md#codersdkuserpreferencesettings) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + +## Update user preference settings + +### Code samples + +```shell +# Example request using curl +curl -X PUT http://coder-server:8080/api/v2/users/{user}/preferences \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H 'Coder-Session-Token: API_KEY' +``` + +`PUT /users/{user}/preferences` + +> Body parameter + +```json +{ + "task_notification_alert_dismissed": true +} +``` + +### Parameters + +| Name | In | Type | Required | Description | +|--------|------|--------------------------------------------------------------------------------------------------------|----------|-------------------------| +| `user` | path | string | true | User ID, name, or me | +| `body` | body | [codersdk.UpdateUserPreferenceSettingsRequest](schemas.md#codersdkupdateuserpreferencesettingsrequest) | true | New preference settings | + +### Example responses + +> 200 Response + +```json +{ + "task_notification_alert_dismissed": true +} +``` + +### Responses + +| Status | Meaning | Description | Schema | +|--------|---------------------------------------------------------|-------------|------------------------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.UserPreferenceSettings](schemas.md#codersdkuserpreferencesettings) | + +To perform this operation, you must be authenticated. [Learn more](authentication.md). + ## Update user profile ### Code samples diff --git a/docs/reference/cli/index.md b/docs/reference/cli/index.md index e22e2deeeca..b26ec94a7f8 100644 --- a/docs/reference/cli/index.md +++ b/docs/reference/cli/index.md @@ -179,7 +179,7 @@ Disable network telemetry. Network telemetry is collected when connecting to wor | Environment | $CODER_USE_KEYRING | | Default | true | -Store and retrieve session tokens using the operating system keyring. Enabled by default. If the keyring is not supported on the current platform, file-based storage is used automatically. Set to false to force file-based storage. +Store and retrieve session tokens using the operating system keyring. This flag is ignored and file-based storage is used when --global-config is set or keyring usage is not supported on the current platform. Set to false to force file-based storage on supported platforms. ### --global-config diff --git a/docs/reference/cli/server.md b/docs/reference/cli/server.md index 951e140f3f0..548d754e803 100644 --- a/docs/reference/cli/server.md +++ b/docs/reference/cli/server.md @@ -16,6 +16,7 @@ coder server [flags] | [create-admin-user](./server_create-admin-user.md) | Create a new admin user with the given username, email and password and adds it to every organization. | | [postgres-builtin-url](./server_postgres-builtin-url.md) | Output the connection URL for the built-in PostgreSQL deployment. | | [postgres-builtin-serve](./server_postgres-builtin-serve.md) | Run the built-in PostgreSQL deployment. | +| [fix-oidc-links](./server_fix-oidc-links.md) | Reset OIDC linked IDs that do not match the expected issuer, allowing users to re-authenticate. | | [dbcrypt](./server_dbcrypt.md) | Manage database encryption. | ## Options @@ -971,7 +972,7 @@ Headers to trust for forwarding IP addresses. e.g. Cf-Connecting-Ip, True-Client | Environment | $CODER_PROXY_TRUSTED_ORIGINS | | YAML | networking.proxyTrustedOrigins | -Origin addresses to respect "proxy-trusted-headers". e.g. 192.168.1.0/24. +Origin addresses to respect "proxy-trusted-headers" and X-Forwarded-Host for subdomain app routing. e.g. 192.168.1.0/24. ### --cache-dir @@ -1175,7 +1176,7 @@ The SSH deployment prefix is used in the Host of the ssh config. | YAML | client.workspaceHostnameSuffix | | Default | coder | -Workspace hostnames use this suffix in SSH config and Coder Connect on Coder Desktop. By default it is coder, resulting in names like myworkspace.coder. +Workspace hostnames use this suffix in SSH config and Coder Connect on Coder Desktop. By default it is coder, resulting in names like myworkspace.coder. The suffix must not start with a dot, and must not contain spaces, newlines, or glob characters (* and ?). ### --ssh-config-options @@ -1185,7 +1186,7 @@ Workspace hostnames use this suffix in SSH config and Coder Connect on Coder Des | Environment | $CODER_SSH_CONFIG_OPTIONS | | YAML | client.sshConfigOptions | -These SSH config options will override the default SSH config options. Provide options in "key=value" or "key value" format separated by commas.Using this incorrectly can break SSH to your deployment, use cautiously. +These SSH config options will override the default SSH config options. Provide options in "key=value" or "key value" format separated by commas. Using this incorrectly can break SSH to your deployment, use cautiously. The following options are not allowed: Host, Match, Include, ProxyCommand, ProxyJump, LocalCommand, PermitLocalCommand, RemoteCommand, KnownHostsCommand, PKCS11Provider, SecurityKeyProvider, SmartcardDevice, XAuthLocation. Option values must not contain newline, carriage return, or NUL characters. ### --cli-upgrade-message diff --git a/docs/reference/cli/server_fix-oidc-links.md b/docs/reference/cli/server_fix-oidc-links.md new file mode 100644 index 00000000000..a5f7f23bd3c --- /dev/null +++ b/docs/reference/cli/server_fix-oidc-links.md @@ -0,0 +1,57 @@ + +# server fix-oidc-links + +Reset OIDC linked IDs that do not match the expected issuer, allowing users to re-authenticate. + +## Usage + +```console +coder server fix-oidc-links [flags] +``` + +## Options + +### -y, --yes + +| | | +|------|-------------------| +| Type | bool | + +Bypass prompts. + +### --postgres-url + +| | | +|-------------|---------------------------------------| +| Type | string | +| Environment | $CODER_PG_CONNECTION_URL | + +URL of a PostgreSQL database. If empty, the built-in PostgreSQL deployment will be used (Coder must not be already running in this case). + +### --postgres-connection-auth + +| | | +|-------------|----------------------------------------| +| Type | password\|awsiamrds | +| Environment | $CODER_PG_CONNECTION_AUTH | +| Default | password | + +Type of auth to use when connecting to postgres. + +### --issuer-url + +| | | +|-------------|-------------------------------------| +| Type | string | +| Environment | $CODER_OIDC_ISSUER_URL | + +The OIDC issuer URL. The canonical issuer is resolved via OIDC discovery. + +### -n, --dry-run + +| | | +|-------------|--------------------------------------------| +| Type | bool | +| Environment | $CODER_FIX_OIDC_LINKS_DRY_RUN | + +Print analysis only, do not modify the database. diff --git a/docs/reference/cli/state_push.md b/docs/reference/cli/state_push.md index 039b03fc01c..7796d0ba8d5 100644 --- a/docs/reference/cli/state_push.md +++ b/docs/reference/cli/state_push.md @@ -18,3 +18,11 @@ coder state push [flags] | Type | int | Specify a workspace build to target by name. Defaults to latest. + +### -n, --no-build + +| | | +|------|-------------------| +| Type | bool | + +Update the state without triggering a workspace build. Useful for state-only migrations. diff --git a/docs/tutorials/reverse-proxy-nginx.md b/docs/tutorials/reverse-proxy-nginx.md index afc48cd6ef7..18dfd8a7344 100644 --- a/docs/tutorials/reverse-proxy-nginx.md +++ b/docs/tutorials/reverse-proxy-nginx.md @@ -54,7 +54,7 @@ you're using `coder.example.com` as your subdomain. ## Install and configure LetsEncrypt Certbot 1. Install LetsEncrypt Certbot: Refer to the - [CertBot documentation](https://certbot.eff.org/instructions?ws=apache&os=ubuntufocal&tab=wildcard). + [CertBot documentation](https://certbot.eff.org/instructions?ws=nginx&os=ubuntufocal&tab=wildcard). Be sure to pick the wildcard tab and select your DNS provider for instructions to install the necessary DNS plugin. @@ -134,7 +134,7 @@ providers, refer to the proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; + proxy_set_header X-Forwarded-Proto $scheme; add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always; } } diff --git a/docs/tutorials/testing-templates.md b/docs/tutorials/testing-templates.md index 025c0d6ace2..ef77d73302b 100644 --- a/docs/tutorials/testing-templates.md +++ b/docs/tutorials/testing-templates.md @@ -111,5 +111,5 @@ jobs: - name: Promote template version if: success() run: | - coder template version promote --template=$TEMPLATE_NAME --template-version=${{ steps.name.outputs.version_name }} --yes + coder template version promote --template=$TEMPLATE_NAME --template-version=${{ steps.name.outputs.version_name }} ``` diff --git a/docs/user-guides/workspace-access/index.md b/docs/user-guides/workspace-access/index.md index 53b1583dac4..9975da1969e 100644 --- a/docs/user-guides/workspace-access/index.md +++ b/docs/user-guides/workspace-access/index.md @@ -26,7 +26,7 @@ customization options, keyboard shortcuts, and troubleshooting guides. ## SSH -### Through with the CLI +### Through the CLI Coder will use the optimal path for an SSH connection (determined by your deployment's [networking configuration](../../admin/infrastructure/index.md)) @@ -156,7 +156,7 @@ services or preview environments. You can also [share ports](./port-forwarding.md#sharing-ports) with other users, or [port-forward](./port-forwarding.md#the-coder-port-forward-command) through -the CLI with `coder port forward`. Read more in the +the CLI with `coder port-forward`. Read more in the [docs on workspace ports](./port-forwarding.md). ![Open Ports window](../../images/networking/listeningports.png) diff --git a/docs/user-guides/workspace-access/web-terminal.md b/docs/user-guides/workspace-access/web-terminal.md index 93c364c2894..6d40631635c 100644 --- a/docs/user-guides/workspace-access/web-terminal.md +++ b/docs/user-guides/workspace-access/web-terminal.md @@ -158,7 +158,15 @@ You can open a terminal with a specific command by adding a query parameter: https://coder.example.com/@user/workspace/terminal?command=htop ``` -This will execute `htop` immediately when the terminal opens. +When a `?command=` parameter is present, a confirmation dialog is shown before +the command executes. The user must click **Run command** to proceed or +**Cancel** to close the terminal window. This prevents external links from +silently executing arbitrary commands in a workspace. + +Template-configured apps that use the `command` attribute in +[`coder_app`](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/app) +are trusted and bypass the confirmation dialog. These apps use the `?app=` +parameter internally, which resolves the command from the agent's app list. ### Container Selection diff --git a/dogfood/coder/Dockerfile b/dogfood/coder/Dockerfile index 655e3b8084f..8f1f5a59e00 100644 --- a/dogfood/coder/Dockerfile +++ b/dogfood/coder/Dockerfile @@ -1,4 +1,4 @@ -# 1.86.0 +# rustc 1.91.1 FROM rust:slim@sha256:5218a2b4b4cb172f26503ac2b2de8e5ffd629ae1c0d885aff2cbe97fd4d1a409 AS rust-utils # Install rust helper programs ENV CARGO_INSTALL_ROOT=/tmp/ @@ -6,13 +6,13 @@ ENV CARGO_INSTALL_ROOT=/tmp/ RUN sed -i 's|http://deb.debian.org/debian|http://mirrors.edge.kernel.org/debian|g' /etc/apt/sources.list && \ apt-get update || true RUN apt-get update && apt-get install -y libssl-dev openssl pkg-config build-essential -RUN cargo install jj-cli typos-cli watchexec-cli +RUN cargo install --locked jj-cli@0.45.1 typos-cli@1.49.0 watchexec-cli@2.3.2 FROM ubuntu:jammy@sha256:104ae83764a5119017b8e8d6218fa0832b09df65aae7d5a6de29a85d813da2fb AS go # Install Go manually, so that we can control the version -ARG GO_VERSION=1.24.10 -ARG GO_CHECKSUM="dd52b974e3d9c5a7bbfb222c685806def6be5d6f7efd10f9caa9ca1fa2f47955" +ARG GO_VERSION=1.25.10 +ARG GO_CHECKSUM="42d4f7a32316aa66591eca7e89867256057a4264451aca10570a715b3637ba70" # Boring Go is needed to build FIPS-compliant binaries. RUN apt-get update && \ @@ -35,11 +35,11 @@ RUN apt-get update && \ # moq for Go tests. go install github.com/matryer/moq@v0.2.3 && \ # swag for Swagger doc generation - go install github.com/swaggo/swag/cmd/swag@v1.7.4 && \ + go install github.com/swaggo/swag/cmd/swag@v1.16.2 && \ # go-swagger tool to generate the go coder api client go install github.com/go-swagger/go-swagger/cmd/swagger@v0.28.0 && \ # goimports for updating imports - go install golang.org/x/tools/cmd/goimports@v0.31.0 && \ + go install golang.org/x/tools/cmd/goimports@v0.41.0 && \ # protoc-gen-go is needed to build sysbox from source go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.30.0 && \ # drpc support for v2 @@ -49,8 +49,8 @@ RUN apt-get update && \ # goreleaser for compiling v2 binaries go install github.com/goreleaser/goreleaser@v1.6.1 && \ # Install the latest version of gopls for editors that support - # the language server protocol - go install golang.org/x/tools/gopls@v0.18.1 && \ + # the language server protocol (v0.21.0+ required for Go 1.25) + go install golang.org/x/tools/gopls@v0.21.0 && \ # gotestsum makes test output more readable go install gotest.tools/gotestsum@v1.9.0 && \ # goveralls collects code coverage metrics from tests @@ -62,8 +62,6 @@ RUN apt-get update && \ # charts and values files go install github.com/norwoodj/helm-docs/cmd/helm-docs@v1.5.0 && \ # sqlc for Go code generation - # (CGO_ENABLED=1 go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.27.0) && \ - # # Switched to coder/sqlc fork to fix ambiguous column bug, see: # - https://github.com/coder/sqlc/pull/1 # - https://github.com/sqlc-dev/sqlc/pull/4159 @@ -77,14 +75,16 @@ RUN apt-get update && \ go install github.com/quasilyte/go-ruleguard/cmd/ruleguard@v0.3.13 && \ # go-releaser for building 'fat binaries' that work cross-platform go install github.com/goreleaser/goreleaser@v1.6.1 && \ - go install mvdan.cc/sh/v3/cmd/shfmt@v3.7.0 && \ + # shfmt for shell script formatting + go install mvdan.cc/sh/v3/cmd/shfmt@v3.12.0 && \ # nfpm is used with `make build` to make release packages go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.35.1 && \ # yq v4 is used to process yaml files in coder v2. Conflicts with # yq v3 used in v1. go install github.com/mikefarah/yq/v4@v4.44.3 && \ mv /tmp/bin/yq /tmp/bin/yq4 && \ - go install go.uber.org/mock/mockgen@v0.5.0 && \ + # mockgen for generating mocks (v0.6.0+ required for Go 1.25) + go install go.uber.org/mock/mockgen@v0.6.0 && \ # Reduce image size. apt-get remove --yes gcc && \ apt-get autoremove --yes && \ @@ -212,9 +212,9 @@ RUN sed -i 's|http://archive.ubuntu.com/ubuntu/|http://mirrors.edge.kernel.org/u # Configure FIPS-compliant policies update-crypto-policies --set FIPS -# NOTE: In scripts/Dockerfile.base we specifically install Terraform version 1.12.2. +# NOTE: In scripts/Dockerfile.base we specifically install Terraform version 1.16.6. # Installing the same version here to match. -RUN wget -O /tmp/terraform.zip "https://releases.hashicorp.com/terraform/1.13.4/terraform_1.13.4_linux_amd64.zip" && \ +RUN wget -O /tmp/terraform.zip "https://releases.hashicorp.com/terraform/1.16.6/terraform_1.16.6_linux_amd64.zip" && \ unzip /tmp/terraform.zip -d /usr/local/bin && \ rm -f /tmp/terraform.zip && \ chmod +x /usr/local/bin/terraform && \ @@ -298,7 +298,6 @@ ARG CLOUD_SQL_PROXY_VERSION=2.2.0 \ KUBECTX_VERSION=0.9.4 \ STRIPE_VERSION=1.14.5 \ TERRAGRUNT_VERSION=0.45.11 \ - TRIVY_VERSION=0.41.0 \ SYFT_VERSION=1.20.0 \ COSIGN_VERSION=2.4.3 \ BUN_VERSION=1.2.15 @@ -337,9 +336,6 @@ RUN curl --silent --show-error --location --output /usr/local/bin/cloud_sql_prox # terragrunt for running Terraform and Terragrunt files curl --silent --show-error --location --output /usr/local/bin/terragrunt "https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/terragrunt_linux_amd64" && \ chmod a=rx /usr/local/bin/terragrunt && \ - # AquaSec Trivy for scanning container images for security issues - curl --silent --show-error --location "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" | \ - tar --extract --gzip --directory=/usr/local/bin --file=- trivy && \ # Anchore Syft for SBOM generation curl --silent --show-error --location "https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}/syft_${SYFT_VERSION}_linux_amd64.tar.gz" | \ tar --extract --gzip --directory=/usr/local/bin --file=- syft && \ diff --git a/dogfood/coder/main.tf b/dogfood/coder/main.tf index 44cfe1654d3..bdd17f564ad 100644 --- a/dogfood/coder/main.tf +++ b/dogfood/coder/main.tf @@ -890,7 +890,6 @@ resource "coder_app" "develop_sh" { icon = "${data.coder_workspace.me.access_url}/emojis/1f4bb.png" // 💻 command = "screen -x develop_sh" share = "authenticated" - subdomain = true open_in = "tab" order = 0 } diff --git a/enterprise/aibridged/aibridged_test.go b/enterprise/aibridged/aibridged_test.go index a66f3157a0d..cd48cca2f39 100644 --- a/enterprise/aibridged/aibridged_test.go +++ b/enterprise/aibridged/aibridged_test.go @@ -268,6 +268,25 @@ func TestRouting(t *testing.T) { expectedStatus: http.StatusTeapot, // Nonsense status to indicate server was hit. expectedHits: 1, }, + // Encoded dot segments are rejected before reaching the upstream. + { + name: "openai encoded traversal", + path: "/openai/v1/models/%2e%2e/chat/completions", + expectedStatus: http.StatusBadRequest, + expectedHits: 0, + }, + { + name: "openai escaped separator traversal", + path: "/openai/v1/models/..%2F..%2Fchat/completions", + expectedStatus: http.StatusBadRequest, + expectedHits: 0, + }, + { + name: "anthropic double encoded traversal", + path: "/anthropic/v1/models/%252e%252e/messages", + expectedStatus: http.StatusBadRequest, + expectedHits: 0, + }, } for _, tc := range cases { diff --git a/enterprise/aibridged/http.go b/enterprise/aibridged/http.go index 7e41f0c0073..f56c790afec 100644 --- a/enterprise/aibridged/http.go +++ b/enterprise/aibridged/http.go @@ -9,6 +9,7 @@ import ( "cdr.dev/slog" "github.com/coder/aibridge" + "github.com/coder/coder/v2/coderd/util/xurl" "github.com/coder/coder/v2/enterprise/aibridged/proto" ) @@ -19,6 +20,7 @@ var ( ErrConnect = xerrors.New("could not connect to coderd") ErrUnauthorized = xerrors.New("unauthorized") ErrAcquireRequestHandler = xerrors.New("failed to acquire request handler") + ErrInvalidRequestPath = xerrors.New("invalid request path") ) // ServeHTTP is the entrypoint for requests which will be intercepted by AI Bridge. @@ -35,6 +37,14 @@ func (s *Server) ServeHTTP(rw http.ResponseWriter, r *http.Request) { logger := s.logger.With(slog.F("path", r.URL.Path)) + // Reject paths that an upstream could resolve outside the route they are + // matched on here, before any authorization or upstream request. + if xurl.ContainsEncodedPath(r.URL) { + logger.Warn(ctx, "invalid request path") + http.Error(rw, ErrInvalidRequestPath.Error(), http.StatusBadRequest) + return + } + key := strings.TrimSpace(ExtractAuthToken(r.Header)) if key == "" { logger.Warn(ctx, "no auth key provided") diff --git a/enterprise/aibridgedserver/aibridgedserver.go b/enterprise/aibridgedserver/aibridgedserver.go index 156f3aa9d05..cc096cf3446 100644 --- a/enterprise/aibridgedserver/aibridgedserver.go +++ b/enterprise/aibridgedserver/aibridgedserver.go @@ -37,12 +37,13 @@ var ( // matching. // TODO: return these errors to the client in a more structured/comparable // way. - ErrInvalidKey = xerrors.New("invalid key") - ErrUnknownKey = xerrors.New("unknown key") - ErrExpired = xerrors.New("expired") - ErrUnknownUser = xerrors.New("unknown user") - ErrDeletedUser = xerrors.New("deleted user") - ErrSystemUser = xerrors.New("system user") + ErrInvalidKey = xerrors.New("invalid key") + ErrUnknownKey = xerrors.New("unknown key") + ErrExpired = xerrors.New("expired") + ErrUnknownUser = xerrors.New("unknown user") + ErrDeletedUser = xerrors.New("deleted user") + ErrInactiveUser = xerrors.New("inactive user") + ErrSystemUser = xerrors.New("system user") ErrNoExternalAuthLinkFound = xerrors.New("no external auth link found") ) @@ -399,10 +400,13 @@ func (s *Server) IsAuthorized(ctx context.Context, in *proto.IsAuthorizedRequest return nil, ErrUnknownUser } - // User is not deleted or a system user. + // User is active, not deleted, and not a system user. if user.Deleted { return nil, ErrDeletedUser } + if user.Status != database.UserStatusActive { + return nil, ErrInactiveUser + } if user.IsSystem { return nil, ErrSystemUser } diff --git a/enterprise/aibridgedserver/aibridgedserver_test.go b/enterprise/aibridgedserver/aibridgedserver_test.go index b871bfb3f8e..f14a4995198 100644 --- a/enterprise/aibridgedserver/aibridgedserver_test.go +++ b/enterprise/aibridgedserver/aibridgedserver_test.go @@ -94,16 +94,36 @@ func TestAuthorization(t *testing.T) { name: "deleted user", expectedErr: aibridgedserver.ErrDeletedUser, mocksFn: func(db *dbmock.MockStore, apiKey database.APIKey, user database.User) { + user.Deleted = true db.EXPECT().GetAPIKeyByID(gomock.Any(), apiKey.ID).Times(1).Return(apiKey, nil) - db.EXPECT().GetUserByID(gomock.Any(), user.ID).Times(1).Return(database.User{ID: user.ID, Deleted: true}, nil) + db.EXPECT().GetUserByID(gomock.Any(), user.ID).Times(1).Return(user, nil) + }, + }, + { + name: "suspended user", + expectedErr: aibridgedserver.ErrInactiveUser, + mocksFn: func(db *dbmock.MockStore, apiKey database.APIKey, user database.User) { + user.Status = database.UserStatusSuspended + db.EXPECT().GetAPIKeyByID(gomock.Any(), apiKey.ID).Times(1).Return(apiKey, nil) + db.EXPECT().GetUserByID(gomock.Any(), user.ID).Times(1).Return(user, nil) + }, + }, + { + name: "dormant user", + expectedErr: aibridgedserver.ErrInactiveUser, + mocksFn: func(db *dbmock.MockStore, apiKey database.APIKey, user database.User) { + user.Status = database.UserStatusDormant + db.EXPECT().GetAPIKeyByID(gomock.Any(), apiKey.ID).Times(1).Return(apiKey, nil) + db.EXPECT().GetUserByID(gomock.Any(), user.ID).Times(1).Return(user, nil) }, }, { name: "system user", expectedErr: aibridgedserver.ErrSystemUser, mocksFn: func(db *dbmock.MockStore, apiKey database.APIKey, user database.User) { + user.IsSystem = true db.EXPECT().GetAPIKeyByID(gomock.Any(), apiKey.ID).Times(1).Return(apiKey, nil) - db.EXPECT().GetUserByID(gomock.Any(), user.ID).Times(1).Return(database.User{ID: user.ID, IsSystem: true}, nil) + db.EXPECT().GetUserByID(gomock.Any(), user.ID).Times(1).Return(user, nil) }, }, { diff --git a/enterprise/cli/create_test.go b/enterprise/cli/create_test.go index 44218abb5a5..941908d17be 100644 --- a/enterprise/cli/create_test.go +++ b/enterprise/cli/create_test.go @@ -371,7 +371,7 @@ func TestEnterpriseCreateWithPreset(t *testing.T) { notifications.NewNoopEnqueuer(), newNoopUsageCheckerPtr(), ) - var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer(db) + var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer() api.AGPL.PrebuildsClaimer.Store(&claimer) // Given: a template and a template version where the preset defines values for all required parameters, @@ -482,7 +482,7 @@ func TestEnterpriseCreateWithPreset(t *testing.T) { notifications.NewNoopEnqueuer(), newNoopUsageCheckerPtr(), ) - var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer(db) + var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer() api.AGPL.PrebuildsClaimer.Store(&claimer) // Given: a template and a template version where the preset defines values for all required parameters, diff --git a/enterprise/cli/server.go b/enterprise/cli/server.go index bc77bc54ba5..a1aa001f26b 100644 --- a/enterprise/cli/server.go +++ b/enterprise/cli/server.go @@ -40,40 +40,44 @@ func (r *RootCmd) Server(_ func()) *serpent.Command { } } - if options.DeploymentValues.DERP.Server.Enable { - options.DERPServer = derp.NewServer(key.NewNode(), tailnet.Logger(options.Logger.Named("derp"))) - var meshKey string - err := options.Database.InTx(func(tx database.Store) error { - // This will block until the lock is acquired, and will be - // automatically released when the transaction ends. - err := tx.AcquireLock(ctx, database.LockIDEnterpriseDeploymentSetup) - if err != nil { - return xerrors.Errorf("acquire lock: %w", err) - } + // Always generate a mesh key, even if the built-in DERP server is + // disabled. This mesh key is still used by workspace proxies running + // HA. + var meshKey string + err := options.Database.InTx(func(tx database.Store) error { + // This will block until the lock is acquired, and will be + // automatically released when the transaction ends. + err := tx.AcquireLock(ctx, database.LockIDEnterpriseDeploymentSetup) + if err != nil { + return xerrors.Errorf("acquire lock: %w", err) + } - meshKey, err = tx.GetDERPMeshKey(ctx) - if err == nil { - return nil - } - if !errors.Is(err, sql.ErrNoRows) { - return xerrors.Errorf("get DERP mesh key: %w", err) - } - meshKey, err = cryptorand.String(32) - if err != nil { - return xerrors.Errorf("generate DERP mesh key: %w", err) - } - err = tx.InsertDERPMeshKey(ctx, meshKey) - if err != nil { - return xerrors.Errorf("insert DERP mesh key: %w", err) - } + meshKey, err = tx.GetDERPMeshKey(ctx) + if err == nil { return nil - }, nil) + } + if !errors.Is(err, sql.ErrNoRows) { + return xerrors.Errorf("get DERP mesh key: %w", err) + } + meshKey, err = cryptorand.String(32) if err != nil { - return nil, nil, err + return xerrors.Errorf("generate DERP mesh key: %w", err) } - if meshKey == "" { - return nil, nil, xerrors.New("mesh key is empty") + err = tx.InsertDERPMeshKey(ctx, meshKey) + if err != nil { + return xerrors.Errorf("insert DERP mesh key: %w", err) } + return nil + }, nil) + if err != nil { + return nil, nil, err + } + if meshKey == "" { + return nil, nil, xerrors.New("mesh key is empty") + } + + if options.DeploymentValues.DERP.Server.Enable { + options.DERPServer = derp.NewServer(key.NewNode(), tailnet.Logger(options.Logger.Named("derp"))) options.DERPServer.SetMeshKey(meshKey) } diff --git a/enterprise/cli/testdata/coder_--help.golden b/enterprise/cli/testdata/coder_--help.golden index 41ed3209687..e199e8cc27d 100644 --- a/enterprise/cli/testdata/coder_--help.golden +++ b/enterprise/cli/testdata/coder_--help.golden @@ -70,9 +70,10 @@ variables or flags. --use-keyring bool, $CODER_USE_KEYRING (default: true) Store and retrieve session tokens using the operating system keyring. - Enabled by default. If the keyring is not supported on the current - platform, file-based storage is used automatically. Set to false to - force file-based storage. + This flag is ignored and file-based storage is used when + --global-config is set or keyring usage is not supported on the + current platform. Set to false to force file-based storage on + supported platforms. -v, --verbose bool, $CODER_VERBOSE Enable verbose output. diff --git a/enterprise/cli/testdata/coder_server_--help.golden b/enterprise/cli/testdata/coder_server_--help.golden index 5ed217752b2..141062f9eb5 100644 --- a/enterprise/cli/testdata/coder_server_--help.golden +++ b/enterprise/cli/testdata/coder_server_--help.golden @@ -10,6 +10,9 @@ SUBCOMMANDS: email and password and adds it to every organization. dbcrypt Manage database encryption. + fix-oidc-links Reset OIDC linked IDs that do not match the + expected issuer, allowing users to + re-authenticate. postgres-builtin-serve Run the built-in PostgreSQL deployment. postgres-builtin-url Output the connection URL for the built-in PostgreSQL deployment. @@ -140,8 +143,12 @@ Clients include the Coder CLI, Coder Desktop, IDE extensions, and the web UI. --ssh-config-options string-array, $CODER_SSH_CONFIG_OPTIONS These SSH config options will override the default SSH config options. Provide options in "key=value" or "key value" format separated by - commas.Using this incorrectly can break SSH to your deployment, use - cautiously. + commas. Using this incorrectly can break SSH to your deployment, use + cautiously. The following options are not allowed: Host, Match, + Include, ProxyCommand, ProxyJump, LocalCommand, PermitLocalCommand, + RemoteCommand, KnownHostsCommand, PKCS11Provider, SecurityKeyProvider, + SmartcardDevice, XAuthLocation. Option values must not contain + newline, carriage return, or NUL characters. --ssh-hostname-prefix string, $CODER_SSH_HOSTNAME_PREFIX (default: coder.) The SSH deployment prefix is used in the Host of the ssh config. @@ -153,7 +160,8 @@ Clients include the Coder CLI, Coder Desktop, IDE extensions, and the web UI. --workspace-hostname-suffix string, $CODER_WORKSPACE_HOSTNAME_SUFFIX (default: coder) Workspace hostnames use this suffix in SSH config and Coder Connect on Coder Desktop. By default it is coder, resulting in names like - myworkspace.coder. + myworkspace.coder. The suffix must not start with a dot, and must not + contain spaces, newlines, or glob characters (* and ?). CONFIG OPTIONS: Use a YAML configuration file when your server launch become unwieldy. @@ -296,8 +304,8 @@ NETWORKING OPTIONS: True-Client-Ip, X-Forwarded-For. --proxy-trusted-origins string-array, $CODER_PROXY_TRUSTED_ORIGINS - Origin addresses to respect "proxy-trusted-headers". e.g. - 192.168.1.0/24. + Origin addresses to respect "proxy-trusted-headers" and + X-Forwarded-Host for subdomain app routing. e.g. 192.168.1.0/24. --redirect-to-access-url bool, $CODER_REDIRECT_TO_ACCESS_URL Specifies whether to redirect requests that do not match the access diff --git a/enterprise/cli/testdata/coder_server_fix-oidc-links_--help.golden b/enterprise/cli/testdata/coder_server_fix-oidc-links_--help.golden new file mode 100644 index 00000000000..a515e404fb7 --- /dev/null +++ b/enterprise/cli/testdata/coder_server_fix-oidc-links_--help.golden @@ -0,0 +1,29 @@ +coder v0.0.0-devel + +USAGE: + coder server fix-oidc-links [flags] + + Reset OIDC linked IDs that do not match the expected issuer, allowing users to + re-authenticate. + +OPTIONS: + --postgres-connection-auth password|awsiamrds, $CODER_PG_CONNECTION_AUTH (default: password) + Type of auth to use when connecting to postgres. + + -n, --dry-run bool, $CODER_FIX_OIDC_LINKS_DRY_RUN + Print analysis only, do not modify the database. + + --issuer-url string, $CODER_OIDC_ISSUER_URL + The OIDC issuer URL. The canonical issuer is resolved via OIDC + discovery. + + --postgres-url string, $CODER_PG_CONNECTION_URL + URL of a PostgreSQL database. If empty, the built-in PostgreSQL + deployment will be used (Coder must not be already running in this + case). + + -y, --yes bool + Bypass prompts. + +——— +Run `coder --help` for a list of global options. diff --git a/enterprise/coderd/aibridged.go b/enterprise/coderd/aibridged.go index 2ff2de902bc..229e4ee3fda 100644 --- a/enterprise/coderd/aibridged.go +++ b/enterprise/coderd/aibridged.go @@ -65,7 +65,7 @@ func (api *API) CreateInMemoryAIBridgeServer(dialCtx context.Context) (client ai if err != nil { return nil, xerrors.Errorf("register key validator service: %w", err) } - server := drpcserver.NewWithOptions(&tracing.DRPCHandler{Handler: mux}, + server := drpcsdk.NewServer(api.Logger, &tracing.DRPCHandler{Handler: mux}, drpcserver.Options{ Manager: drpcsdk.DefaultDRPCOptions(nil), Log: func(err error) { diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index 9a7b1f318f7..747279ef13f 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -971,7 +971,13 @@ func (api *API) updateEntitlements(ctx context.Context) error { var _ wsbuilder.UsageChecker = &API{} -func (api *API) CheckBuildUsage(ctx context.Context, store database.Store, templateVersion *database.TemplateVersion) (wsbuilder.UsageCheckResponse, error) { +func (api *API) CheckBuildUsage( + _ context.Context, + _ database.Store, + templateVersion *database.TemplateVersion, + task *database.Task, + transition database.WorkspaceTransition, +) (wsbuilder.UsageCheckResponse, error) { // If the template version has an external agent, we need to check that the // license is entitled to this feature. if templateVersion.HasExternalAgent.Valid && templateVersion.HasExternalAgent.Bool { @@ -984,48 +990,26 @@ func (api *API) CheckBuildUsage(ctx context.Context, store database.Store, templ } } - // If the template version doesn't have an AI task, we don't need to check - // usage. - if !templateVersion.HasAITask.Valid || !templateVersion.HasAITask.Bool { - return wsbuilder.UsageCheckResponse{ - Permitted: true, - }, nil + // Verify managed agent entitlement for AI task builds. + // The count/limit check is intentionally omitted — breaching the + // limit is advisory only and surfaced as a warning via entitlements. + if transition != database.WorkspaceTransitionStart || task == nil { + return wsbuilder.UsageCheckResponse{Permitted: true}, nil } - // When unlicensed, we need to check that we haven't breached the managed agent - // limit. - // Unlicensed deployments are allowed to use unlimited managed agents. - if api.Entitlements.HasLicense() { - managedAgentLimit, ok := api.Entitlements.Feature(codersdk.FeatureManagedAgentLimit) - if !ok || !managedAgentLimit.Enabled || managedAgentLimit.Limit == nil || managedAgentLimit.UsagePeriod == nil { - return wsbuilder.UsageCheckResponse{ - Permitted: false, - Message: "Your license is not entitled to managed agents. Please contact sales to continue using managed agents.", - }, nil - } - - // This check is intentionally not committed to the database. It's fine if - // it's not 100% accurate or allows for minor breaches due to build races. - // nolint:gocritic // Requires permission to read all usage events. - managedAgentCount, err := store.GetTotalUsageDCManagedAgentsV1(agpldbauthz.AsSystemRestricted(ctx), database.GetTotalUsageDCManagedAgentsV1Params{ - StartDate: managedAgentLimit.UsagePeriod.Start, - EndDate: managedAgentLimit.UsagePeriod.End, - }) - if err != nil { - return wsbuilder.UsageCheckResponse{}, xerrors.Errorf("get managed agent count: %w", err) - } + if !api.Entitlements.HasLicense() { + return wsbuilder.UsageCheckResponse{Permitted: true}, nil + } - if managedAgentCount >= *managedAgentLimit.Limit { - return wsbuilder.UsageCheckResponse{ - Permitted: false, - Message: "You have breached the managed agent limit in your license. Please contact sales to continue using managed agents.", - }, nil - } + managedAgentLimit, ok := api.Entitlements.Feature(codersdk.FeatureManagedAgentLimit) + if !ok || !managedAgentLimit.Enabled { + return wsbuilder.UsageCheckResponse{ + Permitted: false, + Message: "Your license is not entitled to managed agents. Please contact sales to continue using managed agents.", + }, nil } - return wsbuilder.UsageCheckResponse{ - Permitted: true, - }, nil + return wsbuilder.UsageCheckResponse{Permitted: true}, nil } // getProxyDERPStartingRegionID returns the starting region ID that should be @@ -1293,7 +1277,16 @@ func (api *API) setupPrebuilds(featureEnabled bool) (agplprebuilds.Reconciliatio return agplprebuilds.DefaultReconciler, agplprebuilds.DefaultClaimer } - reconciler := prebuilds.NewStoreReconciler(api.Database, api.Pubsub, api.AGPL.FileCache, api.DeploymentValues.Prebuilds, - api.Logger.Named("prebuilds"), quartz.NewReal(), api.PrometheusRegistry, api.NotificationsEnqueuer, api.AGPL.BuildUsageChecker) - return reconciler, prebuilds.NewEnterpriseClaimer(api.Database) + reconciler := prebuilds.NewStoreReconciler( + api.Database, + api.Pubsub, + api.AGPL.FileCache, + api.DeploymentValues.Prebuilds, + api.Logger.Named("prebuilds"), + quartz.NewReal(), + api.PrometheusRegistry, + api.NotificationsEnqueuer, + api.AGPL.BuildUsageChecker, + ) + return reconciler, prebuilds.NewEnterpriseClaimer() } diff --git a/enterprise/coderd/coderd_test.go b/enterprise/coderd/coderd_test.go index c3e6e1579fe..63acf2d4802 100644 --- a/enterprise/coderd/coderd_test.go +++ b/enterprise/coderd/coderd_test.go @@ -3,6 +3,7 @@ package coderd_test import ( "bytes" "context" + "database/sql" "encoding/json" "fmt" "io" @@ -17,10 +18,10 @@ import ( "time" "github.com/google/uuid" - "github.com/moby/moby/pkg/namesgenerator" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" + "go.uber.org/mock/gomock" "cdr.dev/slog" "cdr.dev/slog/sloggers/slogtest" @@ -29,8 +30,6 @@ import ( "github.com/coder/coder/v2/agent/agenttest" "github.com/coder/coder/v2/coderd/httpapi" agplprebuilds "github.com/coder/coder/v2/coderd/prebuilds" - "github.com/coder/coder/v2/coderd/rbac/policy" - "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/enterprise/coderd/prebuilds" "github.com/coder/coder/v2/provisioner/echo" "github.com/coder/coder/v2/provisionersdk/proto" @@ -39,14 +38,20 @@ import ( "github.com/coder/retry" "github.com/coder/serpent" + agplcoderd "github.com/coder/coder/v2/coderd" agplaudit "github.com/coder/coder/v2/coderd/audit" "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbauthz" "github.com/coder/coder/v2/coderd/database/dbfake" + "github.com/coder/coder/v2/coderd/database/dbmock" "github.com/coder/coder/v2/coderd/database/dbtestutil" "github.com/coder/coder/v2/coderd/database/dbtime" + "github.com/coder/coder/v2/coderd/entitlements" "github.com/coder/coder/v2/coderd/rbac" + "github.com/coder/coder/v2/coderd/rbac/policy" + "github.com/coder/coder/v2/coderd/util/namesgenerator" + "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk/workspacesdk" "github.com/coder/coder/v2/enterprise/audit" @@ -621,7 +626,7 @@ func TestManagedAgentLimit(t *testing.T) { ctx := testutil.Context(t, testutil.WaitLong) - cli, _ := coderdenttest.New(t, &coderdenttest.Options{ + cli, owner := coderdenttest.New(t, &coderdenttest.Options{ Options: &coderdtest.Options{ IncludeProvisionerDaemon: true, }, @@ -631,22 +636,18 @@ func TestManagedAgentLimit(t *testing.T) { // expiry warnings. GraceAt: time.Now().Add(time.Hour * 24 * 60), ExpiresAt: time.Now().Add(time.Hour * 24 * 90), - }).ManagedAgentLimit(1, 1), + }).ManagedAgentLimit(1), }) // Get entitlements to check that the license is a-ok. - entitlements, err := cli.Entitlements(ctx) //nolint:gocritic // we're not testing authz on the entitlements endpoint, so using owner is fine + sdkEntitlements, err := cli.Entitlements(ctx) //nolint:gocritic // we're not testing authz on the entitlements endpoint, so using owner is fine require.NoError(t, err) - require.True(t, entitlements.HasLicense) - agentLimit := entitlements.Features[codersdk.FeatureManagedAgentLimit] + require.True(t, sdkEntitlements.HasLicense) + agentLimit := sdkEntitlements.Features[codersdk.FeatureManagedAgentLimit] require.True(t, agentLimit.Enabled) require.NotNil(t, agentLimit.Limit) require.EqualValues(t, 1, *agentLimit.Limit) - require.NotNil(t, agentLimit.SoftLimit) - require.EqualValues(t, 1, *agentLimit.SoftLimit) - require.Empty(t, entitlements.Errors) - // There should be a warning since we're really close to our agent limit. - require.Equal(t, entitlements.Warnings[0], "You are approaching the managed agent limit in your license. Please refer to the Deployment Licenses page for more information.") + require.Empty(t, sdkEntitlements.Errors) // Create a fake provision response that claims there are agents in the // template and every built workspace. @@ -706,23 +707,184 @@ func TestManagedAgentLimit(t *testing.T) { noAiTemplate := coderdtest.CreateTemplate(t, cli, uuid.Nil, noAiVersion.ID) // Create one AI workspace, which should succeed. - workspace := coderdtest.CreateWorkspace(t, cli, aiTemplate.ID) + task, err := cli.CreateTask(ctx, owner.UserID.String(), codersdk.CreateTaskRequest{ + Name: "workspace-1", + TemplateVersionID: aiTemplate.ActiveVersionID, + TemplateVersionPresetID: uuid.Nil, + Input: "hi", + DisplayName: "cool task 1", + }) + require.NoError(t, err, "creating task for AI workspace must succeed") + workspace, err := cli.Workspace(ctx, task.WorkspaceID.UUID) + require.NoError(t, err, "fetching AI workspace must succeed") coderdtest.AwaitWorkspaceBuildJobCompleted(t, cli, workspace.LatestBuild.ID) - // Create a second AI workspace, which should fail. This needs to be done - // manually because coderdtest.CreateWorkspace expects it to succeed. - _, err = cli.CreateUserWorkspace(ctx, codersdk.Me, codersdk.CreateWorkspaceRequest{ //nolint:gocritic // owners must still be subject to the limit - TemplateID: aiTemplate.ID, - Name: coderdtest.RandomUsername(t), - AutomaticUpdates: codersdk.AutomaticUpdatesNever, + // Create a second AI task, which should succeed even though the limit is + // breached. Managed agent limits are advisory only and should never block + // workspace creation. + task2, err := cli.CreateTask(ctx, owner.UserID.String(), codersdk.CreateTaskRequest{ + Name: "workspace-2", + TemplateVersionID: aiTemplate.ActiveVersionID, + TemplateVersionPresetID: uuid.Nil, + Input: "hi", + DisplayName: "bad task 2", }) - require.ErrorContains(t, err, "You have breached the managed agent limit in your license") + require.NoError(t, err, "creating task beyond managed agent limit must succeed") + workspace2, err := cli.Workspace(ctx, task2.WorkspaceID.UUID) + require.NoError(t, err, "fetching AI workspace must succeed") + coderdtest.AwaitWorkspaceBuildJobCompleted(t, cli, workspace2.LatestBuild.ID) // Create a third non-AI workspace, which should succeed. workspace = coderdtest.CreateWorkspace(t, cli, noAiTemplate.ID) coderdtest.AwaitWorkspaceBuildJobCompleted(t, cli, workspace.LatestBuild.ID) } +func TestCheckBuildUsage_NeverBlocksOnManagedAgentLimit(t *testing.T) { + t.Parallel() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // Prepare entitlements with a managed agent limit. + entSet := entitlements.New() + entSet.Modify(func(e *codersdk.Entitlements) { + e.HasLicense = true + limit := int64(1) + issuedAt := time.Now().Add(-2 * time.Hour) + start := time.Now().Add(-time.Hour) + end := time.Now().Add(time.Hour) + e.Features[codersdk.FeatureManagedAgentLimit] = codersdk.Feature{ + Enabled: true, + Limit: &limit, + UsagePeriod: &codersdk.UsagePeriod{IssuedAt: issuedAt, Start: start, End: end}, + } + }) + + // Enterprise API instance with entitlements injected. + agpl := &agplcoderd.API{ + Options: &agplcoderd.Options{ + Entitlements: entSet, + }, + } + eapi := &coderd.API{ + AGPL: agpl, + Options: &coderd.Options{Options: agpl.Options}, + } + + // Template version that has an AI task. + tv := &database.TemplateVersion{ + HasAITask: sql.NullBool{Valid: true, Bool: true}, + HasExternalAgent: sql.NullBool{Valid: true, Bool: false}, + } + + task := &database.Task{ + TemplateVersionID: tv.ID, + } + + // Mock DB: no calls expected since managed agent limits are + // advisory only and no longer query the database at build time. + mDB := dbmock.NewMockStore(ctrl) + + ctx := context.Background() + + // Start transition: should be permitted even though the limit is + // breached. Managed agent limits are advisory only. + startResp, err := eapi.CheckBuildUsage(ctx, mDB, tv, task, database.WorkspaceTransitionStart) + require.NoError(t, err) + require.True(t, startResp.Permitted) + + // Stop transition: should also be permitted. + stopResp, err := eapi.CheckBuildUsage(ctx, mDB, tv, task, database.WorkspaceTransitionStop) + require.NoError(t, err) + require.True(t, stopResp.Permitted) + + // Delete transition: should also be permitted. + deleteResp, err := eapi.CheckBuildUsage(ctx, mDB, tv, task, database.WorkspaceTransitionDelete) + require.NoError(t, err) + require.True(t, deleteResp.Permitted) +} + +func TestCheckBuildUsage_BlocksWithoutManagedAgentEntitlement(t *testing.T) { + t.Parallel() + + tv := &database.TemplateVersion{ + HasAITask: sql.NullBool{Valid: true, Bool: true}, + HasExternalAgent: sql.NullBool{Valid: true, Bool: false}, + } + task := &database.Task{ + TemplateVersionID: tv.ID, + } + + // Both "feature absent" and "feature explicitly disabled" should + // block AI task builds on licensed deployments. + tests := []struct { + name string + setupEnts func(e *codersdk.Entitlements) + }{ + { + name: "FeatureAbsent", + setupEnts: func(e *codersdk.Entitlements) { + e.HasLicense = true + }, + }, + { + name: "FeatureDisabled", + setupEnts: func(e *codersdk.Entitlements) { + e.HasLicense = true + e.Features[codersdk.FeatureManagedAgentLimit] = codersdk.Feature{ + Enabled: false, + } + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + entSet := entitlements.New() + entSet.Modify(tc.setupEnts) + + agpl := &agplcoderd.API{ + Options: &agplcoderd.Options{ + Entitlements: entSet, + }, + } + eapi := &coderd.API{ + AGPL: agpl, + Options: &coderd.Options{Options: agpl.Options}, + } + + mDB := dbmock.NewMockStore(ctrl) + ctx := context.Background() + + // Start transition with a task: should be blocked because the + // license doesn't include the managed agent entitlement. + resp, err := eapi.CheckBuildUsage(ctx, mDB, tv, task, database.WorkspaceTransitionStart) + require.NoError(t, err) + require.False(t, resp.Permitted) + require.Contains(t, resp.Message, "not entitled to managed agents") + + // Stop and delete transitions should still be permitted so + // that existing workspaces can be stopped/cleaned up. + stopResp, err := eapi.CheckBuildUsage(ctx, mDB, tv, task, database.WorkspaceTransitionStop) + require.NoError(t, err) + require.True(t, stopResp.Permitted) + + deleteResp, err := eapi.CheckBuildUsage(ctx, mDB, tv, task, database.WorkspaceTransitionDelete) + require.NoError(t, err) + require.True(t, deleteResp.Permitted) + + // Start transition without a task: should be permitted (not + // an AI task build, so the entitlement check doesn't apply). + noTaskResp, err := eapi.CheckBuildUsage(ctx, mDB, tv, nil, database.WorkspaceTransitionStart) + require.NoError(t, err) + require.True(t, noTaskResp.Permitted) + }) + } +} + // testDBAuthzRole returns a context with a subject that has a role // with permissions required for test setup. func testDBAuthzRole(ctx context.Context) context.Context { @@ -1041,7 +1203,7 @@ func tcpEchoServer(t *testing.T) string { // nolint:revive // t takes precedence. func writeReadEcho(t *testing.T, ctx context.Context, conn net.Conn) { - msg := namesgenerator.GetRandomName(0) + msg := namesgenerator.UniqueName() deadline, ok := ctx.Deadline() if ok { diff --git a/enterprise/coderd/coderdenttest/coderdenttest.go b/enterprise/coderd/coderdenttest/coderdenttest.go index 29758c3dbf0..b8778e1fd99 100644 --- a/enterprise/coderd/coderdenttest/coderdenttest.go +++ b/enterprise/coderd/coderdenttest/coderdenttest.go @@ -15,7 +15,6 @@ import ( "github.com/golang-jwt/jwt/v4" "github.com/google/uuid" - "github.com/moby/moby/pkg/namesgenerator" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -25,6 +24,7 @@ import ( "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/pubsub" "github.com/coder/coder/v2/coderd/prebuilds" + "github.com/coder/coder/v2/coderd/util/namesgenerator" "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk/drpcsdk" @@ -226,12 +226,8 @@ func (opts *LicenseOptions) UserLimit(limit int64) *LicenseOptions { return opts.Feature(codersdk.FeatureUserLimit, limit) } -func (opts *LicenseOptions) ManagedAgentLimit(soft int64, hard int64) *LicenseOptions { - // These don't use named or exported feature names, see - // enterprise/coderd/license/license.go. - opts = opts.Feature(codersdk.FeatureName("managed_agent_limit_soft"), soft) - opts = opts.Feature(codersdk.FeatureName("managed_agent_limit_hard"), hard) - return opts +func (opts *LicenseOptions) ManagedAgentLimit(limit int64) *LicenseOptions { + return opts.Feature(codersdk.FeatureManagedAgentLimit, limit) } func (opts *LicenseOptions) Feature(name codersdk.FeatureName, value int64) *LicenseOptions { @@ -329,9 +325,9 @@ type CreateOrganizationOptions struct { func CreateOrganization(t *testing.T, client *codersdk.Client, opts CreateOrganizationOptions, mutators ...func(*codersdk.CreateOrganizationRequest)) codersdk.Organization { ctx := testutil.Context(t, testutil.WaitMedium) req := codersdk.CreateOrganizationRequest{ - Name: strings.ReplaceAll(strings.ToLower(namesgenerator.GetRandomName(0)), "_", "-"), - DisplayName: namesgenerator.GetRandomName(1), - Description: namesgenerator.GetRandomName(1), + Name: strings.ToLower(namesgenerator.UniqueNameWith("-")), + DisplayName: namesgenerator.UniqueName(), + Description: namesgenerator.UniqueName(), Icon: "", } for _, mutator := range mutators { diff --git a/enterprise/coderd/coderdenttest/proxytest.go b/enterprise/coderd/coderdenttest/proxytest.go index c4e5ed6019f..2b890499f2f 100644 --- a/enterprise/coderd/coderdenttest/proxytest.go +++ b/enterprise/coderd/coderdenttest/proxytest.go @@ -12,12 +12,12 @@ import ( "sync" "testing" - "github.com/moby/moby/pkg/namesgenerator" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "cdr.dev/slog" + "github.com/coder/coder/v2/coderd/util/namesgenerator" "github.com/coder/coder/v2/coderd/workspaceapps/appurl" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/enterprise/coderd" @@ -124,7 +124,7 @@ func NewWorkspaceProxyReplica(t *testing.T, coderdAPI *coderd.API, owner *coders } if options.Name == "" { - options.Name = namesgenerator.GetRandomName(1) + options.Name = namesgenerator.UniqueName() } token := options.Token diff --git a/enterprise/coderd/connectionlog.go b/enterprise/coderd/connectionlog.go index 05e3a40b2d7..c37e2ce497d 100644 --- a/enterprise/coderd/connectionlog.go +++ b/enterprise/coderd/connectionlog.go @@ -16,6 +16,9 @@ import ( "github.com/coder/coder/v2/codersdk" ) +// NOTE: See the auditLogCountCap note. +const connectionLogCountCap = 2000 + // @Summary Get connection logs // @ID get-connection-logs // @Security CoderSessionToken @@ -49,6 +52,7 @@ func (api *API) connectionLogs(rw http.ResponseWriter, r *http.Request) { // #nosec G115 - Safe conversion as pagination limit is expected to be within int32 range filter.LimitOpt = int32(page.Limit) + countFilter.CountCap = connectionLogCountCap count, err := api.Database.CountConnectionLogs(ctx, countFilter) if dbauthz.IsNotAuthorizedError(err) { httpapi.Forbidden(rw) @@ -63,6 +67,7 @@ func (api *API) connectionLogs(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, codersdk.ConnectionLogResponse{ ConnectionLogs: []codersdk.ConnectionLog{}, Count: 0, + CountCap: connectionLogCountCap, }) return } @@ -80,6 +85,7 @@ func (api *API) connectionLogs(rw http.ResponseWriter, r *http.Request) { httpapi.Write(ctx, rw, http.StatusOK, codersdk.ConnectionLogResponse{ ConnectionLogs: convertConnectionLogs(dblogs), Count: count, + CountCap: connectionLogCountCap, }) } diff --git a/enterprise/coderd/license/license.go b/enterprise/coderd/license/license.go index 3cf23823d2d..461e9875f97 100644 --- a/enterprise/coderd/license/license.go +++ b/enterprise/coderd/license/license.go @@ -14,60 +14,9 @@ import ( "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbauthz" - "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/codersdk" ) -const ( - // These features are only included in the license and are not actually - // entitlements after the licenses are processed. These values will be - // merged into the codersdk.FeatureManagedAgentLimit feature. - // - // The reason we need two separate features is because the License v3 format - // uses map[string]int64 for features, so we're unable to use a single value - // with a struct like `{"soft": 100, "hard": 200}`. This is unfortunate and - // we should fix this with a new license format v4 in the future. - // - // These are intentionally not exported as they should not be used outside - // of this package (except tests). - featureManagedAgentLimitHard codersdk.FeatureName = "managed_agent_limit_hard" - featureManagedAgentLimitSoft codersdk.FeatureName = "managed_agent_limit_soft" -) - -var ( - // Mapping of license feature names to the SDK feature name. - // This is used to map from multiple usage period features into a single SDK - // feature. - featureGrouping = map[codersdk.FeatureName]struct { - // The parent feature. - sdkFeature codersdk.FeatureName - // Whether the value of the license feature is the soft limit or the hard - // limit. - isSoft bool - }{ - // Map featureManagedAgentLimitHard and featureManagedAgentLimitSoft to - // codersdk.FeatureManagedAgentLimit. - featureManagedAgentLimitHard: { - sdkFeature: codersdk.FeatureManagedAgentLimit, - isSoft: false, - }, - featureManagedAgentLimitSoft: { - sdkFeature: codersdk.FeatureManagedAgentLimit, - isSoft: true, - }, - } - - // Features that are forbidden to be set in a license. These are the SDK - // features in the usagedBasedFeatureGrouping map. - licenseForbiddenFeatures = func() map[codersdk.FeatureName]struct{} { - features := make(map[codersdk.FeatureName]struct{}) - for _, feature := range featureGrouping { - features[feature.sdkFeature] = struct{}{} - } - return features - }() -) - // Entitlements processes licenses to return whether features are enabled or not. // TODO(@deansheather): This function and the related LicensesEntitlements // function should be refactored into smaller functions that: @@ -273,17 +222,15 @@ func LicensesEntitlements( // licenses with the corresponding features actually set // trump this default entitlement, even if they are set to a // smaller value. - defaultManagedAgentsIsuedAt = time.Date(2025, 7, 1, 0, 0, 0, 0, time.UTC) - defaultManagedAgentsStart = defaultManagedAgentsIsuedAt - defaultManagedAgentsEnd = defaultManagedAgentsStart.AddDate(100, 0, 0) - defaultManagedAgentsSoftLimit int64 = 1000 - defaultManagedAgentsHardLimit int64 = 1000 + defaultManagedAgentsIsuedAt = time.Date(2025, 7, 1, 0, 0, 0, 0, time.UTC) + defaultManagedAgentsStart = defaultManagedAgentsIsuedAt + defaultManagedAgentsEnd = defaultManagedAgentsStart.AddDate(100, 0, 0) + defaultManagedAgentsLimit int64 = 1000 ) entitlements.AddFeature(codersdk.FeatureManagedAgentLimit, codersdk.Feature{ Enabled: true, Entitlement: entitlement, - SoftLimit: &defaultManagedAgentsSoftLimit, - Limit: &defaultManagedAgentsHardLimit, + Limit: &defaultManagedAgentsLimit, UsagePeriod: &codersdk.UsagePeriod{ IssuedAt: defaultManagedAgentsIsuedAt, Start: defaultManagedAgentsStart, @@ -294,18 +241,8 @@ func LicensesEntitlements( // Add all features from the feature set defined. for _, featureName := range claims.FeatureSet.Features() { - if _, ok := licenseForbiddenFeatures[featureName]; ok { - // Ignore any FeatureSet features that are forbidden to be set - // in a license. - continue - } - if _, ok := featureGrouping[featureName]; ok { - // These features need very special handling due to merging - // multiple feature values into a single SDK feature. - continue - } - if featureName == codersdk.FeatureUserLimit || featureName.UsesUsagePeriod() { - // FeatureUserLimit and usage period features are handled below. + if featureName.UsesLimit() || featureName.UsesUsagePeriod() { + // Limit and usage period features are handled below. // They don't provide default values as they are always enabled // and require a limit to be specified in the license to have // any effect. @@ -320,30 +257,24 @@ func LicensesEntitlements( }) } - // A map of SDK feature name to the uncommitted usage feature. - uncommittedUsageFeatures := map[codersdk.FeatureName]usageLimit{} - // Features al-la-carte for featureName, featureValue := range claims.Features { - if _, ok := licenseForbiddenFeatures[featureName]; ok { - entitlements.Errors = append(entitlements.Errors, - fmt.Sprintf("Feature %s is forbidden to be set in a license.", featureName)) - continue + // Old-style licenses encode the managed agent limit as + // separate soft/hard features. + // + // This could be removed in a future release, but can only be + // done once all old licenses containing this are no longer in use. + if featureName == "managed_agent_limit_soft" { + // Maps the soft limit to the canonical feature name + featureName = codersdk.FeatureManagedAgentLimit } - if featureValue < 0 { - // We currently don't use negative values for features. + if featureName == "managed_agent_limit_hard" { + // We can safely ignore the hard limit as it is no longer used. continue } - // Special handling for grouped (e.g. usage period) features. - if grouping, ok := featureGrouping[featureName]; ok { - ul := uncommittedUsageFeatures[grouping.sdkFeature] - if grouping.isSoft { - ul.Soft = &featureValue - } else { - ul.Hard = &featureValue - } - uncommittedUsageFeatures[grouping.sdkFeature] = ul + if featureValue < 0 { + // We currently don't use negative values for features. continue } @@ -355,9 +286,20 @@ func LicensesEntitlements( continue } - // Handling for non-grouped features. - switch featureName { - case codersdk.FeatureUserLimit: + // Handling for limit features. + switch { + case featureName.UsesUsagePeriod(): + entitlements.AddFeature(featureName, codersdk.Feature{ + Enabled: featureValue > 0, + Entitlement: entitlement, + Limit: &featureValue, + UsagePeriod: &codersdk.UsagePeriod{ + IssuedAt: claims.IssuedAt.Time, + Start: usagePeriodStart, + End: usagePeriodEnd, + }, + }) + case featureName.UsesLimit(): if featureValue <= 0 { // 0 user count doesn't make sense, so we skip it. continue @@ -379,46 +321,6 @@ func LicensesEntitlements( } } } - - // Apply uncommitted usage features to the entitlements. - for featureName, ul := range uncommittedUsageFeatures { - if ul.Soft == nil || ul.Hard == nil { - // Invalid license. - entitlements.Errors = append(entitlements.Errors, - fmt.Sprintf("Invalid license (%s): feature %s has missing soft or hard limit values", license.UUID.String(), featureName)) - continue - } - if *ul.Hard < *ul.Soft { - entitlements.Errors = append(entitlements.Errors, - fmt.Sprintf("Invalid license (%s): feature %s has a hard limit less than the soft limit", license.UUID.String(), featureName)) - continue - } - if *ul.Hard < 0 || *ul.Soft < 0 { - entitlements.Errors = append(entitlements.Errors, - fmt.Sprintf("Invalid license (%s): feature %s has a soft or hard limit less than 0", license.UUID.String(), featureName)) - continue - } - - feature := codersdk.Feature{ - Enabled: true, - Entitlement: entitlement, - SoftLimit: ul.Soft, - Limit: ul.Hard, - // `Actual` will be populated below when warnings are generated. - UsagePeriod: &codersdk.UsagePeriod{ - IssuedAt: claims.IssuedAt.Time, - Start: usagePeriodStart, - End: usagePeriodEnd, - }, - } - // If the hard limit is 0, the feature is disabled. - if *ul.Hard <= 0 { - feature.Enabled = false - feature.SoftLimit = ptr.Ref(int64(0)) - feature.Limit = ptr.Ref(int64(0)) - } - entitlements.AddFeature(featureName, feature) - } } // Now the license specific warnings and errors are added to the entitlements. @@ -506,32 +408,9 @@ func LicensesEntitlements( entitlements.AddFeature(codersdk.FeatureManagedAgentLimit, agentLimit) // Only issue warnings if the feature is enabled. - if agentLimit.Enabled { - var softLimit int64 - if agentLimit.SoftLimit != nil { - softLimit = *agentLimit.SoftLimit - } - var hardLimit int64 - if agentLimit.Limit != nil { - hardLimit = *agentLimit.Limit - } - - // Issue a warning early: - // 1. If the soft limit and hard limit are equal, at 75% of the hard - // limit. - // 2. If the limit is greater than the soft limit, at 75% of the - // difference between the hard limit and the soft limit. - softWarningThreshold := int64(float64(hardLimit) * 0.75) - if hardLimit > softLimit && softLimit > 0 { - softWarningThreshold = softLimit + int64(float64(hardLimit-softLimit)*0.75) - } - if managedAgentCount >= *agentLimit.Limit { - entitlements.Warnings = append(entitlements.Warnings, - "You have built more workspaces with managed agents than your license allows. Further managed agent builds will be blocked.") - } else if managedAgentCount >= softWarningThreshold { - entitlements.Warnings = append(entitlements.Warnings, - "You are approaching the managed agent limit in your license. Please refer to the Deployment Licenses page for more information.") - } + if agentLimit.Enabled && agentLimit.Limit != nil && managedAgentCount >= *agentLimit.Limit { + entitlements.Warnings = append(entitlements.Warnings, + codersdk.LicenseManagedAgentLimitExceededWarningText) } } } @@ -621,11 +500,6 @@ var ( type Features map[codersdk.FeatureName]int64 -type usageLimit struct { - Soft *int64 - Hard *int64 // 0 means "disabled" -} - // Claims is the full set of claims in a license. type Claims struct { jwt.RegisteredClaims diff --git a/enterprise/coderd/license/license_test.go b/enterprise/coderd/license/license_test.go index 6c53fb3d89f..220c9d7dd3d 100644 --- a/enterprise/coderd/license/license_test.go +++ b/enterprise/coderd/license/license_test.go @@ -76,8 +76,7 @@ func TestEntitlements(t *testing.T) { f := make(license.Features) for _, name := range codersdk.FeatureNames { if name == codersdk.FeatureManagedAgentLimit { - f[codersdk.FeatureName("managed_agent_limit_soft")] = 100 - f[codersdk.FeatureName("managed_agent_limit_hard")] = 200 + f[codersdk.FeatureManagedAgentLimit] = 100 continue } f[name] = 1 @@ -520,8 +519,7 @@ func TestEntitlements(t *testing.T) { t.Run("Premium", func(t *testing.T) { t.Parallel() const userLimit = 1 - const expectedAgentSoftLimit = 1000 - const expectedAgentHardLimit = 1000 + const expectedAgentLimit = 1000 db, _ := dbtestutil.NewDB(t) licenseOptions := coderdenttest.LicenseOptions{ @@ -553,8 +551,7 @@ func TestEntitlements(t *testing.T) { agentEntitlement := entitlements.Features[featureName] require.True(t, agentEntitlement.Enabled) require.Equal(t, codersdk.EntitlementEntitled, agentEntitlement.Entitlement) - require.EqualValues(t, expectedAgentSoftLimit, *agentEntitlement.SoftLimit) - require.EqualValues(t, expectedAgentHardLimit, *agentEntitlement.Limit) + require.EqualValues(t, expectedAgentLimit, *agentEntitlement.Limit) // This might be shocking, but there's a sound reason for this. // See license.go for more details. @@ -814,7 +811,7 @@ func TestEntitlements(t *testing.T) { ExpiresAt: dbtime.Now().Add(time.Hour * 24 * 90).Truncate(time.Second), // 90 days to remove warning }). UserLimit(100). - ManagedAgentLimit(100, 200) + ManagedAgentLimit(100) lic := database.License{ ID: 1, @@ -855,16 +852,16 @@ func TestEntitlements(t *testing.T) { managedAgentLimit, ok := entitlements.Features[codersdk.FeatureManagedAgentLimit] require.True(t, ok) - require.NotNil(t, managedAgentLimit.SoftLimit) - require.EqualValues(t, 100, *managedAgentLimit.SoftLimit) + require.NotNil(t, managedAgentLimit.Limit) - require.EqualValues(t, 200, *managedAgentLimit.Limit) + // The soft limit value (100) is used as the single Limit. + require.EqualValues(t, 100, *managedAgentLimit.Limit) require.NotNil(t, managedAgentLimit.Actual) require.EqualValues(t, 175, *managedAgentLimit.Actual) - // Should've also populated a warning. + // Usage exceeds the limit, so an exceeded warning should be present. require.Len(t, entitlements.Warnings, 1) - require.Equal(t, "You are approaching the managed agent limit in your license. Please refer to the Deployment Licenses page for more information.", entitlements.Warnings[0]) + require.Equal(t, codersdk.LicenseManagedAgentLimitExceededWarningText, entitlements.Warnings[0]) }) } @@ -1081,13 +1078,12 @@ func TestLicenseEntitlements(t *testing.T) { { Name: "ManagedAgentLimit", Licenses: []*coderdenttest.LicenseOptions{ - enterpriseLicense().UserLimit(100).ManagedAgentLimit(100, 200), + enterpriseLicense().UserLimit(100).ManagedAgentLimit(100), }, Arguments: license.FeatureArguments{ ManagedAgentCountFn: func(ctx context.Context, from time.Time, to time.Time) (int64, error) { - // 175 will generate a warning as it's over 75% of the - // difference between the soft and hard limit. - return 174, nil + // 74 is below the limit (soft=100), so no warning. + return 74, nil }, }, AssertEntitlements: func(t *testing.T, entitlements codersdk.Entitlements) { @@ -1096,9 +1092,9 @@ func TestLicenseEntitlements(t *testing.T) { feature := entitlements.Features[codersdk.FeatureManagedAgentLimit] assert.Equal(t, codersdk.EntitlementEntitled, feature.Entitlement) assert.True(t, feature.Enabled) - assert.Equal(t, int64(100), *feature.SoftLimit) - assert.Equal(t, int64(200), *feature.Limit) - assert.Equal(t, int64(174), *feature.Actual) + // Soft limit value is used as the single Limit. + assert.Equal(t, int64(100), *feature.Limit) + assert.Equal(t, int64(74), *feature.Actual) }, }, { @@ -1111,7 +1107,7 @@ func TestLicenseEntitlements(t *testing.T) { WithIssuedAt(time.Now().Add(-time.Hour * 2)), enterpriseLicense(). UserLimit(100). - ManagedAgentLimit(100, 100). + ManagedAgentLimit(100). WithIssuedAt(time.Now().Add(-time.Hour * 1)). GracePeriod(time.Now()), }, @@ -1128,7 +1124,6 @@ func TestLicenseEntitlements(t *testing.T) { feature := entitlements.Features[codersdk.FeatureManagedAgentLimit] assert.Equal(t, codersdk.EntitlementGracePeriod, feature.Entitlement) assert.True(t, feature.Enabled) - assert.Equal(t, int64(100), *feature.SoftLimit) assert.Equal(t, int64(100), *feature.Limit) assert.Equal(t, int64(74), *feature.Actual) }, @@ -1143,7 +1138,7 @@ func TestLicenseEntitlements(t *testing.T) { WithIssuedAt(time.Now().Add(-time.Hour * 2)), enterpriseLicense(). UserLimit(100). - ManagedAgentLimit(100, 200). + ManagedAgentLimit(100). WithIssuedAt(time.Now().Add(-time.Hour * 1)). Expired(time.Now()), }, @@ -1156,84 +1151,33 @@ func TestLicenseEntitlements(t *testing.T) { feature := entitlements.Features[codersdk.FeatureManagedAgentLimit] assert.Equal(t, codersdk.EntitlementNotEntitled, feature.Entitlement) assert.False(t, feature.Enabled) - assert.Nil(t, feature.SoftLimit) assert.Nil(t, feature.Limit) assert.Nil(t, feature.Actual) }, }, { - Name: "ManagedAgentLimitWarning/ApproachingLimit/DifferentSoftAndHardLimit", - Licenses: []*coderdenttest.LicenseOptions{ - enterpriseLicense(). - UserLimit(100). - ManagedAgentLimit(100, 200), - }, - Arguments: license.FeatureArguments{ - ManagedAgentCountFn: func(ctx context.Context, from time.Time, to time.Time) (int64, error) { - return 175, nil - }, - }, - AssertEntitlements: func(t *testing.T, entitlements codersdk.Entitlements) { - assert.Len(t, entitlements.Warnings, 1) - assert.Equal(t, "You are approaching the managed agent limit in your license. Please refer to the Deployment Licenses page for more information.", entitlements.Warnings[0]) - assertNoErrors(t, entitlements) - - feature := entitlements.Features[codersdk.FeatureManagedAgentLimit] - assert.Equal(t, codersdk.EntitlementEntitled, feature.Entitlement) - assert.True(t, feature.Enabled) - assert.Equal(t, int64(100), *feature.SoftLimit) - assert.Equal(t, int64(200), *feature.Limit) - assert.Equal(t, int64(175), *feature.Actual) - }, - }, - { - Name: "ManagedAgentLimitWarning/ApproachingLimit/EqualSoftAndHardLimit", + Name: "ManagedAgentLimitWarning/ExceededLimit", Licenses: []*coderdenttest.LicenseOptions{ enterpriseLicense(). UserLimit(100). - ManagedAgentLimit(100, 100), + ManagedAgentLimit(100), }, Arguments: license.FeatureArguments{ ManagedAgentCountFn: func(ctx context.Context, from time.Time, to time.Time) (int64, error) { - return 75, nil + return 150, nil }, }, AssertEntitlements: func(t *testing.T, entitlements codersdk.Entitlements) { assert.Len(t, entitlements.Warnings, 1) - assert.Equal(t, "You are approaching the managed agent limit in your license. Please refer to the Deployment Licenses page for more information.", entitlements.Warnings[0]) + assert.Equal(t, codersdk.LicenseManagedAgentLimitExceededWarningText, entitlements.Warnings[0]) assertNoErrors(t, entitlements) feature := entitlements.Features[codersdk.FeatureManagedAgentLimit] assert.Equal(t, codersdk.EntitlementEntitled, feature.Entitlement) assert.True(t, feature.Enabled) - assert.Equal(t, int64(100), *feature.SoftLimit) + // Soft limit (100) is used as the single Limit. assert.Equal(t, int64(100), *feature.Limit) - assert.Equal(t, int64(75), *feature.Actual) - }, - }, - { - Name: "ManagedAgentLimitWarning/BreachedLimit", - Licenses: []*coderdenttest.LicenseOptions{ - enterpriseLicense(). - UserLimit(100). - ManagedAgentLimit(100, 200), - }, - Arguments: license.FeatureArguments{ - ManagedAgentCountFn: func(ctx context.Context, from time.Time, to time.Time) (int64, error) { - return 200, nil - }, - }, - AssertEntitlements: func(t *testing.T, entitlements codersdk.Entitlements) { - assert.Len(t, entitlements.Warnings, 1) - assert.Equal(t, "You have built more workspaces with managed agents than your license allows. Further managed agent builds will be blocked.", entitlements.Warnings[0]) - assertNoErrors(t, entitlements) - - feature := entitlements.Features[codersdk.FeatureManagedAgentLimit] - assert.Equal(t, codersdk.EntitlementEntitled, feature.Entitlement) - assert.True(t, feature.Enabled) - assert.Equal(t, int64(100), *feature.SoftLimit) - assert.Equal(t, int64(200), *feature.Limit) - assert.Equal(t, int64(200), *feature.Actual) + assert.Equal(t, int64(150), *feature.Actual) }, }, { @@ -1288,173 +1232,240 @@ func TestLicenseEntitlements(t *testing.T) { func TestUsageLimitFeatures(t *testing.T) { t.Parallel() - cases := []struct { - sdkFeatureName codersdk.FeatureName - softLimitFeatureName codersdk.FeatureName - hardLimitFeatureName codersdk.FeatureName - }{ - { - sdkFeatureName: codersdk.FeatureManagedAgentLimit, - softLimitFeatureName: codersdk.FeatureName("managed_agent_limit_soft"), - hardLimitFeatureName: codersdk.FeatureName("managed_agent_limit_hard"), - }, - } + // Ensures that usage limit features are ranked by issued at, not by + // values. + t.Run("IssuedAtRanking", func(t *testing.T) { + t.Parallel() - for _, c := range cases { - t.Run(string(c.sdkFeatureName), func(t *testing.T) { - t.Parallel() + // Generate 2 real licenses both with managed agent limit + // features. lic2 should trump lic1 even though it has a lower + // limit, because it was issued later. + lic1 := database.License{ + ID: 1, + UploadedAt: time.Now(), + Exp: time.Now().Add(time.Hour), + UUID: uuid.New(), + JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ + IssuedAt: time.Now().Add(-time.Minute * 2), + NotBefore: time.Now().Add(-time.Minute * 2), + ExpiresAt: time.Now().Add(time.Hour * 2), + Features: license.Features{ + codersdk.FeatureManagedAgentLimit: 100, + }, + }), + } + lic2Iat := time.Now().Add(-time.Minute * 1) + lic2Nbf := lic2Iat.Add(-time.Minute) + lic2Exp := lic2Iat.Add(time.Hour) + lic2 := database.License{ + ID: 2, + UploadedAt: time.Now(), + Exp: lic2Exp, + UUID: uuid.New(), + JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ + IssuedAt: lic2Iat, + NotBefore: lic2Nbf, + ExpiresAt: lic2Exp, + Features: license.Features{ + codersdk.FeatureManagedAgentLimit: 50, + }, + }), + } - // Test for either a missing soft or hard limit feature value. - t.Run("MissingGroupedFeature", func(t *testing.T) { - t.Parallel() - - for _, feature := range []codersdk.FeatureName{ - c.softLimitFeatureName, - c.hardLimitFeatureName, - } { - t.Run(string(feature), func(t *testing.T) { - t.Parallel() - - lic := database.License{ - ID: 1, - UploadedAt: time.Now(), - Exp: time.Now().Add(time.Hour), - UUID: uuid.New(), - JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ - Features: license.Features{ - feature: 100, - }, - }), - } + const actualAgents = 10 + arguments := license.FeatureArguments{ + ActiveUserCount: 10, + ReplicaCount: 0, + ExternalAuthCount: 0, + ManagedAgentCountFn: func(ctx context.Context, from time.Time, to time.Time) (int64, error) { + return actualAgents, nil + }, + } - arguments := license.FeatureArguments{ - ManagedAgentCountFn: func(ctx context.Context, from time.Time, to time.Time) (int64, error) { - return 0, nil - }, - } - entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), []database.License{lic}, map[codersdk.FeatureName]bool{}, coderdenttest.Keys, arguments) - require.NoError(t, err) + // Load the licenses in both orders to ensure the correct + // behavior is observed no matter the order. + for _, order := range [][]database.License{ + {lic1, lic2}, + {lic2, lic1}, + } { + entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), order, map[codersdk.FeatureName]bool{}, coderdenttest.Keys, arguments) + require.NoError(t, err) + + feature, ok := entitlements.Features[codersdk.FeatureManagedAgentLimit] + require.True(t, ok, "feature %s not found", codersdk.FeatureManagedAgentLimit) + require.Equal(t, codersdk.EntitlementEntitled, feature.Entitlement) + require.NotNil(t, feature.Limit) + require.EqualValues(t, 50, *feature.Limit) + require.NotNil(t, feature.Actual) + require.EqualValues(t, actualAgents, *feature.Actual) + require.NotNil(t, feature.UsagePeriod) + require.WithinDuration(t, lic2Iat, feature.UsagePeriod.IssuedAt, 2*time.Second) + require.WithinDuration(t, lic2Nbf, feature.UsagePeriod.Start, 2*time.Second) + require.WithinDuration(t, lic2Exp, feature.UsagePeriod.End, 2*time.Second) + } + }) +} - feature, ok := entitlements.Features[c.sdkFeatureName] - require.True(t, ok, "feature %s not found", c.sdkFeatureName) - require.Equal(t, codersdk.EntitlementNotEntitled, feature.Entitlement) +// TestOldStyleManagedAgentLicenses ensures backward compatibility with +// older licenses that encode the managed agent limit using separate +// "managed_agent_limit_soft" and "managed_agent_limit_hard" feature keys +// instead of the canonical "managed_agent_limit" key. +func TestOldStyleManagedAgentLicenses(t *testing.T) { + t.Parallel() - require.Len(t, entitlements.Errors, 1) - require.Equal(t, fmt.Sprintf("Invalid license (%v): feature %s has missing soft or hard limit values", lic.UUID, c.sdkFeatureName), entitlements.Errors[0]) - }) - } - }) + t.Run("SoftAndHard", func(t *testing.T) { + t.Parallel() - t.Run("HardBelowSoft", func(t *testing.T) { - t.Parallel() + lic := database.License{ + ID: 1, + UploadedAt: time.Now(), + Exp: time.Now().Add(time.Hour), + UUID: uuid.New(), + JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureName("managed_agent_limit_soft"): 100, + codersdk.FeatureName("managed_agent_limit_hard"): 200, + }, + }), + } - lic := database.License{ - ID: 1, - UploadedAt: time.Now(), - Exp: time.Now().Add(time.Hour), - UUID: uuid.New(), - JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ - Features: license.Features{ - c.softLimitFeatureName: 100, - c.hardLimitFeatureName: 50, - }, - }), - } + const actualAgents = 42 + arguments := license.FeatureArguments{ + ManagedAgentCountFn: func(_ context.Context, _, _ time.Time) (int64, error) { + return actualAgents, nil + }, + } - arguments := license.FeatureArguments{ - ManagedAgentCountFn: func(ctx context.Context, from time.Time, to time.Time) (int64, error) { - return 0, nil - }, - } - entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), []database.License{lic}, map[codersdk.FeatureName]bool{}, coderdenttest.Keys, arguments) - require.NoError(t, err) + entitlements, err := license.LicensesEntitlements( + context.Background(), time.Now(), []database.License{lic}, + map[codersdk.FeatureName]bool{}, coderdenttest.Keys, arguments, + ) + require.NoError(t, err) + require.Empty(t, entitlements.Errors) - feature, ok := entitlements.Features[c.sdkFeatureName] - require.True(t, ok, "feature %s not found", c.sdkFeatureName) - require.Equal(t, codersdk.EntitlementNotEntitled, feature.Entitlement) - - require.Len(t, entitlements.Errors, 1) - require.Equal(t, fmt.Sprintf("Invalid license (%v): feature %s has a hard limit less than the soft limit", lic.UUID, c.sdkFeatureName), entitlements.Errors[0]) - }) - - // Ensures that these features are ranked by issued at, not by - // values. - t.Run("IssuedAtRanking", func(t *testing.T) { - t.Parallel() - - // Generate 2 real licenses both with managed agent limit - // features. lic2 should trump lic1 even though it has a lower - // limit, because it was issued later. - lic1 := database.License{ - ID: 1, - UploadedAt: time.Now(), - Exp: time.Now().Add(time.Hour), - UUID: uuid.New(), - JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ - IssuedAt: time.Now().Add(-time.Minute * 2), - NotBefore: time.Now().Add(-time.Minute * 2), - ExpiresAt: time.Now().Add(time.Hour * 2), - Features: license.Features{ - c.softLimitFeatureName: 100, - c.hardLimitFeatureName: 200, - }, - }), - } - lic2Iat := time.Now().Add(-time.Minute * 1) - lic2Nbf := lic2Iat.Add(-time.Minute) - lic2Exp := lic2Iat.Add(time.Hour) - lic2 := database.License{ - ID: 2, - UploadedAt: time.Now(), - Exp: lic2Exp, - UUID: uuid.New(), - JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ - IssuedAt: lic2Iat, - NotBefore: lic2Nbf, - ExpiresAt: lic2Exp, - Features: license.Features{ - c.softLimitFeatureName: 50, - c.hardLimitFeatureName: 100, - }, - }), - } + feature := entitlements.Features[codersdk.FeatureManagedAgentLimit] + require.Equal(t, codersdk.EntitlementEntitled, feature.Entitlement) + require.True(t, feature.Enabled) + require.NotNil(t, feature.Limit) + // The soft limit should be used as the canonical limit. + require.EqualValues(t, 100, *feature.Limit) + require.NotNil(t, feature.Actual) + require.EqualValues(t, actualAgents, *feature.Actual) + require.NotNil(t, feature.UsagePeriod) + }) - const actualAgents = 10 - arguments := license.FeatureArguments{ - ActiveUserCount: 10, - ReplicaCount: 0, - ExternalAuthCount: 0, - ManagedAgentCountFn: func(ctx context.Context, from time.Time, to time.Time) (int64, error) { - return actualAgents, nil - }, - } + t.Run("OnlySoft", func(t *testing.T) { + t.Parallel() - // Load the licenses in both orders to ensure the correct - // behavior is observed no matter the order. - for _, order := range [][]database.License{ - {lic1, lic2}, - {lic2, lic1}, - } { - entitlements, err := license.LicensesEntitlements(context.Background(), time.Now(), order, map[codersdk.FeatureName]bool{}, coderdenttest.Keys, arguments) - require.NoError(t, err) - - feature, ok := entitlements.Features[c.sdkFeatureName] - require.True(t, ok, "feature %s not found", c.sdkFeatureName) - require.Equal(t, codersdk.EntitlementEntitled, feature.Entitlement) - require.NotNil(t, feature.Limit) - require.EqualValues(t, 100, *feature.Limit) - require.NotNil(t, feature.SoftLimit) - require.EqualValues(t, 50, *feature.SoftLimit) - require.NotNil(t, feature.Actual) - require.EqualValues(t, actualAgents, *feature.Actual) - require.NotNil(t, feature.UsagePeriod) - require.WithinDuration(t, lic2Iat, feature.UsagePeriod.IssuedAt, 2*time.Second) - require.WithinDuration(t, lic2Nbf, feature.UsagePeriod.Start, 2*time.Second) - require.WithinDuration(t, lic2Exp, feature.UsagePeriod.End, 2*time.Second) - } - }) - }) - } + lic := database.License{ + ID: 1, + UploadedAt: time.Now(), + Exp: time.Now().Add(time.Hour), + UUID: uuid.New(), + JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureName("managed_agent_limit_soft"): 75, + }, + }), + } + + const actualAgents = 10 + arguments := license.FeatureArguments{ + ManagedAgentCountFn: func(_ context.Context, _, _ time.Time) (int64, error) { + return actualAgents, nil + }, + } + + entitlements, err := license.LicensesEntitlements( + context.Background(), time.Now(), []database.License{lic}, + map[codersdk.FeatureName]bool{}, coderdenttest.Keys, arguments, + ) + require.NoError(t, err) + require.Empty(t, entitlements.Errors) + + feature := entitlements.Features[codersdk.FeatureManagedAgentLimit] + require.Equal(t, codersdk.EntitlementEntitled, feature.Entitlement) + require.True(t, feature.Enabled) + require.NotNil(t, feature.Limit) + require.EqualValues(t, 75, *feature.Limit) + }) + + // A license with only the hard limit key should silently ignore it, + // leaving the feature unset (not entitled). + t.Run("OnlyHard", func(t *testing.T) { + t.Parallel() + + lic := database.License{ + ID: 1, + UploadedAt: time.Now(), + Exp: time.Now().Add(time.Hour), + UUID: uuid.New(), + JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureName("managed_agent_limit_hard"): 200, + }, + }), + } + + arguments := license.FeatureArguments{ + ManagedAgentCountFn: func(_ context.Context, _, _ time.Time) (int64, error) { + return 0, nil + }, + } + + entitlements, err := license.LicensesEntitlements( + context.Background(), time.Now(), []database.License{lic}, + map[codersdk.FeatureName]bool{}, coderdenttest.Keys, arguments, + ) + require.NoError(t, err) + require.Empty(t, entitlements.Errors) + + feature := entitlements.Features[codersdk.FeatureManagedAgentLimit] + require.Equal(t, codersdk.EntitlementNotEntitled, feature.Entitlement) + }) + + // Old-style license with both soft and hard set to zero should + // explicitly disable the feature (and override any Premium default). + t.Run("ExplicitZero", func(t *testing.T) { + t.Parallel() + + lic := database.License{ + ID: 1, + UploadedAt: time.Now(), + Exp: time.Now().Add(time.Hour), + UUID: uuid.New(), + JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ + FeatureSet: codersdk.FeatureSetPremium, + Features: license.Features{ + codersdk.FeatureUserLimit: 100, + codersdk.FeatureName("managed_agent_limit_soft"): 0, + codersdk.FeatureName("managed_agent_limit_hard"): 0, + }, + }), + } + + const actualAgents = 5 + arguments := license.FeatureArguments{ + ActiveUserCount: 10, + ManagedAgentCountFn: func(_ context.Context, _, _ time.Time) (int64, error) { + return actualAgents, nil + }, + } + + entitlements, err := license.LicensesEntitlements( + context.Background(), time.Now(), []database.License{lic}, + map[codersdk.FeatureName]bool{}, coderdenttest.Keys, arguments, + ) + require.NoError(t, err) + + feature := entitlements.Features[codersdk.FeatureManagedAgentLimit] + require.Equal(t, codersdk.EntitlementEntitled, feature.Entitlement) + require.False(t, feature.Enabled) + require.NotNil(t, feature.Limit) + require.EqualValues(t, 0, *feature.Limit) + require.NotNil(t, feature.Actual) + require.EqualValues(t, actualAgents, *feature.Actual) + }) } func TestManagedAgentLimitDefault(t *testing.T) { @@ -1492,20 +1503,16 @@ func TestManagedAgentLimitDefault(t *testing.T) { require.True(t, ok, "feature %s not found", codersdk.FeatureManagedAgentLimit) require.Equal(t, codersdk.EntitlementNotEntitled, feature.Entitlement) require.Nil(t, feature.Limit) - require.Nil(t, feature.SoftLimit) require.Nil(t, feature.Actual) require.Nil(t, feature.UsagePeriod) }) - // "Premium" licenses should receive a default managed agent limit of: - // soft = 1000 - // hard = 1000 + // "Premium" licenses should receive a default managed agent limit of 1000. t.Run("Premium", func(t *testing.T) { t.Parallel() const userLimit = 33 - const softLimit = 1000 - const hardLimit = 1000 + const defaultLimit = 1000 lic := database.License{ ID: 1, UploadedAt: time.Now(), @@ -1536,9 +1543,7 @@ func TestManagedAgentLimitDefault(t *testing.T) { require.True(t, ok, "feature %s not found", codersdk.FeatureManagedAgentLimit) require.Equal(t, codersdk.EntitlementEntitled, feature.Entitlement) require.NotNil(t, feature.Limit) - require.EqualValues(t, hardLimit, *feature.Limit) - require.NotNil(t, feature.SoftLimit) - require.EqualValues(t, softLimit, *feature.SoftLimit) + require.EqualValues(t, defaultLimit, *feature.Limit) require.NotNil(t, feature.Actual) require.EqualValues(t, actualAgents, *feature.Actual) require.NotNil(t, feature.UsagePeriod) @@ -1547,8 +1552,8 @@ func TestManagedAgentLimitDefault(t *testing.T) { require.NotZero(t, feature.UsagePeriod.End) }) - // "Premium" licenses with an explicit managed agent limit should not - // receive a default managed agent limit. + // "Premium" licenses with an explicit managed agent limit should use + // that value instead of the default. t.Run("PremiumExplicitValues", func(t *testing.T) { t.Parallel() @@ -1560,9 +1565,8 @@ func TestManagedAgentLimitDefault(t *testing.T) { JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ FeatureSet: codersdk.FeatureSetPremium, Features: license.Features{ - codersdk.FeatureUserLimit: 100, - codersdk.FeatureName("managed_agent_limit_soft"): 100, - codersdk.FeatureName("managed_agent_limit_hard"): 200, + codersdk.FeatureUserLimit: 100, + codersdk.FeatureManagedAgentLimit: 100, }, }), } @@ -1584,9 +1588,7 @@ func TestManagedAgentLimitDefault(t *testing.T) { require.True(t, ok, "feature %s not found", codersdk.FeatureManagedAgentLimit) require.Equal(t, codersdk.EntitlementEntitled, feature.Entitlement) require.NotNil(t, feature.Limit) - require.EqualValues(t, 200, *feature.Limit) - require.NotNil(t, feature.SoftLimit) - require.EqualValues(t, 100, *feature.SoftLimit) + require.EqualValues(t, 100, *feature.Limit) require.NotNil(t, feature.Actual) require.EqualValues(t, actualAgents, *feature.Actual) require.NotNil(t, feature.UsagePeriod) @@ -1608,9 +1610,8 @@ func TestManagedAgentLimitDefault(t *testing.T) { JWT: coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{ FeatureSet: codersdk.FeatureSetPremium, Features: license.Features{ - codersdk.FeatureUserLimit: 100, - codersdk.FeatureName("managed_agent_limit_soft"): 0, - codersdk.FeatureName("managed_agent_limit_hard"): 0, + codersdk.FeatureUserLimit: 100, + codersdk.FeatureManagedAgentLimit: 0, }, }), } @@ -1634,8 +1635,6 @@ func TestManagedAgentLimitDefault(t *testing.T) { require.False(t, feature.Enabled) require.NotNil(t, feature.Limit) require.EqualValues(t, 0, *feature.Limit) - require.NotNil(t, feature.SoftLimit) - require.EqualValues(t, 0, *feature.SoftLimit) require.NotNil(t, feature.Actual) require.EqualValues(t, actualAgents, *feature.Actual) require.NotNil(t, feature.UsagePeriod) diff --git a/enterprise/coderd/prebuilds/claim.go b/enterprise/coderd/prebuilds/claim.go index 743513cedbc..e057fb03d60 100644 --- a/enterprise/coderd/prebuilds/claim.go +++ b/enterprise/coderd/prebuilds/claim.go @@ -13,18 +13,15 @@ import ( "github.com/coder/coder/v2/coderd/prebuilds" ) -type EnterpriseClaimer struct { - store database.Store -} +type EnterpriseClaimer struct{} -func NewEnterpriseClaimer(store database.Store) *EnterpriseClaimer { - return &EnterpriseClaimer{ - store: store, - } +func NewEnterpriseClaimer() *EnterpriseClaimer { + return &EnterpriseClaimer{} } -func (c EnterpriseClaimer) Claim( +func (EnterpriseClaimer) Claim( ctx context.Context, + store database.Store, now time.Time, userID uuid.UUID, name string, @@ -33,7 +30,7 @@ func (c EnterpriseClaimer) Claim( nextStartAt sql.NullTime, ttl sql.NullInt64, ) (*uuid.UUID, error) { - result, err := c.store.ClaimPrebuiltWorkspace(ctx, database.ClaimPrebuiltWorkspaceParams{ + result, err := store.ClaimPrebuiltWorkspace(ctx, database.ClaimPrebuiltWorkspaceParams{ NewUserID: userID, NewName: name, Now: now, diff --git a/enterprise/coderd/prebuilds/claim_test.go b/enterprise/coderd/prebuilds/claim_test.go index 217a9ff0961..79262401c87 100644 --- a/enterprise/coderd/prebuilds/claim_test.go +++ b/enterprise/coderd/prebuilds/claim_test.go @@ -167,8 +167,14 @@ func TestClaimPrebuild(t *testing.T) { defer provisionerCloser.Close() cache := files.New(prometheus.NewRegistry(), &coderdtest.FakeAuthorizer{}) - reconciler := prebuilds.NewStoreReconciler(spy, pubsub, cache, codersdk.PrebuildsConfig{}, logger, quartz.NewMock(t), prometheus.NewRegistry(), newNoopEnqueuer(), newNoopUsageCheckerPtr()) - var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer(spy) + reconciler := prebuilds.NewStoreReconciler( + spy, pubsub, cache, codersdk.PrebuildsConfig{}, logger, + quartz.NewMock(t), + prometheus.NewRegistry(), + newNoopEnqueuer(), + newNoopUsageCheckerPtr(), + ) + var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer() api.AGPL.PrebuildsClaimer.Store(&claimer) version := coderdtest.CreateTemplateVersion(t, client, orgID, templateWithAgentAndPresetsWithPrebuilds(desiredInstances)) diff --git a/enterprise/coderd/provisionerdaemons.go b/enterprise/coderd/provisionerdaemons.go index 0f6db2508af..5bf13419a90 100644 --- a/enterprise/coderd/provisionerdaemons.go +++ b/enterprise/coderd/provisionerdaemons.go @@ -11,7 +11,6 @@ import ( "github.com/google/uuid" "github.com/hashicorp/yamux" - "github.com/moby/moby/pkg/namesgenerator" "go.opentelemetry.io/otel/trace" "golang.org/x/exp/maps" "golang.org/x/xerrors" @@ -31,6 +30,7 @@ import ( "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/coderd/rbac/policy" "github.com/coder/coder/v2/coderd/telemetry" + "github.com/coder/coder/v2/coderd/util/namesgenerator" "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk/drpcsdk" @@ -193,7 +193,7 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request) } } - name := namesgenerator.GetRandomName(10) + name := namesgenerator.NameDigitWith("_") if vals, ok := r.URL.Query()["name"]; ok && len(vals) > 0 { name = vals[0] } else { @@ -376,7 +376,7 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request) _ = conn.Close(websocket.StatusInternalError, httpapi.WebsocketCloseSprintf("drpc register provisioner daemon: %s", err)) return } - server := drpcserver.NewWithOptions(mux, drpcserver.Options{ + server := drpcsdk.NewServer(logger, mux, drpcserver.Options{ Manager: drpcsdk.DefaultDRPCOptions(nil), Log: func(err error) { if xerrors.Is(err, io.EOF) { diff --git a/enterprise/coderd/subagent_test.go b/enterprise/coderd/subagent_test.go new file mode 100644 index 00000000000..a2653bc982d --- /dev/null +++ b/enterprise/coderd/subagent_test.go @@ -0,0 +1,515 @@ +package coderd_test + +import ( + "cmp" + "context" + "slices" + "strings" + "sync/atomic" + "testing" + + "github.com/google/uuid" + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + + "github.com/coder/coder/v2/agent/agentcontainers" + "github.com/coder/coder/v2/agent/proto" + "github.com/coder/coder/v2/coderd/agentapi" + "github.com/coder/coder/v2/coderd/database" + "github.com/coder/coder/v2/coderd/database/db2sdk" + agpldbauthz "github.com/coder/coder/v2/coderd/database/dbauthz" + "github.com/coder/coder/v2/coderd/database/dbgen" + "github.com/coder/coder/v2/coderd/database/dbmock" + "github.com/coder/coder/v2/coderd/database/dbtestutil" + agplportsharing "github.com/coder/coder/v2/coderd/portsharing" + "github.com/coder/coder/v2/coderd/rbac" + "github.com/coder/coder/v2/coderd/util/ptr" + "github.com/coder/coder/v2/codersdk" + entdbauthz "github.com/coder/coder/v2/enterprise/coderd/dbauthz" + entportsharing "github.com/coder/coder/v2/enterprise/coderd/portsharing" + "github.com/coder/coder/v2/testutil" + "github.com/coder/quartz" +) + +func TestSubAgentAPICreateSubAgentAppShareRespectsEnterpriseMaxPortShareLevel(t *testing.T) { + t.Parallel() + + type expectedApp struct { + slugSuffix string + sharingLevel database.AppSharingLevel + } + + tests := []struct { + name string + maxPortShareLevel database.AppSharingLevel + apps []*proto.CreateSubAgentRequest_App + expectedStoredApps []expectedApp + }{ + { + name: "AuthenticatedClampsPublicOnly", + maxPortShareLevel: database.AppSharingLevelAuthenticated, + apps: []*proto.CreateSubAgentRequest_App{ + { + Slug: "public-app", + Share: proto.CreateSubAgentRequest_App_PUBLIC.Enum(), + Url: ptr.Ref("http://localhost:8080"), + }, + { + Slug: "authenticated-app", + Share: proto.CreateSubAgentRequest_App_AUTHENTICATED.Enum(), + Url: ptr.Ref("http://localhost:8081"), + }, + { + Slug: "owner-app", + Share: proto.CreateSubAgentRequest_App_OWNER.Enum(), + Url: ptr.Ref("http://localhost:8082"), + }, + { + Slug: "organization-app", + Share: proto.CreateSubAgentRequest_App_ORGANIZATION.Enum(), + Url: ptr.Ref("http://localhost:8083"), + }, + }, + expectedStoredApps: []expectedApp{ + { + slugSuffix: "-authenticated-app", + sharingLevel: database.AppSharingLevelAuthenticated, + }, + { + slugSuffix: "-organization-app", + sharingLevel: database.AppSharingLevelOrganization, + }, + { + slugSuffix: "-owner-app", + sharingLevel: database.AppSharingLevelOwner, + }, + { + slugSuffix: "-public-app", + sharingLevel: database.AppSharingLevelAuthenticated, + }, + }, + }, + { + name: "PublicAllowsPublicAuthenticatedOrganizationAndOwner", + maxPortShareLevel: database.AppSharingLevelPublic, + apps: []*proto.CreateSubAgentRequest_App{ + { + Slug: "public-app", + Share: proto.CreateSubAgentRequest_App_PUBLIC.Enum(), + Url: ptr.Ref("http://localhost:8080"), + }, + { + Slug: "authenticated-app", + Share: proto.CreateSubAgentRequest_App_AUTHENTICATED.Enum(), + Url: ptr.Ref("http://localhost:8081"), + }, + { + Slug: "owner-app", + Share: proto.CreateSubAgentRequest_App_OWNER.Enum(), + Url: ptr.Ref("http://localhost:8082"), + }, + { + Slug: "organization-app", + Share: proto.CreateSubAgentRequest_App_ORGANIZATION.Enum(), + Url: ptr.Ref("http://localhost:8083"), + }, + }, + expectedStoredApps: []expectedApp{ + { + slugSuffix: "-authenticated-app", + sharingLevel: database.AppSharingLevelAuthenticated, + }, + { + slugSuffix: "-organization-app", + sharingLevel: database.AppSharingLevelOrganization, + }, + { + slugSuffix: "-owner-app", + sharingLevel: database.AppSharingLevelOwner, + }, + { + slugSuffix: "-public-app", + sharingLevel: database.AppSharingLevelPublic, + }, + }, + }, + { + name: "OrganizationClampsAuthenticatedAndPublic", + maxPortShareLevel: database.AppSharingLevelOrganization, + apps: []*proto.CreateSubAgentRequest_App{ + { + Slug: "authenticated-app", + Share: proto.CreateSubAgentRequest_App_AUTHENTICATED.Enum(), + Url: ptr.Ref("http://localhost:8080"), + }, + { + Slug: "public-app", + Share: proto.CreateSubAgentRequest_App_PUBLIC.Enum(), + Url: ptr.Ref("http://localhost:8081"), + }, + { + Slug: "owner-app", + Share: proto.CreateSubAgentRequest_App_OWNER.Enum(), + Url: ptr.Ref("http://localhost:8082"), + }, + { + Slug: "organization-app", + Share: proto.CreateSubAgentRequest_App_ORGANIZATION.Enum(), + Url: ptr.Ref("http://localhost:8083"), + }, + }, + expectedStoredApps: []expectedApp{ + { + slugSuffix: "-authenticated-app", + sharingLevel: database.AppSharingLevelOrganization, + }, + { + slugSuffix: "-organization-app", + sharingLevel: database.AppSharingLevelOrganization, + }, + { + slugSuffix: "-owner-app", + sharingLevel: database.AppSharingLevelOwner, + }, + { + slugSuffix: "-public-app", + sharingLevel: database.AppSharingLevelOrganization, + }, + }, + }, + { + name: "OwnerClampsOrganizationAuthenticatedAndPublic", + maxPortShareLevel: database.AppSharingLevelOwner, + apps: []*proto.CreateSubAgentRequest_App{ + { + Slug: "authenticated-app", + Share: proto.CreateSubAgentRequest_App_AUTHENTICATED.Enum(), + Url: ptr.Ref("http://localhost:8080"), + }, + { + Slug: "public-app", + Share: proto.CreateSubAgentRequest_App_PUBLIC.Enum(), + Url: ptr.Ref("http://localhost:8081"), + }, + { + Slug: "owner-app", + Share: proto.CreateSubAgentRequest_App_OWNER.Enum(), + Url: ptr.Ref("http://localhost:8082"), + }, + { + Slug: "organization-app", + Share: proto.CreateSubAgentRequest_App_ORGANIZATION.Enum(), + Url: ptr.Ref("http://localhost:8083"), + }, + }, + expectedStoredApps: []expectedApp{ + { + slugSuffix: "-authenticated-app", + sharingLevel: database.AppSharingLevelOwner, + }, + { + slugSuffix: "-organization-app", + sharingLevel: database.AppSharingLevelOwner, + }, + { + slugSuffix: "-owner-app", + sharingLevel: database.AppSharingLevelOwner, + }, + { + slugSuffix: "-public-app", + sharingLevel: database.AppSharingLevelOwner, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + ctx, api, upsertedApps := newMockSubAgentAPIWithMaxPortShareLevel(t, tt.maxPortShareLevel, len(tt.apps)) + resp, err := api.CreateSubAgent(ctx, &proto.CreateSubAgentRequest{ + Name: "child-agent", + Directory: "/workspaces/coder", + Architecture: "amd64", + OperatingSystem: "linux", + Apps: tt.apps, + }) + require.NoError(t, err) + require.NotNil(t, resp.Agent) + require.Empty(t, resp.AppCreationErrors) + require.Len(t, *upsertedApps, len(tt.expectedStoredApps)) + + slices.SortFunc(*upsertedApps, func(a, b database.UpsertWorkspaceAppParams) int { + return cmp.Compare(appSlugSuffix(a.Slug), appSlugSuffix(b.Slug)) + }) + slices.SortFunc(tt.expectedStoredApps, func(a, b expectedApp) int { + return cmp.Compare(a.slugSuffix, b.slugSuffix) + }) + + for i, expectedApp := range tt.expectedStoredApps { + require.Equal(t, expectedApp.slugSuffix, appSlugSuffix((*upsertedApps)[i].Slug)) + require.Equal(t, expectedApp.sharingLevel, (*upsertedApps)[i].SharingLevel) + } + }) + } +} + +func appSlugSuffix(slug string) string { + _, suffix, ok := strings.Cut(slug, "-") + if !ok { + return slug + } + return "-" + suffix +} + +func newMockSubAgentAPIWithMaxPortShareLevel( + t *testing.T, + maxPortShareLevel database.AppSharingLevel, + appCount int, +) (context.Context, *agentapi.SubAgentAPI, *[]database.UpsertWorkspaceAppParams) { + t.Helper() + + ctx := testutil.Context(t, testutil.WaitShort) + log := testutil.Logger(t) + clock := quartz.NewMock(t) + ownerID := uuid.New() + organizationID := uuid.New() + templateID := uuid.New() + parentAgent := database.WorkspaceAgent{ + ID: uuid.New(), + ResourceID: uuid.New(), + } + workspace := database.Workspace{ + ID: uuid.New(), + OwnerID: ownerID, + OrganizationID: organizationID, + TemplateID: templateID, + } + template := database.Template{ + ID: templateID, + MaxPortSharingLevel: maxPortShareLevel, + } + upsertedApps := []database.UpsertWorkspaceAppParams{} + + db := dbmock.NewMockStore(gomock.NewController(t)) + db.EXPECT().GetWorkspaceByAgentID(gomock.Any(), parentAgent.ID).Return(workspace, nil) + db.EXPECT().GetTemplateByID(gomock.Any(), templateID).Return(template, nil) + db.EXPECT().InsertWorkspaceAgent(gomock.Any(), gomock.Any()).DoAndReturn( + func(_ context.Context, params database.InsertWorkspaceAgentParams) (database.WorkspaceAgent, error) { + require.True(t, params.ParentID.Valid) + require.Equal(t, parentAgent.ID, params.ParentID.UUID) + + return database.WorkspaceAgent{ + ID: params.ID, + Name: params.Name, + AuthToken: params.AuthToken, + }, nil + }, + ) + db.EXPECT().UpsertWorkspaceApp(gomock.Any(), gomock.Any()).DoAndReturn( + func(_ context.Context, params database.UpsertWorkspaceAppParams) (database.WorkspaceApp, error) { + upsertedApps = append(upsertedApps, params) + return database.WorkspaceApp{ + ID: params.ID, + AgentID: params.AgentID, + Slug: params.Slug, + SharingLevel: params.SharingLevel, + }, nil + }, + ).Times(appCount) + + portSharer := &atomic.Pointer[agplportsharing.PortSharer]{} + var ps agplportsharing.PortSharer = entportsharing.NewEnterprisePortSharer() + portSharer.Store(&ps) + api := &agentapi.SubAgentAPI{ + OwnerID: ownerID, + OrganizationID: organizationID, + AgentFn: func(context.Context) (database.WorkspaceAgent, error) { + return parentAgent, nil + }, + Log: log, + Clock: clock, + Database: db, + PortSharer: portSharer, + } + + return ctx, api, &upsertedApps +} + +func TestDevcontainerSubAgentAppShareClampedByEnterpriseTemplateMaxPortShareLevel(t *testing.T) { + t.Parallel() + + ctx, db, client := newDevcontainerSubAgentClientWithMaxPortShareLevel(t, database.AppSharingLevelAuthenticated) + subAgent, err := client.Create(ctx, agentcontainers.SubAgent{ + Name: "devcontainer", + Directory: "/workspaces/coder", + Architecture: "amd64", + OperatingSystem: "linux", + Apps: []agentcontainers.SubAgentApp{ + { + Slug: "public-app", + URL: "http://localhost:8080", + Share: codersdk.WorkspaceAppSharingLevelPublic, + }, + { + Slug: "owner-app", + URL: "http://localhost:8081", + Share: codersdk.WorkspaceAppSharingLevelOwner, + }, + }, + }) + require.NoError(t, err) + require.NotEqual(t, uuid.Nil, subAgent.ID) + + apps, err := db.GetWorkspaceAppsByAgentID(ctx, subAgent.ID) + require.NoError(t, err) + require.Len(t, apps, 2) + slices.SortFunc(apps, func(a, b database.WorkspaceApp) int { + return cmp.Compare(appSlugSuffix(a.Slug), appSlugSuffix(b.Slug)) + }) + require.Equal(t, "-owner-app", appSlugSuffix(apps[0].Slug)) + require.Equal(t, database.AppSharingLevelOwner, apps[0].SharingLevel) + require.Equal(t, "-public-app", appSlugSuffix(apps[1].Slug)) + require.Equal(t, database.AppSharingLevelAuthenticated, apps[1].SharingLevel) +} + +func TestDevcontainerCoderAppShareClampedWithGroupRestrictedEnterpriseTemplateACL(t *testing.T) { + t.Parallel() + + ctx, db, client := newDevcontainerSubAgentClientWithMaxPortShareLevel(t, + database.AppSharingLevelAuthenticated, + withGroupRestrictedTemplateACL, + ) + subAgent, err := client.Create(ctx, agentcontainers.SubAgent{ + Name: "devcontainer", + Directory: "/workspaces/coder", + Architecture: "amd64", + OperatingSystem: "linux", + Apps: []agentcontainers.SubAgentApp{ + { + Slug: "public-app", + URL: "http://localhost:8080", + Share: codersdk.WorkspaceAppSharingLevelPublic, + }, + }, + }) + require.NoError(t, err) + + apps, err := db.GetWorkspaceAppsByAgentID(ctx, subAgent.ID) + require.NoError(t, err) + require.Len(t, apps, 1) + require.Equal(t, "-public-app", appSlugSuffix(apps[0].Slug)) + require.Equal(t, database.AppSharingLevelAuthenticated, apps[0].SharingLevel) +} + +type devcontainerSubAgentClientOption func(testing.TB, database.Store, database.Organization, database.User, *database.Template) + +func newDevcontainerSubAgentClientWithMaxPortShareLevel( + t *testing.T, + maxPortShareLevel database.AppSharingLevel, + options ...devcontainerSubAgentClientOption, +) (context.Context, database.Store, agentcontainers.SubAgentClient) { + t.Helper() + + ctx := testutil.Context(t, testutil.WaitShort) + log := testutil.Logger(t) + clock := quartz.NewMock(t) + + rawDB, _ := dbtestutil.NewDB(t) + org := dbgen.Organization(t, rawDB, database.Organization{}) + user := dbgen.User(t, rawDB, database.User{}) + template := dbgen.Template(t, rawDB, database.Template{ + OrganizationID: org.ID, + CreatedBy: user.ID, + MaxPortSharingLevel: maxPortShareLevel, + }) + for _, option := range options { + option(t, rawDB, org, user, &template) + } + templateVersion := dbgen.TemplateVersion(t, rawDB, database.TemplateVersion{ + TemplateID: uuid.NullUUID{Valid: true, UUID: template.ID}, + OrganizationID: org.ID, + CreatedBy: user.ID, + }) + workspace := dbgen.Workspace(t, rawDB, database.WorkspaceTable{ + OrganizationID: org.ID, + TemplateID: template.ID, + OwnerID: user.ID, + }) + job := dbgen.ProvisionerJob(t, rawDB, nil, database.ProvisionerJob{ + Type: database.ProvisionerJobTypeWorkspaceBuild, + OrganizationID: org.ID, + }) + build := dbgen.WorkspaceBuild(t, rawDB, database.WorkspaceBuild{ + JobID: job.ID, + WorkspaceID: workspace.ID, + TemplateVersionID: templateVersion.ID, + }) + resource := dbgen.WorkspaceResource(t, rawDB, database.WorkspaceResource{ + JobID: build.JobID, + }) + parentAgent := dbgen.WorkspaceAgent(t, rawDB, database.WorkspaceAgent{ + ResourceID: resource.ID, + }) + + auth := rbac.NewStrictCachingAuthorizer(prometheus.NewRegistry()) + accessControlStore := &atomic.Pointer[agpldbauthz.AccessControlStore]{} + var acs agpldbauthz.AccessControlStore = entdbauthz.EnterpriseTemplateAccessControlStore{} + accessControlStore.Store(&acs) + db := agpldbauthz.New(rawDB, auth, log, accessControlStore) + portSharer := &atomic.Pointer[agplportsharing.PortSharer]{} + var ps agplportsharing.PortSharer = entportsharing.NewEnterprisePortSharer() + portSharer.Store(&ps) + api := &agentapi.SubAgentAPI{ + OwnerID: user.ID, + OrganizationID: org.ID, + AgentFn: func(context.Context) (database.WorkspaceAgent, error) { + return parentAgent, nil + }, + Log: log, + Clock: clock, + Database: db, + PortSharer: portSharer, + } + + client := agentcontainers.NewSubAgentClientFromAPI(log, devcontainerSubAgentDRPCClient{api: api}) + return ctx, rawDB, client +} + +func withGroupRestrictedTemplateACL(t testing.TB, db database.Store, org database.Organization, user database.User, template *database.Template) { + t.Helper() + + group := dbgen.Group(t, db, database.Group{OrganizationID: org.ID}) + dbgen.GroupMember(t, db, database.GroupMemberTable{ + GroupID: group.ID, + UserID: user.ID, + }) + template.GroupACL = database.TemplateACL{ + group.ID.String(): db2sdk.TemplateRoleActions(codersdk.TemplateRoleUse), + } + template.UserACL = database.TemplateACL{} + require.NoError(t, db.UpdateTemplateACLByID(context.Background(), database.UpdateTemplateACLByIDParams{ + ID: template.ID, + GroupACL: template.GroupACL, + UserACL: template.UserACL, + })) +} + +type devcontainerSubAgentDRPCClient struct { + proto.DRPCAgentClient26 + api *agentapi.SubAgentAPI +} + +func (c devcontainerSubAgentDRPCClient) CreateSubAgent(ctx context.Context, req *proto.CreateSubAgentRequest) (*proto.CreateSubAgentResponse, error) { + return c.api.CreateSubAgent(ctx, req) +} + +func (c devcontainerSubAgentDRPCClient) DeleteSubAgent(ctx context.Context, req *proto.DeleteSubAgentRequest) (*proto.DeleteSubAgentResponse, error) { + return c.api.DeleteSubAgent(ctx, req) +} + +func (c devcontainerSubAgentDRPCClient) ListSubAgents(ctx context.Context, req *proto.ListSubAgentsRequest) (*proto.ListSubAgentsResponse, error) { + return c.api.ListSubAgents(ctx, req) +} diff --git a/enterprise/coderd/userauth_test.go b/enterprise/coderd/userauth_test.go index fd4706a25e5..d5f4556ae36 100644 --- a/enterprise/coderd/userauth_test.go +++ b/enterprise/coderd/userauth_test.go @@ -173,7 +173,7 @@ func TestUserOIDC(t *testing.T) { fields, err := runner.AdminClient.GetAvailableIDPSyncFields(ctx) require.NoError(t, err) require.ElementsMatch(t, []string{ - "sub", "aud", "exp", "iss", // Always included from jwt + "sub", "aud", "exp", "iss", "email_verified", // Always included from jwt "email", "organization", }, fields) diff --git a/enterprise/coderd/workspaceproxy.go b/enterprise/coderd/workspaceproxy.go index 4f3ce120566..872b232b8de 100644 --- a/enterprise/coderd/workspaceproxy.go +++ b/enterprise/coderd/workspaceproxy.go @@ -604,6 +604,25 @@ func (api *API) workspaceProxyRegister(rw http.ResponseWriter, r *http.Request) return } + // Load the mesh key directly from the database. We don't retrieve the mesh + // key from the built-in DERP server because it may not be enabled. + // + // The mesh key is always generated at startup by an enterprise coderd + // server. + var meshKey string + if req.DerpEnabled { + var err error + meshKey, err = api.Database.GetDERPMeshKey(ctx) + if err != nil { + httpapi.InternalServerError(rw, xerrors.Errorf("get DERP mesh key: %w", err)) + return + } + if meshKey == "" { + httpapi.InternalServerError(rw, xerrors.New("mesh key is empty")) + return + } + } + startingRegionID, _ := getProxyDERPStartingRegionID(api.Options.BaseDERPMap) // #nosec G115 - Safe conversion as DERP region IDs are small integers expected to be within int32 range regionID := int32(startingRegionID) + proxy.RegionID @@ -710,7 +729,7 @@ func (api *API) workspaceProxyRegister(rw http.ResponseWriter, r *http.Request) } httpapi.Write(ctx, rw, http.StatusCreated, wsproxysdk.RegisterWorkspaceProxyResponse{ - DERPMeshKey: api.DERPServer.MeshKey(), + DERPMeshKey: meshKey, DERPRegionID: regionID, DERPMap: api.AGPL.DERPMap(), DERPForceWebSockets: api.DeploymentValues.DERP.Config.ForceWebSockets.Value(), diff --git a/enterprise/coderd/workspaceproxy_test.go b/enterprise/coderd/workspaceproxy_test.go index d4be30d8229..49849d600c5 100644 --- a/enterprise/coderd/workspaceproxy_test.go +++ b/enterprise/coderd/workspaceproxy_test.go @@ -2,12 +2,15 @@ package coderd_test import ( "database/sql" + "encoding/json" "fmt" "net" "net/http" "net/http/httptest" "net/http/httputil" "net/url" + "os" + "path/filepath" "runtime" "testing" "time" @@ -16,6 +19,7 @@ import ( "github.com/sqlc-dev/pqtype" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "tailscale.com/tailcfg" "cdr.dev/slog/sloggers/slogtest" "github.com/coder/coder/v2/agent/agenttest" @@ -34,6 +38,7 @@ import ( "github.com/coder/coder/v2/enterprise/wsproxy/wsproxysdk" "github.com/coder/coder/v2/provisioner/echo" "github.com/coder/coder/v2/testutil" + "github.com/coder/serpent" ) func TestRegions(t *testing.T) { @@ -278,10 +283,11 @@ func TestWorkspaceProxyCRUD(t *testing.T) { func TestProxyRegisterDeregister(t *testing.T) { t.Parallel() - setup := func(t *testing.T) (*codersdk.Client, database.Store) { + setupWithDeploymentValues := func(t *testing.T, dv *codersdk.DeploymentValues) (*codersdk.Client, database.Store) { db, pubsub := dbtestutil.NewDB(t) client, _ := coderdenttest.New(t, &coderdenttest.Options{ Options: &coderdtest.Options{ + DeploymentValues: dv, Database: db, Pubsub: pubsub, IncludeProvisionerDaemon: true, @@ -297,6 +303,11 @@ func TestProxyRegisterDeregister(t *testing.T) { return client, db } + setup := func(t *testing.T) (*codersdk.Client, database.Store) { + dv := coderdtest.DeploymentValues(t) + return setupWithDeploymentValues(t, dv) + } + t.Run("OK", func(t *testing.T) { t.Parallel() @@ -363,7 +374,7 @@ func TestProxyRegisterDeregister(t *testing.T) { req = wsproxysdk.RegisterWorkspaceProxyRequest{ AccessURL: "https://cool.proxy.coder.test", WildcardHostname: "*.cool.proxy.coder.test", - DerpEnabled: false, + DerpEnabled: true, ReplicaID: req.ReplicaID, ReplicaHostname: "venus", ReplicaError: "error", @@ -608,6 +619,99 @@ func TestProxyRegisterDeregister(t *testing.T) { require.True(t, ok, "expected to register replica %d", i) } }) + + t.Run("RegisterWithDisabledBuiltInDERP/DerpEnabled", func(t *testing.T) { + t.Parallel() + + // Create a DERP map file. Currently, Coder refuses to start if there + // are zero DERP regions. + // TODO: ideally coder can start without any DERP servers if the + // customer is going to be using DERPs via proxies. We could make it + // a configuration value to allow an empty DERP map on startup or + // something. + tmpDir := t.TempDir() + derpPath := filepath.Join(tmpDir, "derp.json") + content, err := json.Marshal(&tailcfg.DERPMap{ + Regions: map[int]*tailcfg.DERPRegion{ + 1: { + Nodes: []*tailcfg.DERPNode{{}}, + }, + }, + }) + require.NoError(t, err) + require.NoError(t, os.WriteFile(derpPath, content, 0o600)) + + dv := coderdtest.DeploymentValues(t) + dv.DERP.Server.Enable = false // disable built-in DERP server + dv.DERP.Config.Path = serpent.String(derpPath) + client, _ := setupWithDeploymentValues(t, dv) + ctx := testutil.Context(t, testutil.WaitLong) + + createRes, err := client.CreateWorkspaceProxy(ctx, codersdk.CreateWorkspaceProxyRequest{ + Name: "proxy", + }) + require.NoError(t, err) + + proxyClient := wsproxysdk.New(client.URL, createRes.ProxyToken) + registerRes, err := proxyClient.RegisterWorkspaceProxy(ctx, wsproxysdk.RegisterWorkspaceProxyRequest{ + AccessURL: "https://proxy.coder.test", + WildcardHostname: "*.proxy.coder.test", + DerpEnabled: true, + ReplicaID: uuid.New(), + ReplicaHostname: "venus", + ReplicaError: "", + ReplicaRelayAddress: "http://127.0.0.1:8080", + Version: buildinfo.Version(), + }) + require.NoError(t, err) + // Should still be able to retrieve the DERP mesh key from the database, + // even though the built-in DERP server is disabled. + require.Equal(t, registerRes.DERPMeshKey, coderdtest.DefaultDERPMeshKey) + }) + + t.Run("RegisterWithDisabledBuiltInDERP/DerpEnabled", func(t *testing.T) { + t.Parallel() + + // Same as above. + tmpDir := t.TempDir() + derpPath := filepath.Join(tmpDir, "derp.json") + content, err := json.Marshal(&tailcfg.DERPMap{ + Regions: map[int]*tailcfg.DERPRegion{ + 1: { + Nodes: []*tailcfg.DERPNode{{}}, + }, + }, + }) + require.NoError(t, err) + require.NoError(t, os.WriteFile(derpPath, content, 0o600)) + + dv := coderdtest.DeploymentValues(t) + dv.DERP.Server.Enable = false // disable built-in DERP server + dv.DERP.Config.Path = serpent.String(derpPath) + client, _ := setupWithDeploymentValues(t, dv) + ctx := testutil.Context(t, testutil.WaitLong) + + createRes, err := client.CreateWorkspaceProxy(ctx, codersdk.CreateWorkspaceProxyRequest{ + Name: "proxy", + }) + require.NoError(t, err) + + proxyClient := wsproxysdk.New(client.URL, createRes.ProxyToken) + registerRes, err := proxyClient.RegisterWorkspaceProxy(ctx, wsproxysdk.RegisterWorkspaceProxyRequest{ + AccessURL: "https://proxy.coder.test", + WildcardHostname: "*.proxy.coder.test", + DerpEnabled: false, + ReplicaID: uuid.New(), + ReplicaHostname: "venus", + ReplicaError: "", + ReplicaRelayAddress: "http://127.0.0.1:8080", + Version: buildinfo.Version(), + }) + require.NoError(t, err) + // The server shouldn't bother querying or returning the DERP mesh key + // if the proxy's DERP server is disabled. + require.Empty(t, registerRes.DERPMeshKey) + }) } func TestIssueSignedAppToken(t *testing.T) { diff --git a/enterprise/coderd/workspaces_test.go b/enterprise/coderd/workspaces_test.go index 7cf9cd890b6..d89f9742524 100644 --- a/enterprise/coderd/workspaces_test.go +++ b/enterprise/coderd/workspaces_test.go @@ -1978,7 +1978,7 @@ func TestPrebuildsAutobuild(t *testing.T) { notificationsNoop, api.AGPL.BuildUsageChecker, ) - var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer(db) + var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer() api.AGPL.PrebuildsClaimer.Store(&claimer) // Setup user, template and template version with a preset with 1 prebuild instance @@ -2100,7 +2100,7 @@ func TestPrebuildsAutobuild(t *testing.T) { notificationsNoop, api.AGPL.BuildUsageChecker, ) - var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer(db) + var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer() api.AGPL.PrebuildsClaimer.Store(&claimer) // Setup user, template and template version with a preset with 1 prebuild instance @@ -2222,7 +2222,7 @@ func TestPrebuildsAutobuild(t *testing.T) { notificationsNoop, api.AGPL.BuildUsageChecker, ) - var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer(db) + var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer() api.AGPL.PrebuildsClaimer.Store(&claimer) // Setup user, template and template version with a preset with 1 prebuild instance @@ -2366,7 +2366,7 @@ func TestPrebuildsAutobuild(t *testing.T) { notificationsNoop, api.AGPL.BuildUsageChecker, ) - var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer(db) + var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer() api.AGPL.PrebuildsClaimer.Store(&claimer) // Setup user, template and template version with a preset with 1 prebuild instance @@ -2511,7 +2511,7 @@ func TestPrebuildsAutobuild(t *testing.T) { notificationsNoop, api.AGPL.BuildUsageChecker, ) - var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer(db) + var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer() api.AGPL.PrebuildsClaimer.Store(&claimer) // Setup user, template and template version with a preset with 1 prebuild instance @@ -2957,7 +2957,7 @@ func TestWorkspaceProvisionerdServerMetrics(t *testing.T) { notifications.NewNoopEnqueuer(), api.AGPL.BuildUsageChecker, ) - var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer(db) + var claimer agplprebuilds.Claimer = prebuilds.NewEnterpriseClaimer() api.AGPL.PrebuildsClaimer.Store(&claimer) organizationName, err := client.Organization(ctx, owner.OrganizationID) @@ -4477,3 +4477,124 @@ func TestDeleteWorkspaceACL(t *testing.T) { require.Equal(t, acl.Groups[0].ID, group.ID) }) } + +// Unfortunately this test is incompatible with 2.29, so it's commented out in +// this backport PR. +/* +func TestWorkspaceAITask(t *testing.T) { + t.Parallel() + + usage := coderdtest.NewUsageInserter() + owner, _, first := coderdenttest.NewWithDatabase(t, &coderdenttest.Options{ + Options: &coderdtest.Options{ + UsageInserter: usage, + IncludeProvisionerDaemon: true, + }, + LicenseOptions: (&coderdenttest.LicenseOptions{ + Features: license.Features{ + codersdk.FeatureTemplateRBAC: 1, + }, + }).ManagedAgentLimit(10), + }) + + client, _ := coderdtest.CreateAnotherUser(t, owner, first.OrganizationID, + rbac.RoleTemplateAdmin(), rbac.RoleUserAdmin()) + + graphWithTask := []*proto.Response{{ + Type: &proto.Response_Graph{ + Graph: &proto.GraphComplete{ + Error: "", + Timings: nil, + Resources: nil, + Parameters: nil, + ExternalAuthProviders: nil, + Presets: nil, + HasAiTasks: true, + AiTasks: []*proto.AITask{ + { + Id: "test", + SidebarApp: nil, + AppId: "test", + }, + }, + HasExternalAgents: false, + }, + }, + }} + planWithTask := []*proto.Response{{ + Type: &proto.Response_Plan{ + Plan: &proto.PlanComplete{ + Plan: []byte("{}"), + AiTaskCount: 1, + }, + }, + }} + + t.Run("CreateWorkspaceWithTaskNormally", func(t *testing.T) { + // Creating a workspace that has agentic tasks, but is not launced via task + // should not count towards the usage. + t.Cleanup(usage.Reset) + version := coderdtest.CreateTemplateVersion(t, client, first.OrganizationID, &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, + ProvisionPlan: planWithTask, + ProvisionApply: echo.ApplyComplete, + ProvisionGraph: graphWithTask, + }) + _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, first.OrganizationID, version.ID) + wrk := coderdtest.CreateWorkspace(t, client, template.ID) + build := coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, wrk.LatestBuild.ID) + require.Equal(t, codersdk.WorkspaceStatusRunning, build.Status) + require.Len(t, usage.GetEvents(), 0) + }) + + t.Run("CreateTaskWorkspace", func(t *testing.T) { + ctx := testutil.Context(t, testutil.WaitMedium) + t.Cleanup(usage.Reset) + version := coderdtest.CreateTemplateVersion(t, client, first.OrganizationID, &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionInit: echo.InitComplete, + ProvisionPlan: planWithTask, + ProvisionApply: echo.ApplyComplete, + ProvisionGraph: graphWithTask, + }) + _ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, first.OrganizationID, version.ID) + + task, err := client.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{ + TemplateVersionID: template.ActiveVersionID, + Name: "istask", + }) + require.NoError(t, err) + + wrk, err := client.Workspace(ctx, task.WorkspaceID.UUID) + require.NoError(t, err) + + build := coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, wrk.LatestBuild.ID) + require.Equal(t, codersdk.WorkspaceStatusRunning, build.Status) + require.Len(t, usage.GetEvents(), 1) + + usage.Reset() // Clean slate for easy checks + // Stopping the workspace should not create additional usage. + build, err = client.CreateWorkspaceBuild(ctx, wrk.ID, codersdk.CreateWorkspaceBuildRequest{ + TemplateVersionID: wrk.LatestBuild.TemplateVersionID, + Transition: codersdk.WorkspaceTransitionStop, + }) + require.NoError(t, err) + coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, build.ID) + require.Len(t, usage.GetEvents(), 0) + + usage.Reset() // Clean slate for easy checks + // Starting the workspace manually **WILL** create usage, as it's + // still a task workspace. + build, err = client.CreateWorkspaceBuild(ctx, wrk.ID, codersdk.CreateWorkspaceBuildRequest{ + TemplateVersionID: wrk.LatestBuild.TemplateVersionID, + Transition: codersdk.WorkspaceTransitionStart, + }) + require.NoError(t, err) + coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, build.ID) + require.Len(t, usage.GetEvents(), 1) + }) +} +*/ diff --git a/enterprise/dbcrypt/dbcrypt.go b/enterprise/dbcrypt/dbcrypt.go index e0ca58cc523..857455b96c4 100644 --- a/enterprise/dbcrypt/dbcrypt.go +++ b/enterprise/dbcrypt/dbcrypt.go @@ -262,6 +262,39 @@ func (db *dbCrypt) UpdateExternalAuthLink(ctx context.Context, params database.U } func (db *dbCrypt) UpdateExternalAuthLinkRefreshToken(ctx context.Context, params database.UpdateExternalAuthLinkRefreshTokenParams) error { + // The SQL query uses an optimistic lock: + // WHERE oauth_refresh_token = @old_oauth_refresh_token + // The caller supplies the plaintext old token (since dbcrypt + // decrypts on read), but the DB stores the encrypted value. + // Because AES-GCM is non-deterministic, we cannot simply + // re-encrypt the old token — the ciphertext would differ. + // Instead, read the current row from the inner (raw) store + // and use the actual encrypted value for the WHERE clause. + if params.OldOauthRefreshToken != "" && db.ciphers != nil && db.primaryCipherDigest != "" { + raw, err := db.Store.GetExternalAuthLink(ctx, database.GetExternalAuthLinkParams{ + ProviderID: params.ProviderID, + UserID: params.UserID, + }) + if err != nil { + return err + } + // Decrypt the stored token so we can compare with the + // caller-supplied plaintext. + decrypted := raw.OAuthRefreshToken + if err := db.decryptField(&decrypted, raw.OAuthRefreshTokenKeyID); err != nil { + return err + } + if decrypted != params.OldOauthRefreshToken { + // The token has changed since the caller read it; + // the optimistic lock should fail (no rows updated). + // Return nil to match the :exec semantics of the SQL + // query, which silently updates zero rows. + return nil + } + // Use the raw encrypted value so the WHERE clause matches. + params.OldOauthRefreshToken = raw.OAuthRefreshToken + } + // We would normally use a sql.NullString here, but sqlc does not want to make // a params struct with a nullable string. var digest sql.NullString diff --git a/enterprise/dbcrypt/dbcrypt_internal_test.go b/enterprise/dbcrypt/dbcrypt_internal_test.go index e73c3eee85c..fcf9eae2de4 100644 --- a/enterprise/dbcrypt/dbcrypt_internal_test.go +++ b/enterprise/dbcrypt/dbcrypt_internal_test.go @@ -108,6 +108,7 @@ func TestUserLinks(t *testing.T) { err := crypt.UpdateExternalAuthLinkRefreshToken(ctx, database.UpdateExternalAuthLinkRefreshTokenParams{ OAuthRefreshToken: "", OAuthRefreshTokenKeyID: link.OAuthRefreshTokenKeyID.String, + OldOauthRefreshToken: link.OAuthRefreshToken, UpdatedAt: dbtime.Now(), ProviderID: link.ProviderID, UserID: link.UserID, diff --git a/enterprise/tailnet/pgcoord_test.go b/enterprise/tailnet/pgcoord_test.go index eee64f75f4e..ee75721673c 100644 --- a/enterprise/tailnet/pgcoord_test.go +++ b/enterprise/tailnet/pgcoord_test.go @@ -120,7 +120,7 @@ func TestPGCoordinatorSingle_AgentInvalidIP(t *testing.T) { // The agent connection should be closed immediately after sending an invalid addr agent.AssertEventuallyResponsesClosed( - agpl.AuthorizationError{Wrapped: agpl.InvalidNodeAddressError{Addr: prefix.Addr().String()}}.Error()) + agpl.AuthorizationError{Wrapped: xerrors.Errorf("Addresses: %w", agpl.InvalidNodeAddressError{Addr: prefix.Addr().String()})}.Error()) assertEventuallyLost(ctx, t, store, agent.ID) } @@ -146,7 +146,37 @@ func TestPGCoordinatorSingle_AgentInvalidIPBits(t *testing.T) { // The agent connection should be closed immediately after sending an invalid addr agent.AssertEventuallyResponsesClosed( - agpl.AuthorizationError{Wrapped: agpl.InvalidAddressBitsError{Bits: 64}}.Error()) + agpl.AuthorizationError{Wrapped: xerrors.Errorf("Addresses: %w", agpl.InvalidAddressBitsError{Bits: 64})}.Error()) + assertEventuallyLost(ctx, t, store, agent.ID) +} + +func TestPGCoordinatorSingle_AgentInvalidAllowedIP(t *testing.T) { + t.Parallel() + + store, ps := dbtestutil.NewDB(t) + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitSuperLong) + defer cancel() + logger := testutil.Logger(t) + coordinator, err := tailnet.NewPGCoord(ctx, logger, ps, store) + require.NoError(t, err) + defer coordinator.Close() + + agent := agpltest.NewAgent(ctx, t, coordinator, "agent") + defer agent.Close(ctx) + // A valid self-address paired with an AllowedIP belonging to a different + // (victim) agent must be rejected. + victim := agpl.TailscaleServicePrefix.PrefixFromUUID(uuid.New()) + agent.UpdateNode(&proto.Node{ + Addresses: []string{ + agpl.TailscaleServicePrefix.PrefixFromUUID(agent.ID).String(), + }, + AllowedIps: []string{victim.String()}, + PreferredDerp: 10, + }) + + // The agent connection should be closed after sending an invalid AllowedIP. + agent.AssertEventuallyResponsesClosed( + agpl.AuthorizationError{Wrapped: xerrors.Errorf("AllowedIps: %w", agpl.InvalidNodeAddressError{Addr: victim.Addr().String()})}.Error()) assertEventuallyLost(ctx, t, store, agent.ID) } diff --git a/enterprise/wsproxy/wsproxy.go b/enterprise/wsproxy/wsproxy.go index ecd5df9cde1..3cbb64d4aaa 100644 --- a/enterprise/wsproxy/wsproxy.go +++ b/enterprise/wsproxy/wsproxy.go @@ -335,7 +335,9 @@ func New(ctx context.Context, opts *Options) (*Server, error) { tracing.Middleware(s.TracerProvider), httpmw.AttachRequestID, httpmw.ExtractRealIP(s.Options.RealIPConfig), - loggermw.Logger(s.Logger), + loggermw.Logger(s.Logger, func(r *http.Request) string { + return httpmw.EffectiveHost(s.Options.RealIPConfig, r) + }), prometheusMW, // HandleSubdomain is a middleware that handles all requests to the diff --git a/flake.nix b/flake.nix index 38eb53b68fa..23405fc911c 100644 --- a/flake.nix +++ b/flake.nix @@ -94,7 +94,7 @@ # 3. Update the sha256 and run again # 4. Nix will fail with the correct vendorHash # 5. Update the vendorHash - sqlc-custom = unstablePkgs.buildGo124Module { + sqlc-custom = unstablePkgs.buildGo125Module { pname = "sqlc"; version = "coder-fork-aab4e865a51df0c43e1839f81a9d349b41d14f05"; @@ -156,7 +156,7 @@ gnused gnugrep gnutar - unstablePkgs.go_1_24 + unstablePkgs.go_1_25 gofumpt go-migrate (pinnedPkgs.golangci-lint) @@ -224,7 +224,7 @@ # slim bundle into it's own derivation. buildFat = osArch: - unstablePkgs.buildGo124Module { + unstablePkgs.buildGo125Module { name = "coder-${osArch}"; # Updated with ./scripts/update-flake.sh`. # This should be updated whenever go.mod changes! diff --git a/go.mod b/go.mod index 44713ef25fc..9feb31f371c 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/coder/coder/v2 -go 1.24.10 +go 1.25.10 // Required until a v3 of chroma is created to lazily initialize all XML files. // None of our dependencies seem to use the registries anyways, so this @@ -36,7 +36,7 @@ replace github.com/tcnksm/go-httpstat => github.com/coder/go-httpstat v0.0.0-202 // There are a few minor changes we make to Tailscale that we're slowly upstreaming. Compare here: // https://github.com/tailscale/tailscale/compare/main...coder:tailscale:main -replace tailscale.com => github.com/coder/tailscale v1.1.1-0.20250829055706-6eafe0f9199e +replace tailscale.com => github.com/coder/tailscale v1.1.1-0.20260519043957-6f014ff9434f // This is replaced to include // 1. a fix for a data race: c.f. https://github.com/tailscale/wireguard-go/pull/25 @@ -66,7 +66,7 @@ replace github.com/charmbracelet/bubbletea => github.com/coder/bubbletea v1.2.2- // Trivy has some issues that we're floating patches for, and will hopefully // be upstreamed eventually. -replace github.com/aquasecurity/trivy => github.com/coder/trivy v0.0.0-20250807211036-0bb0acd620a8 +replace github.com/aquasecurity/trivy => github.com/coder/trivy v0.0.0-20260309164037-c413f5a2f511 // afero/tarfs has a bug that breaks our usage. A PR has been submitted upstream. // https://github.com/spf13/afero/pull/487 @@ -78,11 +78,10 @@ require ( github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/adrg/xdg v0.5.0 github.com/ammario/tlru v0.4.0 - github.com/andybalholm/brotli v1.2.0 github.com/aquasecurity/trivy-iac v0.8.0 github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 github.com/awalterschulze/gographviz v2.0.3+incompatible - github.com/aws/smithy-go v1.23.2 + github.com/aws/smithy-go v1.24.2 github.com/bramvdbogaerde/go-scp v1.5.0 github.com/briandowns/spinner v1.23.0 github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 @@ -106,7 +105,7 @@ require ( github.com/coder/wgtunnel v0.1.13-0.20240522110300-ade90dfb2da0 github.com/coreos/go-oidc/v3 v3.17.0 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf - github.com/creack/pty v1.1.21 + github.com/creack/pty v1.1.24 github.com/dave/dst v0.27.2 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/dblohm7/wingoes v0.0.0-20240820181039-f2b84150679e @@ -117,28 +116,27 @@ require ( github.com/fatih/structs v1.1.0 github.com/fatih/structtag v1.2.0 github.com/fergusstrange/embedded-postgres v1.32.0 - github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa github.com/gen2brain/beeep v0.11.1 github.com/gliderlabs/ssh v0.3.8 - github.com/go-chi/chi/v5 v5.2.2 + github.com/go-chi/chi/v5 v5.2.4 github.com/go-chi/cors v1.2.1 github.com/go-chi/httprate v0.15.0 - github.com/go-jose/go-jose/v4 v4.1.3 + github.com/go-jose/go-jose/v4 v4.1.4 github.com/go-logr/logr v1.4.3 github.com/go-playground/validator/v10 v10.28.0 github.com/gofrs/flock v0.13.0 github.com/gohugoio/hugo v0.152.2 github.com/golang-jwt/jwt/v4 v4.5.2 github.com/golang-migrate/migrate/v4 v4.19.0 - github.com/gomarkdown/markdown v0.0.0-20240930133441-72d49d9543d8 + github.com/gomarkdown/markdown v0.0.0-20260417124207-7d523f7318df github.com/google/go-cmp v0.7.0 github.com/google/go-github/v43 v43.0.1-0.20220414155304-00e42332e405 github.com/google/go-github/v61 v61.0.0 github.com/google/uuid v1.6.0 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-reap v0.0.0-20170704170343-bf58d8a43e7b - github.com/hashicorp/go-version v1.7.0 - github.com/hashicorp/hc-install v0.9.2 + github.com/hashicorp/go-version v1.9.0 + github.com/hashicorp/hc-install v0.9.4 github.com/hashicorp/terraform-config-inspect v0.0.0-20211115214459-90acf1ca460f github.com/hashicorp/terraform-json v0.27.2 github.com/hashicorp/yamux v0.1.2 @@ -154,11 +152,10 @@ require ( github.com/mattn/go-isatty v0.0.20 github.com/mitchellh/go-wordwrap v1.0.1 github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c - github.com/moby/moby v28.5.0+incompatible github.com/mocktools/go-smtp-mock/v2 v2.5.0 github.com/muesli/termenv v0.16.0 github.com/natefinch/atomic v1.0.1 - github.com/open-policy-agent/opa v1.6.0 + github.com/open-policy-agent/opa v1.11.0 github.com/ory/dockertest/v3 v3.12.0 github.com/pion/udp v0.1.4 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c @@ -168,15 +165,14 @@ require ( github.com/prometheus/client_golang v1.23.2 github.com/prometheus/client_model v0.6.2 github.com/prometheus/common v0.67.4 - github.com/quasilyte/go-ruleguard/dsl v0.3.22 + github.com/quasilyte/go-ruleguard/dsl v0.3.23 github.com/robfig/cron/v3 v3.0.1 - github.com/shirou/gopsutil/v4 v4.25.5 + github.com/shirou/gopsutil/v4 v4.25.6 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/spf13/afero v1.15.0 github.com/spf13/pflag v1.0.10 github.com/sqlc-dev/pqtype v0.3.0 github.com/stretchr/testify v1.11.1 - github.com/swaggo/http-swagger/v2 v2.0.1 github.com/swaggo/swag v1.16.2 github.com/tidwall/gjson v1.18.0 github.com/u-root/u-root v0.14.0 @@ -184,31 +180,30 @@ require ( github.com/valyala/fasthttp v1.68.0 github.com/wagslane/go-password-validator v0.3.0 github.com/zclconf/go-cty-yaml v1.1.0 - go.mozilla.org/pkcs7 v0.9.0 go.nhat.io/otelsql v0.16.0 - go.opentelemetry.io/otel v1.38.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 - go.opentelemetry.io/otel/sdk v1.38.0 - go.opentelemetry.io/otel/trace v1.38.0 + go.opentelemetry.io/otel v1.43.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 + go.opentelemetry.io/otel/sdk v1.43.0 + go.opentelemetry.io/otel/trace v1.43.0 go.uber.org/atomic v1.11.0 go.uber.org/goleak v1.3.1-0.20240429205332-517bace7cc29 go.uber.org/mock v0.6.0 go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516 - golang.org/x/crypto v0.45.0 - golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 - golang.org/x/mod v0.30.0 - golang.org/x/net v0.47.0 - golang.org/x/oauth2 v0.33.0 - golang.org/x/sync v0.18.0 - golang.org/x/sys v0.38.0 - golang.org/x/term v0.37.0 - golang.org/x/text v0.31.0 - golang.org/x/tools v0.39.0 + golang.org/x/crypto v0.52.0 + golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f + golang.org/x/mod v0.35.0 + golang.org/x/net v0.55.0 + golang.org/x/oauth2 v0.34.0 + golang.org/x/sync v0.20.0 + golang.org/x/sys v0.45.0 + golang.org/x/term v0.43.0 + golang.org/x/text v0.37.0 + golang.org/x/tools v0.44.0 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da - google.golang.org/api v0.256.0 - google.golang.org/grpc v1.77.0 - google.golang.org/protobuf v1.36.10 + google.golang.org/api v0.260.0 + google.golang.org/grpc v1.79.3 + google.golang.org/protobuf v1.36.11 gopkg.in/DataDog/dd-trace-go.v1 v1.74.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 @@ -219,12 +214,12 @@ require ( ) require ( - cloud.google.com/go/auth v0.17.0 // indirect + cloud.google.com/go/auth v0.18.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect - cloud.google.com/go/logging v1.13.0 // indirect - cloud.google.com/go/longrunning v0.6.7 // indirect - dario.cat/mergo v1.0.1 // indirect - filippo.io/edwards25519 v1.1.0 // indirect + cloud.google.com/go/logging v1.13.1 // indirect + cloud.google.com/go/longrunning v0.7.0 // indirect + dario.cat/mergo v1.0.2 // indirect + filippo.io/edwards25519 v1.1.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/DataDog/appsec-internal-go v1.11.2 // indirect github.com/DataDog/datadog-agent/pkg/obfuscate v0.64.2 // indirect @@ -244,7 +239,7 @@ require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect - github.com/ProtonMail/go-crypto v1.3.0 // indirect + github.com/ProtonMail/go-crypto v1.4.1 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/agnivade/levenshtein v1.2.1 // indirect github.com/akutz/memconn v0.1.0 // indirect @@ -255,20 +250,20 @@ require ( github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c // indirect github.com/atotto/clipboard v0.1.4 // indirect - github.com/aws/aws-sdk-go-v2 v1.40.0 - github.com/aws/aws-sdk-go-v2/config v1.32.1 - github.com/aws/aws-sdk-go-v2/credentials v1.19.1 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.5 + github.com/aws/aws-sdk-go-v2/config v1.32.5 + github.com/aws/aws-sdk-go-v2/credentials v1.19.5 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.2 - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect github.com/aws/aws-sdk-go-v2/service/ssm v1.60.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.9 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -280,13 +275,12 @@ require ( github.com/chromedp/sysutil v1.1.0 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect - github.com/cloudflare/circl v1.6.1 // indirect + github.com/cloudflare/circl v1.6.3 // indirect github.com/containerd/continuity v0.4.5 // indirect github.com/coreos/go-iptables v0.6.0 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect - github.com/docker/cli v28.3.2+incompatible // indirect - github.com/docker/docker v28.3.3+incompatible // indirect - github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/cli v29.2.0+incompatible // indirect + github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd // indirect github.com/dustin/go-humanize v1.0.1 @@ -295,16 +289,15 @@ require ( github.com/elastic/go-windows v1.0.0 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.10 // indirect github.com/go-chi/hostrouter v0.3.0 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/spec v0.21.0 // indirect - github.com/go-openapi/swag v0.23.1 // indirect + github.com/go-openapi/jsonpointer v0.22.4 // indirect + github.com/go-openapi/jsonreference v0.21.4 // indirect + github.com/go-openapi/spec v0.22.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect @@ -324,10 +317,10 @@ require ( github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect - github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.9 // indirect + github.com/googleapis/gax-go/v2 v2.16.0 // indirect github.com/gorilla/css v1.0.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cty v1.5.0 // indirect @@ -344,8 +337,6 @@ require ( github.com/hdevalence/ed25519consensus v0.1.0 // indirect github.com/illarion/gonotify v1.0.1 // indirect github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 // indirect - github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect github.com/jsimonetti/rtnetlink v1.3.5 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -373,7 +364,7 @@ require ( github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect @@ -391,17 +382,16 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/prometheus/procfs v0.16.1 // indirect - github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/prometheus/procfs v0.17.0 // indirect + github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect github.com/riandyrn/otelchi v0.5.1 // indirect github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b // indirect - github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.10.0 // indirect + github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/cast v1.10.0 // indirect - github.com/swaggo/files/v2 v2.0.0 // indirect github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d // indirect github.com/tailscale/golang-x-crypto v0.0.0-20230713185742-f0b76a10a08e // indirect @@ -409,7 +399,7 @@ require ( github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect github.com/tailscale/wireguard-go v0.0.0-20231121184858-cc193a0b3272 - github.com/tchap/go-patricia/v2 v2.3.2 // indirect + github.com/tchap/go-patricia/v2 v2.3.3 // indirect github.com/tcnksm/go-httpstat v0.2.0 // indirect github.com/tdewolff/parse/v2 v2.8.5-0.20251020133559-0efcf90bef1a // indirect github.com/tidwall/match v1.2.0 // indirect @@ -429,7 +419,7 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/yashtewari/glob-intersection v0.2.0 // indirect - github.com/yuin/goldmark v1.7.13 // indirect + github.com/yuin/goldmark v1.7.17 // indirect github.com/yuin/goldmark-emoji v1.0.6 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zclconf/go-cty v1.17.0 @@ -440,24 +430,24 @@ require ( go.opentelemetry.io/collector/pdata/pprofile v0.121.0 // indirect go.opentelemetry.io/collector/semconv v0.123.0 // indirect go.opentelemetry.io/contrib v1.19.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect - go.opentelemetry.io/otel/metric v1.38.0 // indirect - go.opentelemetry.io/proto/otlp v1.7.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.27.1 // indirect go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect golang.org/x/time v0.14.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20250715232539-7130f93afb79 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect + google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect gopkg.in/ini.v1 v1.67.0 // indirect howett.net/plist v1.0.0 // indirect kernel.org/pub/linux/libs/security/libcap/psx v1.2.73 // indirect - sigs.k8s.io/yaml v1.5.0 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) require github.com/coder/clistat v1.1.2 @@ -473,66 +463,94 @@ require ( ) require ( + github.com/andybalholm/brotli v1.2.0 github.com/anthropics/anthropic-sdk-go v1.18.0 github.com/brianvoe/gofakeit/v7 v7.9.0 github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225 github.com/coder/aibridge v0.2.0 github.com/coder/aisdk-go v0.0.9 - github.com/coder/boundary v1.0.1-0.20250925154134-55a44f2a7945 - github.com/coder/preview v1.0.4 + github.com/coder/boundary v0.0.1-alpha + github.com/coder/preview v1.0.8 github.com/danieljoos/wincred v1.2.3 github.com/dgraph-io/ristretto/v2 v2.3.0 github.com/fsnotify/fsnotify v1.9.0 - github.com/go-git/go-git/v5 v5.16.2 + github.com/go-git/go-git/v5 v5.19.1 github.com/icholy/replace v0.6.0 github.com/mark3labs/mcp-go v0.38.0 + github.com/smallstep/pkcs7 v0.2.1 + github.com/swaggo/http-swagger/v2 v2.0.2 gonum.org/v1/gonum v0.16.0 ) require ( - cel.dev/expr v0.24.0 // indirect - cloud.google.com/go v0.121.4 // indirect - cloud.google.com/go/iam v1.5.2 // indirect - cloud.google.com/go/monitoring v1.24.2 // indirect - cloud.google.com/go/storage v1.55.0 // indirect + cel.dev/expr v0.25.1 // indirect + cloud.google.com/go v0.121.6 // indirect + cloud.google.com/go/iam v1.5.3 // indirect + cloud.google.com/go/monitoring v1.24.3 // indirect + cloud.google.com/go/storage v1.57.1 // indirect git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.64.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.64.2 // indirect github.com/DataDog/dd-trace-go/v2 v2.0.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect - github.com/Masterminds/semver/v3 v3.3.1 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/alecthomas/chroma v0.10.0 // indirect github.com/aquasecurity/go-version v0.0.1 // indirect github.com/aquasecurity/iamgo v0.0.10 // indirect github.com/aquasecurity/jfather v0.0.8 // indirect github.com/aquasecurity/trivy v0.61.1-0.20250407075540-f1329c7ea1aa // indirect - github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169 // indirect - github.com/aws/aws-sdk-go v1.55.7 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 // indirect + github.com/aquasecurity/trivy-checks v1.12.2-0.20251219190323-79d27547baf5 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect - github.com/buger/jsonparser v1.1.1 // indirect - github.com/cenkalti/backoff/v5 v5.0.2 // indirect + github.com/buger/jsonparser v1.1.2 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect - github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f // indirect - github.com/envoyproxy/go-control-plane/envoy v1.35.0 // indirect - github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect + github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect github.com/esiqveland/notify v0.13.3 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-billy/v5 v5.9.0 // indirect + github.com/go-openapi/swag/conv v0.25.4 // indirect + github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.4 // indirect + github.com/go-openapi/swag/loading v0.25.4 // indirect + github.com/go-openapi/swag/stringutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect github.com/go-sql-driver/mysql v1.9.3 // indirect - github.com/goccy/go-yaml v1.18.0 // indirect - github.com/google/go-containerregistry v0.20.6 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/goccy/go-yaml v1.19.0 // indirect + github.com/google/go-containerregistry v0.20.7 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect - github.com/hashicorp/go-getter v1.7.9 // indirect - github.com/hashicorp/go-safetemp v1.0.0 // indirect + github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65 // indirect + github.com/hashicorp/go-getter v1.8.3 // indirect github.com/invopop/jsonschema v0.13.0 // indirect github.com/jackmordaunt/icns/v3 v3.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/lestrrat-go/blackmagic v1.0.4 // indirect + github.com/lestrrat-go/dsig v1.0.0 // indirect + github.com/lestrrat-go/dsig-secp256k1 v1.0.0 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/httprc/v3 v3.0.1 // indirect + github.com/lestrrat-go/jwx/v3 v3.0.12 // indirect + github.com/lestrrat-go/option v1.0.1 // indirect + github.com/lestrrat-go/option/v2 v2.0.0 // indirect + github.com/moby/moby/api v1.54.0 // indirect + github.com/moby/moby/client v0.3.0 // indirect github.com/moby/sys/user v0.4.0 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/openai/openai-go v1.12.0 // indirect @@ -540,23 +558,27 @@ require ( github.com/package-url/packageurl-go v0.1.3 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect - github.com/samber/lo v1.51.0 // indirect + github.com/samber/lo v1.52.0 // indirect + github.com/segmentio/asm v1.2.1 // indirect github.com/sergeymakinen/go-bmp v1.0.0 // indirect github.com/sergeymakinen/go-ico v1.0.0-beta.0 // indirect github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect + github.com/swaggo/files/v2 v2.0.0 // indirect github.com/tidwall/sjson v1.2.5 // indirect github.com/tmaxmax/go-sse v0.11.0 // indirect github.com/ulikunitz/xz v0.5.15 // indirect - github.com/vektah/gqlparser/v2 v2.5.28 // indirect + github.com/valyala/fastjson v1.6.4 // indirect + github.com/vektah/gqlparser/v2 v2.5.31 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.38.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect google.golang.org/genai v1.12.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect + k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d // indirect ) diff --git a/go.sum b/go.sum index 2cdd8dcccfb..ebdfb1c6284 100644 --- a/go.sum +++ b/go.sum @@ -1,635 +1,40 @@ cdr.dev/slog v1.6.2-0.20250703074222-9df5e0a6c145 h1:Mk4axSLxKw3hjkf3PffBLQYta7nPVIWObuKCPDWgQLc= cdr.dev/slog v1.6.2-0.20250703074222-9df5e0a6c145/go.mod h1:NaoTA7KwopCrnaSb0JXTC0PTp/O/Y83Lndnq0OEV3ZQ= -cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= -cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go v0.121.4 h1:cVvUiY0sX0xwyxPwdSU2KsF9knOVmtRyAMt8xou0iTs= -cloud.google.com/go v0.121.4/go.mod h1:XEBchUiHFJbz4lKBZwYBDHV/rSyfFktk737TLDU089s= -cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= -cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= -cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= -cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= -cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= -cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= -cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= -cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= -cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= -cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= -cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= -cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= -cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= -cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= -cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= -cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= -cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= -cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= -cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= -cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= -cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= -cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= -cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= -cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= -cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= -cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= -cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= -cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= -cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= -cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= -cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= -cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= -cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= -cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= -cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= -cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= -cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= -cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= -cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= -cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= -cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= -cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= -cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= -cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= -cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= -cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= -cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= -cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= -cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= -cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= -cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= -cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= -cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4= -cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ= +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= +cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= +cloud.google.com/go/auth v0.18.0 h1:wnqy5hrv7p3k7cShwAU/Br3nzod7fxoqG+k0VZ+/Pk0= +cloud.google.com/go/auth v0.18.0/go.mod h1:wwkPM1AgE1f2u6dG443MiWoD8C3BtOywNsUMcUTVDRo= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= -cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= -cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= -cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= -cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= -cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= -cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= -cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= -cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= -cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= -cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= -cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= -cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= -cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= -cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= -cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= -cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= -cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= -cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= -cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= -cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= -cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= -cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= -cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= -cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= -cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= -cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= -cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= -cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= -cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= -cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= -cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= -cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= -cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= -cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= -cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= -cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= -cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= -cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= -cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= -cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= -cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= -cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= -cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= -cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= -cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= -cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= -cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= -cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= -cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= -cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= -cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= -cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= -cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= -cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= -cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= -cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= -cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= -cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= -cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= -cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= -cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= -cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= -cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= -cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= -cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= -cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= -cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= -cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= -cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= -cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= -cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= -cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= -cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= -cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= -cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= -cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= -cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= -cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= -cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= -cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= -cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= -cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= -cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= -cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= -cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= -cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= -cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= -cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= -cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= -cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= -cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= -cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= -cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= -cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= -cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= -cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= -cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= -cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= -cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= -cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= -cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= -cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= -cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= -cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= -cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= -cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= -cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= -cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= -cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= -cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= -cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= -cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= -cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= -cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= -cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= -cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= -cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= -cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= -cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= -cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= -cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= -cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= -cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= -cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= -cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= -cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= -cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= -cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= -cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= -cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= -cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= -cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= -cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= -cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= -cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= -cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= -cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= -cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= -cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= -cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= -cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= -cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= -cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= -cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= -cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= -cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= -cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= -cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= -cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= -cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= -cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= -cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= -cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= -cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= -cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= -cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= -cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= -cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= -cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= -cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= -cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= -cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= -cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= -cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= -cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= -cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= -cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= -cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= -cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= -cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= -cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= -cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= -cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= -cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= -cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= -cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= -cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= -cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= -cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= -cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= -cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= -cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= -cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= -cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= -cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= -cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= -cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= -cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= -cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= -cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= -cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= -cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= -cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= -cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= -cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= -cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= -cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= -cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= -cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= -cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= -cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= -cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= -cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= -cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= -cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= -cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= -cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= -cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= -cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= -cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= -cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= -cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= -cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= -cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= -cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= -cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= -cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= -cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= -cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= -cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= -cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= -cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= -cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= -cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= -cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= -cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= -cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= -cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= -cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= -cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= -cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= -cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= -cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= -cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= -cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= -cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= -cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= -cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= -cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= -cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= -cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= -cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= -cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= -cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= -cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= -cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= -cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= -cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= -cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= -cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= -cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= -cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= -cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= -cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= -cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= -cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= -cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= -cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= -cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= -cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= -cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= -cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= -cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= -cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= -cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= -cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= -cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= -cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= -cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= -cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= -cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= -cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= -cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= -cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= -cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= -cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= -cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= -cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= -cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= -cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= -cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= -cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= -cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= -cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= -cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= -cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= -cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= -cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= -cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= -cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= -cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= -cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= -cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= -cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= -cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= -cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= -cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= -cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= -cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= -cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= -cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= -cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= -cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= -cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= -cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= -cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= -cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= -cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= -cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= -cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= -cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= -cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/storage v1.55.0 h1:NESjdAToN9u1tmhVqhXCaCwYBuvEhZLLv0gBr+2znf0= -cloud.google.com/go/storage v1.55.0/go.mod h1:ztSmTTwzsdXe5syLVS0YsbFxXuvEmEyZj7v7zChEmuY= -cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= -cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= -cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= -cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= -cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= -cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= -cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= -cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= -cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= -cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= -cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= -cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= -cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= -cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= -cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= -cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= -cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= -cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= -cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= -cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= -cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= -cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= -cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= -cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= -cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= -cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= -cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= -cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= -cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= -cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= -cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= -cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= -cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= -cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= -cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= -cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= -cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= -cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= -cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= -cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= -cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= -cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= -cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= -cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= -cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= -cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= -cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= -cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= -cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= -cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= -cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= -cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= -cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= -cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= -cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= -cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= -cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= -dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= +cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= +cloud.google.com/go/logging v1.13.1 h1:O7LvmO0kGLaHY/gq8cV7T0dyp6zJhYAOtZPX4TF3QtY= +cloud.google.com/go/logging v1.13.1/go.mod h1:XAQkfkMBxQRjQek96WLPNze7vsOmay9H5PqfsNYDqvw= +cloud.google.com/go/longrunning v0.7.0 h1:FV0+SYF1RIj59gyoWDRi45GiYUMM3K1qO51qoboQT1E= +cloud.google.com/go/longrunning v0.7.0/go.mod h1:ySn2yXmjbK9Ba0zsQqunhDkYi0+9rlXIwnoAf+h+TPY= +cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE= +cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI= +cloud.google.com/go/storage v1.57.1 h1:gzao6odNJ7dR3XXYvAgPK+Iw4fVPPznEPPyNjbaVkq8= +cloud.google.com/go/storage v1.57.1/go.mod h1:329cwlpzALLgJuu8beyJ/uvQznDHpa2U5lGjWednkzg= +cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U= +cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw= +filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/mkcert v1.4.4 h1:8eVbbwfVlaqUM7OwuftKc2nuYOoTDQWqsoXmzoXZdbc= filippo.io/mkcert v1.4.4/go.mod h1:VyvOchVuAye3BoUsPUOOofKygVwLV2KQMVFJNRq+1dA= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~jackmordaunt/go-toast v1.1.2 h1:/yrfI55LRt1M7H1vkaw+NaH1+L1CDxrqDltwm5euVuE= git.sr.ht/~jackmordaunt/go-toast v1.1.2/go.mod h1:jA4OqHKTQ4AFBdwrSnwnskUIIS3HYzlJSgdzCKqfavo= -git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 h1:+tu3HOoMXB7RXEINRVIpxJCT+KdYiI7LAEAUrOw3dIU= github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZdiDllfyYH5l5OkAaZtk7VkWe89bPJFmnDBNHxg= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/DataDog/appsec-internal-go v1.11.2 h1:Q00pPMQzqMIw7jT2ObaORIxBzSly+deS0Ely9OZ/Bj0= @@ -670,29 +75,27 @@ github.com/DataDog/sketches-go v1.4.7 h1:eHs5/0i2Sdf20Zkj0udVFWuCrXGRFig2Dcfm5rt github.com/DataDog/sketches-go v1.4.7/go.mod h1:eAmQ/EBmtSO+nQp7IZMZVRPT4BQTmIc5RZQ+deGlTPM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0/go.mod h1:jUZ5LYlw40WMd07qxcQJD5M40aUxrfwqQX1g7zxYnrQ= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0 h1:lhhYARPUu3LmHysQ/igznQphfzynnqI3D75oUyw1HXk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.54.0/go.mod h1:l9rva3ApbBpEJxSNYnwT9N4CDLrWgtq3u8736C5hyJw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.54.0 h1:xfK3bbi6F2RDtaZFtUdKO3osOBIhNb+xTs8lFW6yx9o= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.54.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 h1:s0WlVbf9qpvkh1c/uDAPElam0WrL7fHRIidgZJ7UqZI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc= github.com/JohannesKaufmann/dom v0.2.0 h1:1bragmEb19K8lHAqgFgqCpiPCFEZMTXzOIEjuxkUfLQ= github.com/JohannesKaufmann/dom v0.2.0/go.mod h1:57iSUl5RKric4bUkgos4zu6Xt5LMHUnw3TF1l5CbGZo= github.com/JohannesKaufmann/html-to-markdown/v2 v2.4.0 h1:C0/TerKdQX9Y9pbYi1EsLr5LDNANsqunyI/btpyfCg8= github.com/JohannesKaufmann/html-to-markdown/v2 v2.4.0/go.mod h1:OLaKh+giepO8j7teevrNwiy/fwf8LXgoc9g7rwaE1jk= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= -github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= -github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= -github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= +github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM= +github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= github.com/SherClockHolmes/webpush-go v1.4.0 h1:ocnzNKWN23T9nvHi6IfyrQjkIc0oJWv1B1pULsf9i3s= github.com/SherClockHolmes/webpush-go v1.4.0/go.mod h1:XSq8pKX11vNV8MJEMwjrlTkxhAj1zKfxmyhdV7Pd6UA= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= @@ -703,10 +106,6 @@ github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7l github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A= github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw= github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU= @@ -721,17 +120,12 @@ github.com/ammario/tlru v0.4.0 h1:sJ80I0swN3KOX2YxC6w8FbCqpQucWdbb+J36C05FPuU= github.com/ammario/tlru v0.4.0/go.mod h1:aYzRFu0XLo4KavE9W8Lx7tzjkX+pAApz+NgcKYIFUBQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/anthropics/anthropic-sdk-go v1.18.0 h1:jfxRA7AqZoCm83nHO/OVQp8xuwjUKtBziEdMbfmofHU= github.com/anthropics/anthropic-sdk-go v1.18.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= -github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= -github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= @@ -743,8 +137,8 @@ github.com/aquasecurity/iamgo v0.0.10 h1:t/HG/MI1eSephztDc+Rzh/YfgEa+NqgYRSfr6pH github.com/aquasecurity/iamgo v0.0.10/go.mod h1:GI9IQJL2a+C+V2+i3vcwnNKuIJXZ+HAfqxZytwy+cPk= github.com/aquasecurity/jfather v0.0.8 h1:tUjPoLGdlkJU0qE7dSzd1MHk2nQFNPR0ZfF+6shaExE= github.com/aquasecurity/jfather v0.0.8/go.mod h1:Ag+L/KuR/f8vn8okUi8Wc1d7u8yOpi2QTaGX10h71oY= -github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169 h1:TckzIxUX7lZaU9f2lNxCN0noYYP8fzmSQf6a4JdV83w= -github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169/go.mod h1:nT69xgRcBD4NlHwTBpWMYirpK5/Zpl8M+XDOgmjMn2k= +github.com/aquasecurity/trivy-checks v1.12.2-0.20251219190323-79d27547baf5 h1:8HnXyjgCiJwVX1mTKeqdyizd7ZBmXMPL+BMQ5UZd0Nk= +github.com/aquasecurity/trivy-checks v1.12.2-0.20251219190323-79d27547baf5/go.mod h1:hBSA3ziBFwGENK6/PYNIKm6N24SFg0wsv1VXeqPG/3M= github.com/aquasecurity/trivy-iac v0.8.0 h1:NKFhk/BTwQ0jIh4t74V8+6UIGUvPlaxO9HPlSMQi3fo= github.com/aquasecurity/trivy-iac v0.8.0/go.mod h1:ARiMeNqcaVWOXJmp8hmtMnNm/Jd836IOmDBUW5r4KEk= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= @@ -759,43 +153,48 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= -github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE= -github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.40.0 h1:/WMUA0kjhZExjOQN2z3oLALDREea1A7TobfuiBrKlwc= -github.com/aws/aws-sdk-go-v2 v1.40.0/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 h1:12SpdwU8Djs+YGklkinSSlcrPyj3H4VifVsKf78KbwA= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11/go.mod h1:dd+Lkp6YmMryke+qxW/VnKyhMBDTYP41Q2Bb+6gNZgY= -github.com/aws/aws-sdk-go-v2/config v1.32.1 h1:iODUDLgk3q8/flEC7ymhmxjfoAnBDwEEYEVyKZ9mzjU= -github.com/aws/aws-sdk-go-v2/config v1.32.1/go.mod h1:xoAgo17AGrPpJBSLg81W+ikM0cpOZG8ad04T2r+d5P0= -github.com/aws/aws-sdk-go-v2/credentials v1.19.1 h1:JeW+EwmtTE0yXFK8SmklrFh/cGTTXsQJumgMZNlbxfM= -github.com/aws/aws-sdk-go-v2/credentials v1.19.1/go.mod h1:BOoXiStwTF+fT2XufhO0Efssbi1CNIO/ZXpZu87N0pw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 h1:WZVR5DbDgxzA0BJeudId89Kmgy6DIU4ORpxwsVHz0qA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14/go.mod h1:Dadl9QO0kHgbrH1GRqGiZdYtW5w+IXXaBNCHTIaheM4= +github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY= +github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI= +github.com/aws/aws-sdk-go-v2/config v1.32.5 h1:pz3duhAfUgnxbtVhIK39PGF/AHYyrzGEyRD9Og0QrE8= +github.com/aws/aws-sdk-go-v2/config v1.32.5/go.mod h1:xmDjzSUs/d0BB7ClzYPAZMmgQdrodNjPPhd6bGASwoE= +github.com/aws/aws-sdk-go-v2/credentials v1.19.5 h1:xMo63RlqP3ZZydpJDMBsH9uJ10hgHYfQFIk1cHDXrR4= +github.com/aws/aws-sdk-go-v2/credentials v1.19.5/go.mod h1:hhbH6oRcou+LpXfA/0vPElh/e0M3aFeOblE1sssAAEk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo= github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.2 h1:QbFjOdplTkOgviHNKyTW/TZpvIYhD6lqEc3tkIvqMoQ= github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.2/go.mod h1:d0pTYUeTv5/tPSlbPZZQSqssM158jZBs02jx2LDslM8= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 h1:PZHqQACxYb8mYgms4RZbhZG0a7dPW06xOjmaH0EJC/I= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14/go.mod h1:VymhrMJUWs69D8u0/lZ7jSB6WgaG/NqHi3gX0aYf6U0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 h1:bOS19y6zlJwagBfHxs0ESzr1XCOU2KXJCWcq3E2vfjY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14/go.mod h1:1ipeGBMAxZ0xcTm6y6paC2C/J6f6OO7LBODV9afuAyM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 h1:FIouAnCE46kyYqyhs0XEBDFFSREtdnr8HQuLPQPLCrY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14/go.mod h1:UTwDc5COa5+guonQU8qBikJo1ZJ4ln2r1MkF7Dqag1E= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.1 h1:BDgIUYGEo5TkayOWv/oBLPphWwNm/A91AebUjAu5L5g= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.1/go.mod h1:iS6EPmNeqCsGo+xQmXv0jIMjyYtQfnwg36zl2FwEouk= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13/go.mod h1:CEuVn5WqOMilYl+tbccq8+N2ieCy0gVn3OtRb0vBNNM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ= +github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 h1:HwxWTbTrIHm5qY+CAEur0s/figc3qwvLWsNkF4RPToo= +github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 h1:HpI7aMmJ+mm1wkSHIA2t5EaFFv5EFYXePW30p1EIrbQ= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.4/go.mod h1:C5RdGMYGlfM0gYq/tifqgn4EbyX99V15P2V3R+VHbQU= github.com/aws/aws-sdk-go-v2/service/ssm v1.60.1 h1:OwMzNDe5VVTXD4kGmeK/FtqAITiV8Mw4TCa8IyNO0as= github.com/aws/aws-sdk-go-v2/service/ssm v1.60.1/go.mod h1:IyVabkWrs8SNdOEZLyFFcW9bUltV4G6OQS0s6H20PHg= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.4 h1:U//SlnkE1wOQiIImxzdY5PXat4Wq+8rlfVEw4Y7J8as= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.4/go.mod h1:av+ArJpoYf3pgyrj6tcehSFW+y9/QvAY8kMooR9bZCw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.9 h1:LU8S9W/mPDAU9q0FjCLi0TrCheLMGwzbRpvUMwYspcA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.9/go.mod h1:/j67Z5XBVDx8nZVp9EuFM9/BS5dvBznbqILGuu73hug= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 h1:GdGmKtG+/Krag7VfyOXV17xjTCz0i9NT+JnqLTOI5nA= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.1/go.mod h1:6TxbXoDSgBQ225Qd8Q+MbxUxUh6TtNKwbRt/EPS9xso= -github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= -github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.7 h1:eYnlt6QxnFINKzwxP5/Ucs1vkG7VT3Iezmvfgc2waUw= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.7/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX1s+lFTg4+4DOy70= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= @@ -840,28 +239,21 @@ github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/ github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E= github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bramvdbogaerde/go-scp v1.5.0 h1:a9BinAjTfQh273eh7vd3qUgmBC+bx+3TRDtkZWmIpzM= github.com/bramvdbogaerde/go-scp v1.5.0/go.mod h1:on2aH5AxaFb2G0N5Vsdy6B0Ml7k9HuHSwfo1y0QzAbQ= github.com/brianvoe/gofakeit/v7 v7.9.0 h1:6NsaMy9D5ZKVwIZ1V8L//J2FrOF3546FcXDElWLx994= github.com/brianvoe/gofakeit/v7 v7.9.0/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= -github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= +github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk= +github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/bytecodealliance/wasmtime-go/v39 v39.0.1 h1:RibaT47yiyCRxMOj/l2cvL8cWiWBSqDXHyqsa9sGcCE= +github.com/bytecodealliance/wasmtime-go/v39 v39.0.1/go.mod h1:miR4NYIEBXeDNamZIzpskhJ0z/p8al+lwMWylQ/ZJb4= github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 h1:BjkPE3785EwPhhyuFkbINB+2a1xATwk8SNDWnJiD41g= github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5/go.mod h1:jtAfVaU/2cu1+wdSRPWE2c1N2qeAA3K4RH9pYgqwets= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= -github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= @@ -882,15 +274,12 @@ github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Y github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= -github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327 h1:UQ4AU+BGti3Sy/aLU8KVseYKNALcX9UXY6DfpwQ6J8E= github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k= github.com/chromedp/chromedp v0.14.1 h1:0uAbnxewy/Q+Bg7oafVePE/6EXEho9hnaC38f+TTENg= github.com/chromedp/chromedp v0.14.1/go.mod h1:rHzAv60xDE7VNy/MYtTUrYreSc0ujt2O1/C3bzctYBo= github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM= github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs= github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= @@ -899,39 +288,24 @@ github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyM github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= -github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= -github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= +github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= +github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225 h1:tRIViZ5JRmzdOEo5wUWngaGEFBG8OaE1o2GIHN5ujJ8= github.com/coder/agentapi-sdk-go v0.0.0-20250505131810-560d1d88d225/go.mod h1:rNLVpYgEVeu1Zk29K64z6Od8RBP9DwqCu9OfCzh8MR4= github.com/coder/aibridge v0.2.0 h1:kAWhHD6fsmDLH1WxIwXPu9Ineijj+lVniko45C003Vo= github.com/coder/aibridge v0.2.0/go.mod h1:2T0RSnIX1WTqFajzXsaNsoNe6mmNsNeCTxiHBWEsFnE= github.com/coder/aisdk-go v0.0.9 h1:Vzo/k2qwVGLTR10ESDeP2Ecek1SdPfZlEjtTfMveiVo= github.com/coder/aisdk-go v0.0.9/go.mod h1:KF6/Vkono0FJJOtWtveh5j7yfNrSctVTpwgweYWSp5M= -github.com/coder/boundary v1.0.1-0.20250925154134-55a44f2a7945 h1:hDUf02kTX8EGR3+5B+v5KdYvORs4YNfDPci0zCs+pC0= -github.com/coder/boundary v1.0.1-0.20250925154134-55a44f2a7945/go.mod h1:d1AMFw81rUgrGHuZzWdPNhkY0G8w7pvLNLYF0e3ceC4= +github.com/coder/boundary v0.0.1-alpha h1:6shUQ2zkrWrfbgVcqWvpV2ibljOQvPvYqTctWBkKoUA= +github.com/coder/boundary v0.0.1-alpha/go.mod h1:d1AMFw81rUgrGHuZzWdPNhkY0G8w7pvLNLYF0e3ceC4= github.com/coder/bubbletea v1.2.2-0.20241212190825-007a1cdb2c41 h1:SBN/DA63+ZHwuWwPHPYoCZ/KLAjHv5g4h2MS4f2/MTI= github.com/coder/bubbletea v1.2.2-0.20241212190825-007a1cdb2c41/go.mod h1:I9ULxr64UaOSUv7hcb3nX4kowodJCVS7vt7VVJk/kW4= github.com/coder/clistat v1.1.2 h1:1WzCsEQ/VFBNyxu5ryy0Pdb6rrMh+byCp3aZMkn9k/E= github.com/coder/clistat v1.1.2/go.mod h1:F+gLef+F9chVrleq808RBxdaoq52R4VLopuLdAsh8Y4= github.com/coder/flog v1.1.0 h1:kbAes1ai8fIS5OeV+QAnKBQE22ty1jRF/mcAwHpLBa4= github.com/coder/flog v1.1.0/go.mod h1:UQlQvrkJBvnRGo69Le8E24Tcl5SJleAAR7gYEHzAmdQ= -github.com/coder/glog v1.0.1-0.20220322161911-7365fe7f2cd1/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/coder/go-httpstat v0.0.0-20230801153223-321c88088322 h1:m0lPZjlQ7vdVpRBPKfYIFlmgevoTkBxB10wv6l2gOaU= github.com/coder/go-httpstat v0.0.0-20230801153223-321c88088322/go.mod h1:rOLFDDVKVFiDqZFXoteXc97YXx7kFi9kYqR+2ETPkLQ= github.com/coder/go-scim/pkg/v2 v2.0.0-20230221055123-1d63c1222136 h1:0RgB61LcNs24WOxc3PBvygSNTQurm0PYPujJjLLOzs0= @@ -942,8 +316,8 @@ github.com/coder/pq v1.10.5-0.20250807075151-6ad9b0a25151 h1:YAxwg3lraGNRwoQ18H7 github.com/coder/pq v1.10.5-0.20250807075151-6ad9b0a25151/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs= github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc= -github.com/coder/preview v1.0.4 h1:f506bnyhHtI3ICl/8Eb/gemcKvm/AGzQ91uyxjF+D9k= -github.com/coder/preview v1.0.4/go.mod h1:PpLayC3ngQQ0iUhW2yVRFszOooto4JrGGMomv1rqUvA= +github.com/coder/preview v1.0.8 h1:RqejfDTplczgSiNqsrQTH7g2qV0p5FGZHTkc/psWZfM= +github.com/coder/preview v1.0.8/go.mod h1:BvAfITWREXP08NIOasaAJ2hi2TWFWc6Y0CSPKEPsMzk= github.com/coder/quartz v0.3.0 h1:bUoSEJ77NBfKtUqv6CPSC0AS8dsjqAqqAv7bN02m1mg= github.com/coder/quartz v0.3.0/go.mod h1:BgE7DOj/8NfvRgvKw0jPLDQH/2Lya2kxcTaNJ8X0rZk= github.com/coder/retry v1.5.1 h1:iWu8YnD8YqHs3XwqrqsjoBTAVqT9ml6z9ViJ2wlMiqc= @@ -952,14 +326,14 @@ github.com/coder/serpent v0.12.0 h1:fUu3qVjeRvVy3DB/C2EFFvOctm+f2HKyckyfA86O63Q= github.com/coder/serpent v0.12.0/go.mod h1:mPEpD8Cq106E0glBs5ROAAGoALLtD5HAAMVZmjf4zO0= github.com/coder/ssh v0.0.0-20231128192721-70855dedb788 h1:YoUSJ19E8AtuUFVYBpXuOD6a/zVP3rcxezNsoDseTUw= github.com/coder/ssh v0.0.0-20231128192721-70855dedb788/go.mod h1:aGQbuCLyhRLMzZF067xc84Lh7JDs1FKwCmF1Crl9dxQ= -github.com/coder/tailscale v1.1.1-0.20250829055706-6eafe0f9199e h1:9RKGKzGLHtTvVBQublzDGtCtal3cXP13diCHoAIGPeI= -github.com/coder/tailscale v1.1.1-0.20250829055706-6eafe0f9199e/go.mod h1:jU9T1vEs+DOs8NtGp1F2PT0/TOGVwtg/JCCKYRgvMOs= +github.com/coder/tailscale v1.1.1-0.20260519043957-6f014ff9434f h1:gYivllu5CHhvRr4SM93zSQDj9cG2V+Pc0URTFy3fF/Y= +github.com/coder/tailscale v1.1.1-0.20260519043957-6f014ff9434f/go.mod h1:WTWP5ZNODDXHwWlQ1Jc2MFhqxu93pUs7lIy28Fd5a5E= github.com/coder/terraform-config-inspect v0.0.0-20250107175719-6d06d90c630e h1:JNLPDi2P73laR1oAclY6jWzAbucf70ASAvf5mh2cME0= github.com/coder/terraform-config-inspect v0.0.0-20250107175719-6d06d90c630e/go.mod h1:Gz/z9Hbn+4KSp8A2FBtNszfLSdT2Tn/uAKGuVqqWmDI= github.com/coder/terraform-provider-coder/v2 v2.13.1 h1:dtPaJUvueFm+XwBPUMWQCc5Z1QUQBW4B4RNyzX4h4y8= github.com/coder/terraform-provider-coder/v2 v2.13.1/go.mod h1:2irB3W8xRUo73nP5w6lN/dhN3abeCIKpqg8zElKIX/I= -github.com/coder/trivy v0.0.0-20250807211036-0bb0acd620a8 h1:VYB/6cIIKsVkwXOAWbqpj4Ux+WwF/XTnRyvHcwfHZ7A= -github.com/coder/trivy v0.0.0-20250807211036-0bb0acd620a8/go.mod h1:O73tP+UvJlI2GQZD060Jt0sf+6alKcGAgORh6sgB0+M= +github.com/coder/trivy v0.0.0-20260309164037-c413f5a2f511 h1:wJS3Pk13VuCbV8hjrQRnOBCUwP3Islk91sMvbSdY0Vk= +github.com/coder/trivy v0.0.0-20260309164037-c413f5a2f511/go.mod h1:+zF17ZBOdhFWwD3+GkLxZ/vkmKLudoOtt+hgnc1TQpA= github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/coder/wgtunnel v0.1.13-0.20240522110300-ade90dfb2da0 h1:C2/eCr+r0a5Auuw3YOiSyLNHkdMtyCZHPFBx7syN4rk= @@ -974,8 +348,10 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E= -github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4= +github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/stargz-snapshotter/estargz v0.18.1 h1:cy2/lpgBXDA3cDKSyEfNOFMA/c10O1axL69EU7iirO8= +github.com/containerd/stargz-snapshotter/estargz v0.18.1/go.mod h1:ALIEqa7B6oVDsrF37GkGN20SuvG/pIMm7FwP7ZmRb0Q= github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc= @@ -984,11 +360,10 @@ github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pq github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= -github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/cyphar/filepath-securejoin v0.5.1 h1:eYgfMq5yryL4fbWfkLpFFy2ukSELzaJOTaUTuh+oF48= -github.com/cyphar/filepath-securejoin v0.5.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= +github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ= github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= github.com/dave/dst v0.27.2 h1:4Y5VFTkhGLC1oddtNwuxxe36pnyLxMFXT51FOzH8Ekc= @@ -1001,8 +376,10 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dblohm7/wingoes v0.0.0-20240820181039-f2b84150679e h1:L+XrFvD0vBIBm+Wf9sFN6aU395t7JROoai0qXZraA4U= github.com/dblohm7/wingoes v0.0.0-20240820181039-f2b84150679e/go.mod h1:SUxUaAK/0UG5lYyZR1L1nC4AaYYvSSYTWQSH3FPcxKU= -github.com/dgraph-io/badger/v4 v4.7.0 h1:Q+J8HApYAY7UMpL8d9owqiB+odzEc0zn/aqOD9jhc6Y= -github.com/dgraph-io/badger/v4 v4.7.0/go.mod h1:He7TzG3YBy3j4f5baj5B7Zl2XyfNe5bl4Udl0aPemVA= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= +github.com/dgraph-io/badger/v4 v4.8.0 h1:JYph1ChBijCw8SLeybvPINizbDKWZ5n/GYbz2yhN/bs= +github.com/dgraph-io/badger/v4 v4.8.0/go.mod h1:U6on6e8k/RTbUWxqKR0MvugJuVmkxSNc79ap4917h4w= github.com/dgraph-io/ristretto/v2 v2.3.0 h1:qTQ38m7oIyd4GAed/QkUZyPFNMnvVWyazGXRwvOt5zk= github.com/dgraph-io/ristretto/v2 v2.3.0/go.mod h1:gpoRV3VzrEY1a9dWAYV6T1U7YzfgttXdd/ZzL1s9OZM= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= @@ -1019,15 +396,14 @@ github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5 github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v28.3.2+incompatible h1:mOt9fcLE7zaACbxW1GeS65RI67wIJrTnqS3hP2huFsY= -github.com/docker/cli v28.3.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= -github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/cli v29.2.0+incompatible h1:9oBd9+YM7rxjZLfyMGxjraKBKE4/nVyvVfN4qNl9XRM= +github.com/docker/cli v29.2.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= +github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd h1:QMSNEh9uQkDjyPwu/J541GgSH+4hw+0skJDIj9HJ3mE= github.com/dop251/goja v0.0.0-20241024094426-79f3a7efcdbd/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -1047,29 +423,14 @@ github.com/emersion/go-smtp v0.21.2 h1:OLDgvZKuofk4em9fT5tFG5j4jE1/hXnX75UMvcrL4 github.com/emersion/go-smtp v0.21.2/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= -github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= -github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329/go.mod h1:Alz8LEClvR7xKsrq3qzoc4N0guvVNSS8KmSChGYr9hs= -github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo= -github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= -github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/esiqveland/notify v0.13.3 h1:QCMw6o1n+6rl+oLUfg8P1IIDSFsDEb2WlXvVvIJbI/o= @@ -1089,8 +450,6 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fergusstrange/embedded-postgres v1.32.0 h1:kh2ozEvAx2A0LoIJZEGNwHmoFTEQD243KrHjifcYGMo= github.com/fergusstrange/embedded-postgres v1.32.0/go.mod h1:w0YvnCgf19o6tskInrOOACtnqfVlOvluz3hlNLY7tRk= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= @@ -1100,51 +459,38 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gen2brain/beeep v0.11.1 h1:EbSIhrQZFDj1K2fzlMpAYlFOzV8YuNe721A58XcCTYI= github.com/gen2brain/beeep v0.11.1/go.mod h1:jQVvuwnLuwOcdctHn/uyh8horSBNJ8uGb9Cn2W4tvoc= github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ= github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I= github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo= github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618= -github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= +github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4= +github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/hostrouter v0.3.0 h1:75it1eO3FvkG8te1CvU6Kvr3WzAZNEBbo8xIrxUKLOQ= github.com/go-chi/hostrouter v0.3.0/go.mod h1:KLB+7PH/ceOr6FCmMyWD2Dmql/clpOe+y7I7CUeTkaQ= github.com/go-chi/httprate v0.15.0 h1:j54xcWV9KGmPf/X4H32/aTH+wBlrvxL7P+SdnRqxh5g= github.com/go-chi/httprate v0.15.0/go.mod h1:rzGHhVrsBn3IMLYDOZQsSU4fJNWcjui4fWKJcCId1R4= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= -github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= -github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM= -github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA= +github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw= +github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00= +github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= -github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 h1:iizUGZ9pEquQS5jTGkh4AqeeHCMbfbjeb0zMt0aEFzs= github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -1156,16 +502,33 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= -github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= -github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= -github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= -github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= +github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= +github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= +github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= +github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc= +github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs= +github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -1193,9 +556,10 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs= github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= -github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.19.0 h1:EmkZ9RIsX+Uq4DYFowegAuJo8+xdX3T/2dwNPXbxEYE= +github.com/goccy/go-yaml v1.19.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= @@ -1227,75 +591,32 @@ github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9v github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang-migrate/migrate/v4 v4.19.0 h1:RcjOnCGz3Or6HQYEJ/EEVLfWnmw9KnoigPSjzhCuaSE= github.com/golang-migrate/migrate/v4 v4.19.0/go.mod h1:9dyEcu+hO+G9hPSw8AIg50yg622pXJsoHItQnDGZkI0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U= github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomarkdown/markdown v0.0.0-20240930133441-72d49d9543d8 h1:4txT5G2kqVAKMjzidIabL/8KqjIK71yj30YOeuxLn10= -github.com/gomarkdown/markdown v0.0.0-20240930133441-72d49d9543d8/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/gomarkdown/markdown v0.0.0-20260417124207-7d523f7318df h1:Mwihr/o+v4L5h56rwHLOE20+hh7Okhwno5BHz3zDuao= +github.com/gomarkdown/markdown v0.0.0-20260417124207-7d523f7318df/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU= -github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y= +github.com/google/go-containerregistry v0.20.7 h1:24VGNpS0IwrOZ2ms2P1QE3Xa5X9p4phx0aUgzYzHW6I= +github.com/google/go-containerregistry v0.20.7/go.mod h1:Lx5LCZQjLH1QBaMPeGwsME9biPeo1lPx6lbGj/UmzgM= github.com/google/go-github/v43 v43.0.1-0.20220414155304-00e42332e405 h1:DdHws/YnnPrSywrjNYu2lEHqYHWp/LnEx56w59esd54= github.com/google/go-github/v43 v43.0.1-0.20220414155304-00e42332e405/go.mod h1:4RgUDSnsxP19d65zJWqvqJ/poJxBCvmna50eXmIvoR8= github.com/google/go-github/v61 v61.0.0 h1:VwQCBwhyE9JclCI+22/7mLB1PuU9eowCXKY5pNlu1go= @@ -1306,73 +627,32 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/nftables v0.2.0 h1:PbJwaBmbVLzpeldoeUKGkE2RjstrjPKMl6oLrfEJ6/8= github.com/google/nftables v0.2.0/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ= -github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= -github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/enterprise-certificate-proxy v0.3.9 h1:TOpi/QG8iDcZlkQlGlFUti/ZtyLkliXvHDcyUIMuFrU= +github.com/googleapis/enterprise-certificate-proxy v0.3.9/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y= +github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= github.com/hairyhenderson/go-codeowners v0.7.0 h1:s0W4wF8bdsBEjTWzwzSlsatSthWtTAF2xLgo4a4RwAo= github.com/hairyhenderson/go-codeowners v0.7.0/go.mod h1:wUlNgQ3QjqC4z8DnM5nnCYVq/icpqXJyJOukKx5U8/Q= +github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65 h1:81+kWbE1yErFBMjME0I5k3x3kojjKsWtPYHEAutoPow= +github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65/go.mod h1:WtMzv9T++tfWVea+qB2MXoaqxw33S8bpJslzUike2mQ= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -1382,8 +662,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-cty v1.5.0 h1:EkQ/v+dDNUqnuVpmS5fPqyY71NXVgT5gf32+57xY8g0= github.com/hashicorp/go-cty v1.5.0/go.mod h1:lFUCG5kd8exDobgSfyj4ONE/dc822kiYMguVKdHGMLM= -github.com/hashicorp/go-getter v1.7.9 h1:G9gcjrDixz7glqJ+ll5IWvggSBR+R0B54DSRt4qfdC4= -github.com/hashicorp/go-getter v1.7.9/go.mod h1:dyFCmT1AQkDfOIt9NH8pw9XBDqNrIKJT5ylbpi7zPNE= +github.com/hashicorp/go-getter v1.8.3 h1:gIS+oTNv3kyYAvlUVgMR46MiG0bM0KuSON/KZEvRoRg= +github.com/hashicorp/go-getter v1.8.3/go.mod h1:CUTt9x2bCtJ/sV8ihgrITL3IUE+0BE1j/e4n5P/GIM4= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -1394,29 +674,25 @@ github.com/hashicorp/go-reap v0.0.0-20170704170343-bf58d8a43e7b h1:3GrpnZQBxcMj1 github.com/hashicorp/go-reap v0.0.0-20170704170343-bf58d8a43e7b/go.mod h1:qIFzeFcJU3OIFk/7JreWXcUjFmcCaeHTH9KoNyHYVCs= github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= -github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= -github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= github.com/hashicorp/go-terraform-address v0.0.0-20240523040243-ccea9d309e0c h1:5v6L/m/HcAZYbrLGYBpPkcCVtDWwIgFxq2+FUmfPxPk= github.com/hashicorp/go-terraform-address v0.0.0-20240523040243-ccea9d309e0c/go.mod h1:xoy1vl2+4YvqSQEkKcFjNYxTk7cll+o1f1t2wxnHIX8= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA= +github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hashicorp/hc-install v0.9.2 h1:v80EtNX4fCVHqzL9Lg/2xkp62bbvQMnvPQ0G+OmtO24= -github.com/hashicorp/hc-install v0.9.2/go.mod h1:XUqBQNnuT4RsxoxiM9ZaUk0NX8hi2h+Lb6/c0OZnC/I= +github.com/hashicorp/hc-install v0.9.4 h1:KKWOpUG0EqIV63Qk2GGFrZ0s275NVs5lKf9N5vjBNoc= +github.com/hashicorp/hc-install v0.9.4/go.mod h1:4LRYeEN2bMIFfIv57ldMWt9awfuZhvpbRt0vWmv51WU= github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE= github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/terraform-exec v0.23.1 h1:diK5NSSDXDKqHEOIQefBMu9ny+FhzwlwV0xgUTB7VTo= -github.com/hashicorp/terraform-exec v0.23.1/go.mod h1:e4ZEg9BJDRaSalGm2z8vvrPONt0XWG0/tXpmzYTf+dM= +github.com/hashicorp/terraform-exec v0.24.0 h1:mL0xlk9H5g2bn0pPF6JQZk5YlByqSqrO5VoaNtAf8OE= +github.com/hashicorp/terraform-exec v0.24.0/go.mod h1:lluc/rDYfAhYdslLJQg3J0oDqo88oGQAdHR+wDqFvo4= github.com/hashicorp/terraform-json v0.27.2 h1:BwGuzM6iUPqf9JYM/Z4AF1OJ5VVJEEzoKST/tRDBJKU= github.com/hashicorp/terraform-json v0.27.2/go.mod h1:GzPLJ1PLdUG5xL6xn1OXWIjteQRT2CNT9o/6A9mi9hE= github.com/hashicorp/terraform-plugin-go v0.29.0 h1:1nXKl/nSpaYIUBU1IG/EsDOX0vv+9JxAltQyDMpq5mU= @@ -1441,9 +717,6 @@ github.com/hugelgupf/vmtest v0.0.0-20240216064925-0561770280a1 h1:jWoR2Yqg8tzM0v github.com/hugelgupf/vmtest v0.0.0-20240216064925-0561770280a1/go.mod h1:B63hDJMhTupLWCHwopAyEo7wRFowx9kOc8m8j1sfOqE= github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/icholy/replace v0.6.0 h1:EBiD2pGqZIOJAbEaf/5GVRaD/Pmbb4n+K3LrBdXd4dw= github.com/icholy/replace v0.6.0/go.mod h1:zzi8pxElj2t/5wHHHYmH45D+KxytX/t4w3ClY5nlK+g= github.com/illarion/gonotify v1.0.1 h1:F1d+0Fgbq/sDWjj/r66ekjDG+IDeecQKUFH4wNwsoio= @@ -1461,11 +734,6 @@ github.com/jdkato/prose v1.2.1/go.mod h1:AiRHgVagnEx2JbQRQowVBKjG0bcs/vtkGCH1dYA github.com/jedib0t/go-pretty/v6 v6.7.1 h1:bHDSsj93NuJ563hHuM7ohk/wpX7BmRFNIsVv1ssI2/M= github.com/jedib0t/go-pretty/v6 v6.7.1/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY= -github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -1476,10 +744,6 @@ github.com/jsimonetti/rtnetlink v1.3.5 h1:hVlNQNRlLDGZz31gBPicsG7Q53rnlsz1l1Ix/9 github.com/jsimonetti/rtnetlink v1.3.5/go.mod h1:0LFedyiTkebnd43tE4YAkWGIq9jQphow4CcwxaT2Y00= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/justinas/nosurf v1.2.0 h1:yMs1bSRrNiwXk4AS6n8vL2Ssgpb9CB25T/4xrixaK0s= github.com/justinas/nosurf v1.2.0/go.mod h1:ALpWdSbuNGy2lZWtyXdjkYv4edL23oSEgfBT1gPJ5BQ= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= @@ -1490,21 +754,15 @@ github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f h1:dKccXx7xA56UNq github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f/go.mod h1:4rEELDSfUAlBSyUjPG0JnaNGjf13JySHFeRdD/3dLP0= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= -github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ= github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a/go.mod h1:YTtCCM3ryyfiu4F7t8HQ1mxvp1UBdWM2r6Xa+nGWvDk= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -1513,8 +771,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylecarbs/chroma/v2 v2.0.0-20240401211003-9e036e0631f3 h1:Z9/bo5PSeMutpdiKYNt/TTSfGM1Ll0naj3QzYX9VxTc= github.com/kylecarbs/chroma/v2 v2.0.0-20240401211003-9e036e0631f3/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk= -github.com/kylecarbs/opencensus-go v0.23.1-0.20220307014935-4d0325a68f8b/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -github.com/kylecarbs/readline v0.0.0-20220211054233-0d62993714c8/go.mod h1:n/KX1BZoN1m9EwoXkn/xAV4fd3k8c++gGBsgLONaPOY= github.com/kylecarbs/spinner v1.18.2-0.20220329160715-20702b5af89e h1:OP0ZMFeZkUnOzTFRfpuK3m7Kp4fNvC6qN+exwj7aI4M= github.com/kylecarbs/spinner v1.18.2-0.20220329160715-20702b5af89e/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -1525,15 +781,26 @@ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kUL github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/liamg/memoryfs v1.6.0 h1:jAFec2HI1PgMTem5gR7UT8zi9u4BfG5jorCRlLH06W8= -github.com/liamg/memoryfs v1.6.0/go.mod h1:z7mfqXFQS8eSeBBsFjYLlxYRMRyiPktytvYCYTb3BSk= +github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= +github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw= +github.com/lestrrat-go/dsig v1.0.0 h1:OE09s2r9Z81kxzJYRn07TFM9XA4akrUdoMwr0L8xj38= +github.com/lestrrat-go/dsig v1.0.0/go.mod h1:dEgoOYYEJvW6XGbLasr8TFcAxoWrKlbQvmJgCR0qkDo= +github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY= +github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/httprc/v3 v3.0.1 h1:3n7Es68YYGZb2Jf+k//llA4FTZMl3yCwIjFIk4ubevI= +github.com/lestrrat-go/httprc/v3 v3.0.1/go.mod h1:2uAvmbXE4Xq8kAUjVrZOq1tZVYYYs5iP62Cmtru00xk= +github.com/lestrrat-go/jwx/v3 v3.0.12 h1:p25r68Y4KrbBdYjIsQweYxq794CtGCzcrc5dGzJIRjg= +github.com/lestrrat-go/jwx/v3 v3.0.12/go.mod h1:HiUSaNmMLXgZ08OmGBaPVvoZQgJVOQphSrGr5zMamS8= +github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= +github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss= +github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg= github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= @@ -1544,7 +811,6 @@ github.com/marekm4/color-extractor v1.2.1 h1:3Zb2tQsn6bITZ8MBVhc33Qn1k5/SEuZ18mr github.com/marekm4/color-extractor v1.2.1/go.mod h1:90VjmiHI6M8ez9eYUaXLdcKnS+BAOp7w+NpwBdkJmpA= github.com/mark3labs/mcp-go v0.38.0 h1:E5tmJiIXkhwlV0pLAwAT0O5ZjUZSISE/2Jxg+6vpq4I= github.com/mark3labs/mcp-go v0.38.0/go.mod h1:T7tUa2jO6MavG+3P25Oy/jR7iCeJPHImCZHRymCn39g= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -1553,16 +819,13 @@ github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stg github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= @@ -1577,8 +840,6 @@ github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwX github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -1597,8 +858,10 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= -github.com/moby/moby v28.5.0+incompatible h1:eN6ksRE7BojoGW18USJGfyqhx/FWJPLs0jqaTNlfSsM= -github.com/moby/moby v28.5.0+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= +github.com/moby/moby/api v1.54.0 h1:7kbUgyiKcoBhm0UrWbdrMs7RX8dnwzURKVbZGy2GnL0= +github.com/moby/moby/api v1.54.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= +github.com/moby/moby/client v0.3.0 h1:UUGL5okry+Aomj3WhGt9Aigl3ZOxZGqR7XPo+RLPlKs= +github.com/moby/moby/client v0.3.0/go.mod h1:HJgFbJRvogDQjbM8fqc1MCEm4mIAGMLjXbgwoZp6jCQ= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= @@ -1614,8 +877,9 @@ github.com/mocktools/go-smtp-mock/v2 v2.5.0/go.mod h1:h9AOf/IXLSU2m/1u4zsjtOM/Wd github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -1650,8 +914,8 @@ github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI= github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g= github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY= github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo= -github.com/open-policy-agent/opa v1.6.0 h1:/S/cnNQJ2MUMNzizHPbisTWBHowmLkPrugY5jjkPlRQ= -github.com/open-policy-agent/opa v1.6.0/go.mod h1:zFmw4P+W62+CWGYRDDswfVYSCnPo6oYaktQnfIaRFC4= +github.com/open-policy-agent/opa v1.11.0 h1:eOd/jJrbavakiX477yT4LrXZfUWViAot/AsKsjsfe7o= +github.com/open-policy-agent/opa v1.11.0/go.mod h1:QimuJO4T3KYxWzrmAymqlFvsIanCjKrGjmmC8GgAdgE= github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.120.1 h1:lK/3zr73guK9apbXTcnDnYrC0YCQ25V3CIULYz3k2xU= github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.120.1/go.mod h1:01TvyaK8x640crO2iFwW/6CFCZgNsOvOGH3B5J239m0= github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.120.1 h1:TCyOus9tym82PD1VYtthLKMVMlVyRwtDI4ck4SR2+Ok= @@ -1684,10 +948,6 @@ github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY= github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= @@ -1698,8 +958,8 @@ github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1 github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= github.com/pion/udp v0.1.4 h1:OowsTmu1Od3sD6i3fQUJxJn2fEvJO6L1TidgadtbTI8= github.com/pion/udp v0.1.4/go.mod h1:G8LDo56HsFwC24LIcnT4YIDU5qcB6NepqqjP0keL2us= -github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= -github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU= +github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= @@ -1720,22 +980,18 @@ github.com/prometheus-community/pro-bing v0.7.0 h1:KFYFbxC2f2Fp6c+TyxbCOEarf7rbn github.com/prometheus-community/pro-bing v0.7.0/go.mod h1:Moob9dvlY50Bfq6i88xIwfyw7xLFHH69LUgx9n5zqCE= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= +github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= -github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= -github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= -github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= -github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/quasilyte/go-ruleguard/dsl v0.3.23 h1:lxjt5B6ZCiBeeNO8/oQsegE6fLeCzuMRoVWSkXC4uvY= +github.com/quasilyte/go-ruleguard/dsl v0.3.23/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg= +github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/riandyrn/otelchi v0.5.1 h1:0/45omeqpP7f/cvdL16GddQBfAEmZvUyl2QzLSE6uYo= github.com/riandyrn/otelchi v0.5.1/go.mod h1:ZxVxNEl+jQ9uHseRYIxKWRb3OY8YXFEu+EkNiiSNUEA= github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3 h1:4+LEVOB87y175cLJC/mbsgKmoDOjrBldtXvioEy96WY= @@ -1746,38 +1002,35 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= -github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI= -github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= +github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw= +github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= -github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= +github.com/secure-systems-lab/go-securesystemslib v0.10.0 h1:l+H5ErcW0PAehBNrBxoGv1jjNpGYdZ9RcheFkB2WI14= +github.com/secure-systems-lab/go-securesystemslib v0.10.0/go.mod h1:MRKONWmRoFzPNQ9USRF9i1mc7MvAVvF1LlW8X5VWDvk= +github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= +github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sergeymakinen/go-bmp v1.0.0 h1:SdGTzp9WvCV0A1V0mBeaS7kQAwNLdVJbmHlqNWq0R+M= github.com/sergeymakinen/go-bmp v1.0.0/go.mod h1:/mxlAQZRLxSvJFNIEGGLBE/m40f3ZnUifpgVDlcUIEY= github.com/sergeymakinen/go-ico v1.0.0-beta.0 h1:m5qKH7uPKLdrygMWxbamVn+tl2HfiA3K6MFJw4GfZvQ= github.com/sergeymakinen/go-ico v1.0.0-beta.0/go.mod h1:wQ47mTczswBO5F0NoDt7O0IXgnV4Xy3ojrroMQzyhUk= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc= -github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= +github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs= +github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yfB+If0vjp97vuT74F72r8hfRpP8jLU0= +github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/smallstep/pkcs7 v0.2.1 h1:6Kfzr/QizdIuB6LSv8y1LJdZ3aPSfTNhTLqAx9CTLfA= +github.com/smallstep/pkcs7 v0.2.1/go.mod h1:RcXHsMfL+BzH8tRhmrF1NkkpebKpq3JEM66cOFxanf0= github.com/sosedoff/gitkit v0.4.0 h1:opyQJ/h9xMRLsz2ca/2CRXtstePcpldiZN8DpLLF8Os= github.com/sosedoff/gitkit v0.4.0/go.mod h1:V3EpGZ0nvCBhXerPsbDeqtyReNb48cwP9KtkUYTKT5I= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= @@ -1804,7 +1057,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= @@ -1813,8 +1065,8 @@ github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7 github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU= github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw= github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM= -github.com/swaggo/http-swagger/v2 v2.0.1 h1:mNOBLxDjSNwCKlMxcErjjvct/xhc9t2KIO48xzz/V/k= -github.com/swaggo/http-swagger/v2 v2.0.1/go.mod h1:XYhrQVIKz13CxuKD4p4kvpaRB4jJ1/MlfQXVOE+CX8Y= +github.com/swaggo/http-swagger/v2 v2.0.2 h1:FKCdLsl+sFCx60KFsyM0rDarwiUSZ8DqbfSyIKC9OBg= +github.com/swaggo/http-swagger/v2 v2.0.2/go.mod h1:r7/GBkAWIfK6E/OLnE8fXnviHiDeAHmgIyooa4xm3AQ= github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04= github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E= github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG04SN9W+iWHCRyHqlVYILiSXziwk= @@ -1831,20 +1083,20 @@ github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+y github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc= github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA= github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk= -github.com/tchap/go-patricia/v2 v2.3.2 h1:xTHFutuitO2zqKAQ5rCROYgUb7Or/+IC3fts9/Yc7nM= -github.com/tchap/go-patricia/v2 v2.3.2/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= +github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc= +github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/tdewolff/minify/v2 v2.24.5 h1:ytxthX3xSxrK3Xx5B38flg5moCKs/dB8VwiD/RzJViU= github.com/tdewolff/minify/v2 v2.24.5/go.mod h1:q09KtNnVai7TyEzGEZeWPAnK+c8Z+NI8prCXZW652bo= github.com/tdewolff/parse/v2 v2.8.5-0.20251020133559-0efcf90bef1a h1:Rmq+utdraciok/97XHRweYdsAo/M4LOswpCboo3yvN4= github.com/tdewolff/parse/v2 v2.8.5-0.20251020133559-0efcf90bef1a/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo= github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE= github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= -github.com/testcontainers/testcontainers-go v0.38.0 h1:d7uEapLcv2P8AvH8ahLqDMMxda2W9gQN1nRbHS28HBw= -github.com/testcontainers/testcontainers-go v0.38.0/go.mod h1:C52c9MoHpWO+C4aqmgSU+hxlR5jlEayWtgYrb8Pzz1w= -github.com/testcontainers/testcontainers-go/modules/localstack v0.38.0 h1:3ljIy6FmHtFhZsZwsaMIj/27nCRm0La7N/dl5Jou8AA= -github.com/testcontainers/testcontainers-go/modules/localstack v0.38.0/go.mod h1:BTsbqWC9huPV8Jg8k46Jz4x1oRAA9XGxneuuOOIrtKY= -github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I= -github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= +github.com/testcontainers/testcontainers-go v0.40.0 h1:pSdJYLOVgLE8YdUY2FHQ1Fxu+aMnb6JfVz1mxk7OeMU= +github.com/testcontainers/testcontainers-go v0.40.0/go.mod h1:FSXV5KQtX2HAMlm7U3APNyLkkap35zNLxukw9oBi/MY= +github.com/testcontainers/testcontainers-go/modules/localstack v0.40.0 h1:b+lN2Ch4J/6EwqB+Af+QQbSfv4sFGetHlBHpXi+1yJU= +github.com/testcontainers/testcontainers-go/modules/localstack v0.40.0/go.mod h1:8LuTSboTo2MJKFKV5xH6z4ZH1s3jhRJWwvtPJzKogj4= +github.com/tetratelabs/wazero v1.10.1 h1:2DugeJf6VVk58KTPszlNfeeN8AhhpwcZqkJj2wwFuH8= +github.com/tetratelabs/wazero v1.10.1/go.mod h1:DRm5twOQ5Gr1AoEdSi0CLjDQF1J9ZAuyqFIjl1KKfQU= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -1870,7 +1122,6 @@ github.com/u-root/u-root v0.14.0 h1:Ka4T10EEML7dQ5XDvO9c3MBN8z4nuSnGjcd1jmU2ivg= github.com/u-root/u-root v0.14.0/go.mod h1:hAyZorapJe4qzbLWlAkmSVCJGbfoU9Pu4jpJ1WMluqE= github.com/u-root/uio v0.0.0-20240209044354-b3d14b93376a h1:BH1SOPEvehD2kVrndDnGJiUF0TrBpNs+iyYocu6h0og= github.com/u-root/uio v0.0.0-20240209044354-b3d14b93376a/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA= -github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/unrolled/secure v1.17.0 h1:Io7ifFgo99Bnh0J7+Q+qcMzWM6kaDPCA5FroFZEdbWU= @@ -1879,8 +1130,12 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok= github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4= -github.com/vektah/gqlparser/v2 v2.5.28 h1:bIulcl3LF69ba6EiZVGD88y4MkM+Jxrf3P2MX8xLRkY= -github.com/vektah/gqlparser/v2 v2.5.28/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= +github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= +github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= +github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4= +github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= +github.com/vektah/gqlparser/v2 v2.5.31 h1:YhWGA1mfTjID7qJhd1+Vxhpk5HTgydrGU9IgkWBTJ7k= +github.com/vektah/gqlparser/v2 v2.5.31/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts= github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs= github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= @@ -1931,15 +1186,12 @@ github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCO github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= -github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark v1.7.17 h1:p36OVWwRb246iHxA/U4p8OPEpOTESm4n+g+8t0EE5uA= +github.com/yuin/goldmark v1.7.17/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs= github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= @@ -1956,8 +1208,6 @@ github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -go.mozilla.org/pkcs7 v0.9.0 h1:yM4/HS9dYv7ri2biPtxt8ikvB37a980dg69/pKmS+eI= -go.mozilla.org/pkcs7 v0.9.0/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.nhat.io/otelsql v0.16.0 h1:MUKhNSl7Vk1FGyopy04FBDimyYogpRFs0DBB9frQal0= go.nhat.io/otelsql v0.16.0/go.mod h1:YB2ocf0Q8+kK4kxzXYUOHj7P2Km8tNmE2QlRS0frUtc= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= @@ -1993,40 +1243,37 @@ go.opentelemetry.io/collector/semconv v0.123.0/go.mod h1:te6VQ4zZJO5Lp8dM2XIhDxD go.opentelemetry.io/contrib v1.0.0/go.mod h1:EH4yDYeNoaTqn/8yCWQmfNB78VHfGX2Jt2bvnvzBlGM= go.opentelemetry.io/contrib v1.19.0 h1:rnYI7OEPMWFeM4QCqWQ3InMJ0arWMR1i0Cx9A5hcjYM= go.opentelemetry.io/contrib v1.19.0/go.mod h1:gIzjwWFoGazJmtCaDgViqOSJPde2mCWzv60o0bWPcZs= -go.opentelemetry.io/contrib/detectors/gcp v1.38.0 h1:ZoYbqX7OaA/TAikspPl3ozPI6iY6LiIY9I8cUfm+pJs= -go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 h1:rbRJ8BBoVMsQShESYZ0FkvcITu8X8QNwJogcLUmDNNw= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0/go.mod h1:ru6KHrNtNHxM4nD/vd6QrLVWgKhxPYgblq4VAtNawTQ= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY= +go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE= +go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 h1:nRVXXvf78e00EwY6Wp0YII8ww2JVWshZ20HfTlE11AM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.37.0 h1:6VjV6Et+1Hd2iLZEPtdV7vie80Yyqf7oikJLjQ/myi0= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.37.0/go.mod h1:u8hcp8ji5gaM/RfcOo8z9NMnf1pVLfVY7lBY2VOGuUU= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0 h1:SNhVp/9q4Go/XHBkQ1/d5u9P/U+L1yaGPoi0x+mStaI= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0/go.mod h1:tx8OOlGH6R4kLV67YaYO44GFXloEjGPZuMjEkaaqIp4= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= -go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= +go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -2036,19 +1283,17 @@ go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= -go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= -go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8= go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516 h1:X66ZEoMN2SuaoI/dfZVYobB6E5zjZyyHUMWlCA7MgGE= go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516/go.mod h1:TQvodOM+hJTioNQJilmLXu08JNb8i+ccq418+KWu1/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -2060,281 +1305,87 @@ golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= -golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.32.0 h1:6lZQWq75h7L5IWNk0r+SCpUJ6tUVd3v4ZHnbRKLkUDQ= -golang.org/x/image v0.32.0/go.mod h1:/R37rrQmKXtO6tYXAjtDLwQgFLHmhW+V6ayXlxzP2Pc= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= +golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE= +golang.org/x/image v0.38.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= -golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= -golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2342,19 +1393,14 @@ golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= @@ -2362,116 +1408,42 @@ golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= -golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= @@ -2480,293 +1452,34 @@ golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvY golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80= golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= -google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/api v0.256.0 h1:u6Khm8+F9sxbCTYNoBHg6/Hwv0N/i+V94MvkOSor6oI= -google.golang.org/api v0.256.0/go.mod h1:KIgPhksXADEKJlnEoRa9qAII4rXcy40vfI8HRqcU964= +google.golang.org/api v0.260.0 h1:XbNi5E6bOVEj/uLXQRlt6TKuEzMD7zvW/6tNwltE4P4= +google.golang.org/api v0.260.0/go.mod h1:Shj1j0Phr/9sloYrKomICzdYgsSDImpTxME8rGLaZ/o= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genai v1.12.0 h1:0JjAdwvEAha9ZpPH5hL6dVG8bpMnRbAMCgv2f2LDnz4= google.golang.org/genai v1.12.0/go.mod h1:HFXR1zT3LCdLxd/NW6IOSCczOYyRAxwaShvYbgPSeVw= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= -google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/genproto v0.0.0-20250715232539-7130f93afb79 h1:Nt6z9UHqSlIdIGJdz6KhTIs2VRx/iOsA5iE8bmQNcxs= -google.golang.org/genproto v0.0.0-20250715232539-7130f93afb79/go.mod h1:kTmlBHMPqR5uCZPBvwa2B18mvubkjyY3CRLI0c6fj0s= -google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4= -google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 h1:GvESR9BIyHUahIb0NcTum6itIWtdoglGX+rnGxm2934= +google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:yJ2HH4EHEDTd3JiLmhds6NkJ17ITVYOdV3m3VKOnws0= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/DataDog/dd-trace-go.v1 v1.74.0 h1:wScziU1ff6Bnyr8MEyxATPSLJdnLxKz3p6RsA8FUaek= gopkg.in/DataDog/dd-trace-go.v1 v1.74.0/go.mod h1:ReNBsNfnsjVC7GsCe80zRcykL/n+nxvsNrg3NbjuleM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= @@ -2775,8 +1488,6 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -2789,66 +1500,22 @@ gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= gvisor.dev/gvisor v0.0.0-20240509041132-65b30f7869dc h1:DXLLFYv/k/xr0rWcwVEvWme1GR36Oc4kNMspg38JeiE= gvisor.dev/gvisor v0.0.0-20240509041132-65b30f7869dc/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= -k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= -k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= -k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= -k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= +k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0= +k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= kernel.org/pub/linux/libs/security/libcap/cap v1.2.73 h1:Th2b8jljYqkyZKS3aD3N9VpYsQpHuXLgea+SZUIfODA= kernel.org/pub/linux/libs/security/libcap/cap v1.2.73/go.mod h1:hbeKwKcboEsxARYmcy/AdPVN11wmT/Wnpgv4k4ftyqY= kernel.org/pub/linux/libs/security/libcap/psx v1.2.73 h1:SEAEUiPVylTD4vqqi+vtGkSnXeP2FcRO3FoZB1MklMw= kernel.org/pub/linux/libs/security/libcap/psx v1.2.73/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= +pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ= -sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= software.sslmate.com/src/go-pkcs12 v0.2.0/go.mod h1:23rNcYsMabIc1otwLpTkCCPwUq6kQsTyowttG/as0kQ= storj.io/drpc v0.0.33 h1:yCGZ26r66ZdMP0IcTYsj7WDAUIIjzXk6DJhbhvt9FHI= diff --git a/install.sh b/install.sh index 99752791a90..420a1f63a9e 100755 --- a/install.sh +++ b/install.sh @@ -273,7 +273,7 @@ EOF main() { MAINLINE=1 STABLE=0 - TERRAFORM_VERSION="1.13.4" + TERRAFORM_VERSION="1.16.6" if [ "${TRACE-}" ]; then set -x diff --git a/provisioner/terraform/install.go b/provisioner/terraform/install.go index 83791abfc11..02e9e42b103 100644 --- a/provisioner/terraform/install.go +++ b/provisioner/terraform/install.go @@ -22,10 +22,10 @@ var ( // when Terraform is not available on the system. // NOTE: Keep this in sync with the version in scripts/Dockerfile.base. // NOTE: Keep this in sync with the version in install.sh. - TerraformVersion = version.Must(version.NewVersion("1.13.4")) + TerraformVersion = version.Must(version.NewVersion("1.16.6")) minTerraformVersion = version.Must(version.NewVersion("1.1.0")) - maxTerraformVersion = version.Must(version.NewVersion("1.13.9")) // use .9 to automatically allow patch releases + maxTerraformVersion = version.Must(version.NewVersion("1.16.9")) // use .9 to automatically allow patch releases errTerraformMinorVersionMismatch = xerrors.New("Terraform binary minor version mismatch.") ) diff --git a/provisioner/terraform/install_test.go b/provisioner/terraform/install_test.go index c259ccd2d2e..c857dc9d672 100644 --- a/provisioner/terraform/install_test.go +++ b/provisioner/terraform/install_test.go @@ -102,7 +102,7 @@ func (p *terraformProxy) handleGet(w http.ResponseWriter, r *http.Request) { require.NoError(p.t, err) // update index.json so urls in it point to proxy by making them relative - // "https://releases.hashicorp.com/terraform/1.13.4/terraform_1.13.4_windows_amd64.zip" -> "/terraform/1.13.4/terraform_1.13.4_windows_amd64.zip" + // "https://releases.hashicorp.com/terraform/1.14.5/terraform_1.14.5_windows_amd64.zip" -> "/terraform/1.14.5/terraform_1.14.5_windows_amd64.zip" if strings.HasSuffix(r.URL.Path, "index.json") { body = []byte(strings.ReplaceAll(string(body), terraformURL, "")) } diff --git a/provisioner/terraform/provision.go b/provisioner/terraform/provision.go index c99ee55ad8c..a4b07b02aa9 100644 --- a/provisioner/terraform/provision.go +++ b/provisioner/terraform/provision.go @@ -265,6 +265,7 @@ func provisionEnv( "CODER_WORKSPACE_BUILD_ID="+metadata.GetWorkspaceBuildId(), "CODER_TASK_ID="+metadata.GetTaskId(), "CODER_TASK_PROMPT="+metadata.GetTaskPrompt(), + awsSDKUserAgentEnv(safeEnvironValue(env, awsSDKUserAgentEnvKey)), ) if metadata.GetPrebuiltWorkspaceBuildStage().IsPrebuild() { env = append(env, provider.IsPrebuildEnvironmentVariable()+"=true") diff --git a/provisioner/terraform/provision_test.go b/provisioner/terraform/provision_test.go index 9a8a49c29b9..ed497aa2fa5 100644 --- a/provisioner/terraform/provision_test.go +++ b/provisioner/terraform/provision_test.go @@ -1226,6 +1226,7 @@ func TestProvision_SafeEnv(t *testing.T) { require.Contains(t, log, passedValue) require.NotContains(t, log, secretValue) require.Contains(t, log, "CODER_") + require.Contains(t, log, "AWS_SDK_UA_APP_ID=APN_1.1/pc_cdfmjwn8i6u8l9fwz8h82e4w3$") } func TestProvision_MalformedModules(t *testing.T) { diff --git a/provisioner/terraform/safeenv.go b/provisioner/terraform/safeenv.go index 4da2fc32cd9..a42a899bc82 100644 --- a/provisioner/terraform/safeenv.go +++ b/provisioner/terraform/safeenv.go @@ -53,3 +53,39 @@ func safeEnviron() []string { } return strippedEnv } + +// safeEnvironValue returns the value of the named variable in the given +// `KEY=VALUE` environment slice, or an empty string if it is not present. +func safeEnvironValue(env []string, name string) string { + prefix := name + "=" + for _, e := range env { + if strings.HasPrefix(e, prefix) { + return strings.TrimPrefix(e, prefix) + } + } + return "" +} + +const ( + awsSDKUserAgentEnvKey = "AWS_SDK_UA_APP_ID" + // awsSDKUserAgentCoder is Coder's AWS Partner Revenue Measurement + // User-Agent string. The `APN_1.1/pc_$` format and the + // space-delimited append behavior below follow AWS's guidance: + // https://docs.aws.amazon.com/PRM/latest/aws-prm-onboarding-guide/automated-user-agent.html + awsSDKUserAgentCoder = "APN_1.1/pc_cdfmjwn8i6u8l9fwz8h82e4w3$" +) + +// awsSDKUserAgentEnv returns the AWS_SDK_UA_APP_ID value to pass to the +// Terraform subprocess. If the caller's environment already configures an +// Application ID (e.g. an operator who is also an AWS Partner and wants +// their own revenue attribution), Coder's value is appended with a space +// delimiter so both attributions are preserved. Otherwise Coder's value is +// used on its own. +// +// See: https://docs.aws.amazon.com/PRM/latest/aws-prm-onboarding-guide/automated-user-agent.html +func awsSDKUserAgentEnv(existing string) string { + if existing == "" { + return awsSDKUserAgentEnvKey + "=" + awsSDKUserAgentCoder + } + return awsSDKUserAgentEnvKey + "=" + existing + " " + awsSDKUserAgentCoder +} diff --git a/provisioner/terraform/safeenv_internal_test.go b/provisioner/terraform/safeenv_internal_test.go new file mode 100644 index 00000000000..1863f8fee18 --- /dev/null +++ b/provisioner/terraform/safeenv_internal_test.go @@ -0,0 +1,44 @@ +package terraform + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSafeEnvironValue(t *testing.T) { + t.Parallel() + + env := []string{ + "FOO=bar", + "AWS_SDK_UA_APP_ID=my-existing-id", + "BAZ=qux", + } + require.Equal(t, "my-existing-id", safeEnvironValue(env, "AWS_SDK_UA_APP_ID")) + require.Equal(t, "bar", safeEnvironValue(env, "FOO")) + require.Equal(t, "", safeEnvironValue(env, "MISSING")) +} + +func TestAWSSDKUserAgentEnv(t *testing.T) { + t.Parallel() + + t.Run("NoExisting", func(t *testing.T) { + t.Parallel() + require.Equal(t, + "AWS_SDK_UA_APP_ID=APN_1.1/pc_cdfmjwn8i6u8l9fwz8h82e4w3$", + awsSDKUserAgentEnv(""), + ) + }) + + t.Run("AppendToExisting", func(t *testing.T) { + t.Parallel() + // When the operator is themselves an AWS Partner and has set their own + // Application ID, we append Coder's with a space delimiter so both + // attributions are preserved. See: + // https://docs.aws.amazon.com/PRM/latest/aws-prm-onboarding-guide/automated-user-agent.html + require.Equal(t, + "AWS_SDK_UA_APP_ID=EXISTING_APP_ID APN_1.1/pc_cdfmjwn8i6u8l9fwz8h82e4w3$", + awsSDKUserAgentEnv("EXISTING_APP_ID"), + ) + }) +} diff --git a/provisioner/terraform/serve_internal_test.go b/provisioner/terraform/serve_internal_test.go index c87ee30724e..ec93b49d464 100644 --- a/provisioner/terraform/serve_internal_test.go +++ b/provisioner/terraform/serve_internal_test.go @@ -44,7 +44,7 @@ func Test_absoluteBinaryPath(t *testing.T) { { name: "TestMalformedVersion", terraformVersion: "version", - expectedErr: xerrors.Errorf("Terraform binary get version failed: Malformed version: version"), + expectedErr: xerrors.Errorf("Terraform binary get version failed: malformed version: version"), }, } // nolint:paralleltest diff --git a/provisioner/terraform/testdata/generate.sh b/provisioner/terraform/testdata/generate.sh index 7eb396b2454..11b3d2c40a7 100755 --- a/provisioner/terraform/testdata/generate.sh +++ b/provisioner/terraform/testdata/generate.sh @@ -3,6 +3,11 @@ set -euo pipefail cd "$(dirname "${BASH_SOURCE[0]}")/resources" +# These environment variables influence the coder provider. +for v in $(env | grep -E '^CODER_' | cut -d= -f1); do + unset "$v" +done + generate() { local name="$1" diff --git a/provisioner/terraform/testdata/resources/ai-tasks-app/ai-tasks-app.tfplan.json b/provisioner/terraform/testdata/resources/ai-tasks-app/ai-tasks-app.tfplan.json index 2669980027b..aa379e9a7cd 100644 --- a/provisioner/terraform/testdata/resources/ai-tasks-app/ai-tasks-app.tfplan.json +++ b/provisioner/terraform/testdata/resources/ai-tasks-app/ai-tasks-app.tfplan.json @@ -41,6 +41,7 @@ "sidebar_app": [] }, "after_unknown": { + "enabled": true, "id": true, "prompt": true, "sidebar_app": [] @@ -81,11 +82,11 @@ "schema_version": 1, "values": { "access_port": 443, - "access_url": "https://dev.coder.com/", + "access_url": "https://mydeployment.coder.com", "id": "5c06d6ea-101b-4069-8d14-7179df66ebcc", "is_prebuild": false, "is_prebuild_claim": false, - "name": "coder", + "name": "default", "prebuild_count": 0, "start_count": 1, "template_id": "", @@ -104,7 +105,7 @@ "schema_version": 0, "values": { "email": "default@example.com", - "full_name": "coder", + "full_name": "default", "groups": [], "id": "8796d8d7-88f1-445a-bea7-65f5cf530b95", "login_type": null, diff --git a/provisioner/terraform/testdata/resources/ai-tasks-app/ai-tasks-app.tfstate.json b/provisioner/terraform/testdata/resources/ai-tasks-app/ai-tasks-app.tfstate.json index a883d214358..62d4e11ebc0 100644 --- a/provisioner/terraform/testdata/resources/ai-tasks-app/ai-tasks-app.tfstate.json +++ b/provisioner/terraform/testdata/resources/ai-tasks-app/ai-tasks-app.tfstate.json @@ -27,11 +27,11 @@ "schema_version": 1, "values": { "access_port": 443, - "access_url": "https://dev.coder.com/", + "access_url": "https://mydeployment.coder.com", "id": "bca94359-107b-43c9-a272-99af4b239aad", "is_prebuild": false, "is_prebuild_claim": false, - "name": "coder", + "name": "default", "prebuild_count": 0, "start_count": 1, "template_id": "", @@ -50,7 +50,7 @@ "schema_version": 0, "values": { "email": "default@example.com", - "full_name": "coder", + "full_name": "default", "groups": [], "id": "cb8c55f2-7f66-4e69-a584-eb08f4a7cf04", "login_type": null, @@ -79,8 +79,9 @@ "schema_version": 1, "values": { "app_id": "5ece4674-dd35-4f16-88c8-82e40e72e2fd", + "enabled": false, "id": "c4f032b8-97e4-42b0-aa2f-30a9e698f8d4", - "prompt": "default", + "prompt": null, "sidebar_app": [] }, "sensitive_values": { diff --git a/provisioner/terraform/testdata/resources/ai-tasks-multiple/ai-tasks-multiple.tfplan.json b/provisioner/terraform/testdata/resources/ai-tasks-multiple/ai-tasks-multiple.tfplan.json index f83c8646d7a..6aec7e328ba 100644 --- a/provisioner/terraform/testdata/resources/ai-tasks-multiple/ai-tasks-multiple.tfplan.json +++ b/provisioner/terraform/testdata/resources/ai-tasks-multiple/ai-tasks-multiple.tfplan.json @@ -66,6 +66,7 @@ }, "after_unknown": { "app_id": true, + "enabled": true, "id": true, "prompt": true, "sidebar_app": [ @@ -97,6 +98,7 @@ "sidebar_app": [] }, "after_unknown": { + "enabled": true, "id": true, "prompt": true, "sidebar_app": [] @@ -137,11 +139,11 @@ "schema_version": 1, "values": { "access_port": 443, - "access_url": "https://dev.coder.com/", + "access_url": "https://mydeployment.coder.com", "id": "344575c1-55b9-43bb-89b5-35f547e2cf08", "is_prebuild": false, "is_prebuild_claim": false, - "name": "sebenza-nonix", + "name": "default", "prebuild_count": 0, "start_count": 1, "template_id": "", @@ -173,7 +175,9 @@ }, "sensitive_values": { "groups": [], + "oidc_access_token": true, "rbac_roles": [], + "session_token": true, "ssh_private_key": true } } diff --git a/provisioner/terraform/testdata/resources/ai-tasks-multiple/ai-tasks-multiple.tfstate.json b/provisioner/terraform/testdata/resources/ai-tasks-multiple/ai-tasks-multiple.tfstate.json index d97cffd4572..0c9a9224d50 100644 --- a/provisioner/terraform/testdata/resources/ai-tasks-multiple/ai-tasks-multiple.tfstate.json +++ b/provisioner/terraform/testdata/resources/ai-tasks-multiple/ai-tasks-multiple.tfstate.json @@ -27,11 +27,11 @@ "schema_version": 1, "values": { "access_port": 443, - "access_url": "https://dev.coder.com/", + "access_url": "https://mydeployment.coder.com", "id": "b6713709-6736-4d2f-b3da-7b5b242df5f4", "is_prebuild": false, "is_prebuild_claim": false, - "name": "sebenza-nonix", + "name": "default", "prebuild_count": 0, "start_count": 1, "template_id": "", @@ -63,7 +63,9 @@ }, "sensitive_values": { "groups": [], + "oidc_access_token": true, "rbac_roles": [], + "session_token": true, "ssh_private_key": true } }, @@ -77,8 +79,9 @@ "schema_version": 1, "values": { "app_id": "5ece4674-dd35-4f16-88c8-82e40e72e2fd", + "enabled": false, "id": "89e6ab36-2e98-4d13-9b4c-69b7588b7e1d", - "prompt": "default", + "prompt": null, "sidebar_app": [ { "id": "5ece4674-dd35-4f16-88c8-82e40e72e2fd" @@ -101,8 +104,9 @@ "schema_version": 1, "values": { "app_id": "5ece4674-dd35-4f16-88c8-82e40e72e2fd", + "enabled": false, "id": "5ece4674-dd35-4f16-88c8-82e40e72e2fd", - "prompt": "default", + "prompt": null, "sidebar_app": [] }, "sensitive_values": { diff --git a/provisioner/terraform/testdata/resources/ai-tasks-sidebar/ai-tasks-sidebar.tfplan.json b/provisioner/terraform/testdata/resources/ai-tasks-sidebar/ai-tasks-sidebar.tfplan.json index 6a507463d12..db05edfa166 100644 --- a/provisioner/terraform/testdata/resources/ai-tasks-sidebar/ai-tasks-sidebar.tfplan.json +++ b/provisioner/terraform/testdata/resources/ai-tasks-sidebar/ai-tasks-sidebar.tfplan.json @@ -50,6 +50,7 @@ }, "after_unknown": { "app_id": true, + "enabled": true, "id": true, "prompt": true, "sidebar_app": [ @@ -94,11 +95,11 @@ "schema_version": 1, "values": { "access_port": 443, - "access_url": "https://dev.coder.com/", + "access_url": "https://mydeployment.coder.com", "id": "344575c1-55b9-43bb-89b5-35f547e2cf08", "is_prebuild": false, "is_prebuild_claim": false, - "name": "sebenza-nonix", + "name": "default", "prebuild_count": 0, "start_count": 1, "template_id": "", @@ -130,7 +131,9 @@ }, "sensitive_values": { "groups": [], + "oidc_access_token": true, "rbac_roles": [], + "session_token": true, "ssh_private_key": true } } diff --git a/provisioner/terraform/testdata/resources/ai-tasks-sidebar/ai-tasks-sidebar.tfstate.json b/provisioner/terraform/testdata/resources/ai-tasks-sidebar/ai-tasks-sidebar.tfstate.json index 947e3ee1e94..74de9a93d9a 100644 --- a/provisioner/terraform/testdata/resources/ai-tasks-sidebar/ai-tasks-sidebar.tfstate.json +++ b/provisioner/terraform/testdata/resources/ai-tasks-sidebar/ai-tasks-sidebar.tfstate.json @@ -27,11 +27,11 @@ "schema_version": 1, "values": { "access_port": 443, - "access_url": "https://dev.coder.com/", + "access_url": "https://mydeployment.coder.com", "id": "b6713709-6736-4d2f-b3da-7b5b242df5f4", "is_prebuild": false, "is_prebuild_claim": false, - "name": "sebenza-nonix", + "name": "default", "prebuild_count": 0, "start_count": 1, "template_id": "", @@ -63,7 +63,9 @@ }, "sensitive_values": { "groups": [], + "oidc_access_token": true, "rbac_roles": [], + "session_token": true, "ssh_private_key": true } }, @@ -77,8 +79,9 @@ "schema_version": 1, "values": { "app_id": "5ece4674-dd35-4f16-88c8-82e40e72e2fd", + "enabled": false, "id": "89e6ab36-2e98-4d13-9b4c-69b7588b7e1d", - "prompt": "default", + "prompt": null, "sidebar_app": [ { "id": "5ece4674-dd35-4f16-88c8-82e40e72e2fd" diff --git a/provisioner/terraform/testdata/resources/external-agents/external-agents.tfplan.json b/provisioner/terraform/testdata/resources/external-agents/external-agents.tfplan.json index 3d085a535b2..47e8702ac74 100644 --- a/provisioner/terraform/testdata/resources/external-agents/external-agents.tfplan.json +++ b/provisioner/terraform/testdata/resources/external-agents/external-agents.tfplan.json @@ -147,11 +147,11 @@ "schema_version": 1, "values": { "access_port": 443, - "access_url": "https://dev.coder.com/", + "access_url": "https://mydeployment.coder.com", "id": "0b7fc772-5e27-4096-b8a3-9e6a8b914ebe", "is_prebuild": false, "is_prebuild_claim": false, - "name": "kacper", + "name": "default", "prebuild_count": 0, "start_count": 1, "template_id": "", @@ -170,7 +170,7 @@ "schema_version": 0, "values": { "email": "default@example.com", - "full_name": "kacpersaw", + "full_name": "default", "groups": [], "id": "1ebd1795-7cf2-47c5-8024-5d56e68f1681", "login_type": null, diff --git a/provisioner/terraform/testdata/resources/external-agents/external-agents.tfstate.json b/provisioner/terraform/testdata/resources/external-agents/external-agents.tfstate.json index af884a315ec..4574516636a 100644 --- a/provisioner/terraform/testdata/resources/external-agents/external-agents.tfstate.json +++ b/provisioner/terraform/testdata/resources/external-agents/external-agents.tfstate.json @@ -27,11 +27,11 @@ "schema_version": 1, "values": { "access_port": 443, - "access_url": "https://dev.coder.com/", + "access_url": "https://mydeployment.coder.com", "id": "dfa1dbe8-ad31-410b-b201-a4ed4d884938", "is_prebuild": false, "is_prebuild_claim": false, - "name": "kacper", + "name": "default", "prebuild_count": 0, "start_count": 1, "template_id": "", @@ -50,7 +50,7 @@ "schema_version": 0, "values": { "email": "default@example.com", - "full_name": "kacpersaw", + "full_name": "default", "groups": [], "id": "f5e82b90-ea22-4288-8286-9cf7af651143", "login_type": null, diff --git a/provisioner/terraform/testdata/resources/external-auth-providers/external-auth-providers.tfplan.json b/provisioner/terraform/testdata/resources/external-auth-providers/external-auth-providers.tfplan.json index 696a7ee61f2..c954cdd71ed 100644 --- a/provisioner/terraform/testdata/resources/external-auth-providers/external-auth-providers.tfplan.json +++ b/provisioner/terraform/testdata/resources/external-auth-providers/external-auth-providers.tfplan.json @@ -136,7 +136,9 @@ "id": "github", "optional": null }, - "sensitive_values": {} + "sensitive_values": { + "access_token": true + } }, { "address": "data.coder_external_auth.gitlab", @@ -150,7 +152,9 @@ "id": "gitlab", "optional": true }, - "sensitive_values": {} + "sensitive_values": { + "access_token": true + } } ] } diff --git a/provisioner/terraform/testdata/resources/external-auth-providers/external-auth-providers.tfstate.json b/provisioner/terraform/testdata/resources/external-auth-providers/external-auth-providers.tfstate.json index 35e407dff46..0f5016503a9 100644 --- a/provisioner/terraform/testdata/resources/external-auth-providers/external-auth-providers.tfstate.json +++ b/provisioner/terraform/testdata/resources/external-auth-providers/external-auth-providers.tfstate.json @@ -16,7 +16,9 @@ "id": "github", "optional": null }, - "sensitive_values": {} + "sensitive_values": { + "access_token": true + } }, { "address": "data.coder_external_auth.gitlab", @@ -30,7 +32,9 @@ "id": "gitlab", "optional": true }, - "sensitive_values": {} + "sensitive_values": { + "access_token": true + } }, { "address": "coder_agent.main", diff --git a/provisioner/terraform/testdata/resources/mapped-apps/mapped-apps.tfplan.json b/provisioner/terraform/testdata/resources/mapped-apps/mapped-apps.tfplan.json index 7a16a0c8bbe..d7aa2a899d7 100644 --- a/provisioner/terraform/testdata/resources/mapped-apps/mapped-apps.tfplan.json +++ b/provisioner/terraform/testdata/resources/mapped-apps/mapped-apps.tfplan.json @@ -56,6 +56,7 @@ "share": "owner", "slug": "app1", "subdomain": null, + "tooltip": null, "url": null }, "sensitive_values": { @@ -83,6 +84,7 @@ "share": "owner", "slug": "app2", "subdomain": null, + "tooltip": null, "url": null }, "sensitive_values": { @@ -175,6 +177,7 @@ "share": "owner", "slug": "app1", "subdomain": null, + "tooltip": null, "url": null }, "after_unknown": { @@ -213,6 +216,7 @@ "share": "owner", "slug": "app2", "subdomain": null, + "tooltip": null, "url": null }, "after_unknown": { diff --git a/provisioner/terraform/testdata/resources/mapped-apps/mapped-apps.tfstate.json b/provisioner/terraform/testdata/resources/mapped-apps/mapped-apps.tfstate.json index c45b6543497..bbac19aefac 100644 --- a/provisioner/terraform/testdata/resources/mapped-apps/mapped-apps.tfstate.json +++ b/provisioner/terraform/testdata/resources/mapped-apps/mapped-apps.tfstate.json @@ -72,6 +72,7 @@ "share": "owner", "slug": "app1", "subdomain": null, + "tooltip": null, "url": null }, "sensitive_values": { @@ -104,6 +105,7 @@ "share": "owner", "slug": "app2", "subdomain": null, + "tooltip": null, "url": null }, "sensitive_values": { diff --git a/provisioner/terraform/testdata/resources/multiple-agents-multiple-apps/multiple-agents-multiple-apps.tfplan.json b/provisioner/terraform/testdata/resources/multiple-agents-multiple-apps/multiple-agents-multiple-apps.tfplan.json index c6930602ed0..d00eab27fdb 100644 --- a/provisioner/terraform/testdata/resources/multiple-agents-multiple-apps/multiple-agents-multiple-apps.tfplan.json +++ b/provisioner/terraform/testdata/resources/multiple-agents-multiple-apps/multiple-agents-multiple-apps.tfplan.json @@ -86,6 +86,7 @@ "share": "owner", "slug": "app1", "subdomain": null, + "tooltip": null, "url": null }, "sensitive_values": { @@ -118,6 +119,7 @@ "share": "owner", "slug": "app2", "subdomain": true, + "tooltip": null, "url": null }, "sensitive_values": { @@ -146,6 +148,7 @@ "share": "owner", "slug": "app3", "subdomain": false, + "tooltip": null, "url": null }, "sensitive_values": { @@ -294,6 +297,7 @@ "share": "owner", "slug": "app1", "subdomain": null, + "tooltip": null, "url": null }, "after_unknown": { @@ -337,6 +341,7 @@ "share": "owner", "slug": "app2", "subdomain": true, + "tooltip": null, "url": null }, "after_unknown": { @@ -378,6 +383,7 @@ "share": "owner", "slug": "app3", "subdomain": false, + "tooltip": null, "url": null }, "after_unknown": { diff --git a/provisioner/terraform/testdata/resources/multiple-agents-multiple-apps/multiple-agents-multiple-apps.tfstate.json b/provisioner/terraform/testdata/resources/multiple-agents-multiple-apps/multiple-agents-multiple-apps.tfstate.json index 12a3dab0465..07b97376032 100644 --- a/provisioner/terraform/testdata/resources/multiple-agents-multiple-apps/multiple-agents-multiple-apps.tfstate.json +++ b/provisioner/terraform/testdata/resources/multiple-agents-multiple-apps/multiple-agents-multiple-apps.tfstate.json @@ -116,6 +116,7 @@ "share": "owner", "slug": "app1", "subdomain": null, + "tooltip": null, "url": null }, "sensitive_values": { @@ -153,6 +154,7 @@ "share": "owner", "slug": "app2", "subdomain": true, + "tooltip": null, "url": null }, "sensitive_values": { @@ -186,6 +188,7 @@ "share": "owner", "slug": "app3", "subdomain": false, + "tooltip": null, "url": null }, "sensitive_values": { diff --git a/provisioner/terraform/testdata/resources/multiple-agents-multiple-monitors/multiple-agents-multiple-monitors.tf b/provisioner/terraform/testdata/resources/multiple-agents-multiple-monitors/multiple-agents-multiple-monitors.tf index f86ceb180ed..075ebc6c249 100644 --- a/provisioner/terraform/testdata/resources/multiple-agents-multiple-monitors/multiple-agents-multiple-monitors.tf +++ b/provisioner/terraform/testdata/resources/multiple-agents-multiple-monitors/multiple-agents-multiple-monitors.tf @@ -2,7 +2,7 @@ terraform { required_providers { coder = { source = "coder/coder" - version = "2.2.0-pre0" + version = ">=2.2.0" } } } diff --git a/provisioner/terraform/testdata/resources/multiple-agents-multiple-monitors/multiple-agents-multiple-monitors.tfplan.json b/provisioner/terraform/testdata/resources/multiple-agents-multiple-monitors/multiple-agents-multiple-monitors.tfplan.json index ae850f57d13..462bafb0ffa 100644 --- a/provisioner/terraform/testdata/resources/multiple-agents-multiple-monitors/multiple-agents-multiple-monitors.tfplan.json +++ b/provisioner/terraform/testdata/resources/multiple-agents-multiple-monitors/multiple-agents-multiple-monitors.tfplan.json @@ -134,6 +134,7 @@ "share": "owner", "slug": "app1", "subdomain": null, + "tooltip": null, "url": null }, "sensitive_values": { @@ -166,6 +167,7 @@ "share": "owner", "slug": "app2", "subdomain": true, + "tooltip": null, "url": null }, "sensitive_values": { @@ -369,6 +371,7 @@ "share": "owner", "slug": "app1", "subdomain": null, + "tooltip": null, "url": null }, "after_unknown": { @@ -412,6 +415,7 @@ "share": "owner", "slug": "app2", "subdomain": true, + "tooltip": null, "url": null }, "after_unknown": { @@ -456,7 +460,7 @@ "coder": { "name": "coder", "full_name": "registry.terraform.io/coder/coder", - "version_constraint": "2.2.0-pre0" + "version_constraint": ">= 2.2.0" }, "null": { "name": "null", diff --git a/provisioner/terraform/testdata/resources/multiple-agents-multiple-monitors/multiple-agents-multiple-monitors.tfstate.json b/provisioner/terraform/testdata/resources/multiple-agents-multiple-monitors/multiple-agents-multiple-monitors.tfstate.json index 9e1f2abeb15..0e6b901be0e 100644 --- a/provisioner/terraform/testdata/resources/multiple-agents-multiple-monitors/multiple-agents-multiple-monitors.tfstate.json +++ b/provisioner/terraform/testdata/resources/multiple-agents-multiple-monitors/multiple-agents-multiple-monitors.tfstate.json @@ -164,6 +164,7 @@ "share": "owner", "slug": "app1", "subdomain": null, + "tooltip": null, "url": null }, "sensitive_values": { @@ -201,6 +202,7 @@ "share": "owner", "slug": "app2", "subdomain": true, + "tooltip": null, "url": null }, "sensitive_values": { diff --git a/provisioner/terraform/testdata/resources/multiple-apps/multiple-apps.tfplan.json b/provisioner/terraform/testdata/resources/multiple-apps/multiple-apps.tfplan.json index f6b271c6eaf..2732b13a084 100644 --- a/provisioner/terraform/testdata/resources/multiple-apps/multiple-apps.tfplan.json +++ b/provisioner/terraform/testdata/resources/multiple-apps/multiple-apps.tfplan.json @@ -55,6 +55,7 @@ "share": "owner", "slug": "app1", "subdomain": null, + "tooltip": null, "url": null }, "sensitive_values": { @@ -87,6 +88,7 @@ "share": "owner", "slug": "app2", "subdomain": true, + "tooltip": null, "url": null }, "sensitive_values": { @@ -115,6 +117,7 @@ "share": "owner", "slug": "app3", "subdomain": false, + "tooltip": null, "url": null }, "sensitive_values": { @@ -206,6 +209,7 @@ "share": "owner", "slug": "app1", "subdomain": null, + "tooltip": null, "url": null }, "after_unknown": { @@ -249,6 +253,7 @@ "share": "owner", "slug": "app2", "subdomain": true, + "tooltip": null, "url": null }, "after_unknown": { @@ -290,6 +295,7 @@ "share": "owner", "slug": "app3", "subdomain": false, + "tooltip": null, "url": null }, "after_unknown": { diff --git a/provisioner/terraform/testdata/resources/multiple-apps/multiple-apps.tfstate.json b/provisioner/terraform/testdata/resources/multiple-apps/multiple-apps.tfstate.json index 3f1473f6bdc..0386d070c86 100644 --- a/provisioner/terraform/testdata/resources/multiple-apps/multiple-apps.tfstate.json +++ b/provisioner/terraform/testdata/resources/multiple-apps/multiple-apps.tfstate.json @@ -71,6 +71,7 @@ "share": "owner", "slug": "app1", "subdomain": null, + "tooltip": null, "url": null }, "sensitive_values": { @@ -108,6 +109,7 @@ "share": "owner", "slug": "app2", "subdomain": true, + "tooltip": null, "url": null }, "sensitive_values": { @@ -141,6 +143,7 @@ "share": "owner", "slug": "app3", "subdomain": false, + "tooltip": null, "url": null }, "sensitive_values": { diff --git a/provisioner/terraform/testdata/resources/presets-multiple-defaults/presets-multiple-defaults.tfplan.json b/provisioner/terraform/testdata/resources/presets-multiple-defaults/presets-multiple-defaults.tfplan.json index 5be0935b3f6..04dfee5519d 100644 --- a/provisioner/terraform/testdata/resources/presets-multiple-defaults/presets-multiple-defaults.tfplan.json +++ b/provisioner/terraform/testdata/resources/presets-multiple-defaults/presets-multiple-defaults.tfplan.json @@ -162,6 +162,8 @@ "schema_version": 1, "values": { "default": true, + "description": null, + "icon": null, "id": "development", "name": "development", "parameters": { @@ -194,6 +196,8 @@ "schema_version": 1, "values": { "default": true, + "description": null, + "icon": null, "id": "production", "name": "production", "parameters": { diff --git a/provisioner/terraform/testdata/resources/presets-multiple-defaults/presets-multiple-defaults.tfstate.json b/provisioner/terraform/testdata/resources/presets-multiple-defaults/presets-multiple-defaults.tfstate.json index ccad929f2ad..a82e32b53ab 100644 --- a/provisioner/terraform/testdata/resources/presets-multiple-defaults/presets-multiple-defaults.tfstate.json +++ b/provisioner/terraform/testdata/resources/presets-multiple-defaults/presets-multiple-defaults.tfstate.json @@ -42,6 +42,8 @@ "schema_version": 1, "values": { "default": true, + "description": null, + "icon": null, "id": "development", "name": "development", "parameters": { @@ -74,6 +76,8 @@ "schema_version": 1, "values": { "default": true, + "description": null, + "icon": null, "id": "production", "name": "production", "parameters": { diff --git a/provisioner/terraform/testdata/resources/presets-single-default/presets-single-default.tfplan.json b/provisioner/terraform/testdata/resources/presets-single-default/presets-single-default.tfplan.json index 8c8bea87d8a..37907a5d218 100644 --- a/provisioner/terraform/testdata/resources/presets-single-default/presets-single-default.tfplan.json +++ b/provisioner/terraform/testdata/resources/presets-single-default/presets-single-default.tfplan.json @@ -162,6 +162,8 @@ "schema_version": 1, "values": { "default": true, + "description": null, + "icon": null, "id": "development", "name": "development", "parameters": { @@ -194,6 +196,8 @@ "schema_version": 1, "values": { "default": false, + "description": null, + "icon": null, "id": "production", "name": "production", "parameters": { diff --git a/provisioner/terraform/testdata/resources/presets-single-default/presets-single-default.tfstate.json b/provisioner/terraform/testdata/resources/presets-single-default/presets-single-default.tfstate.json index f871abdc20f..60ee5b9dc8b 100644 --- a/provisioner/terraform/testdata/resources/presets-single-default/presets-single-default.tfstate.json +++ b/provisioner/terraform/testdata/resources/presets-single-default/presets-single-default.tfstate.json @@ -42,6 +42,8 @@ "schema_version": 1, "values": { "default": true, + "description": null, + "icon": null, "id": "development", "name": "development", "parameters": { @@ -74,6 +76,8 @@ "schema_version": 1, "values": { "default": false, + "description": null, + "icon": null, "id": "production", "name": "production", "parameters": { diff --git a/provisioner/terraform/testdata/resources/presets/presets.tfplan.json b/provisioner/terraform/testdata/resources/presets/presets.tfplan.json index 7254a3d177d..a7e21a525be 100644 --- a/provisioner/terraform/testdata/resources/presets/presets.tfplan.json +++ b/provisioner/terraform/testdata/resources/presets/presets.tfplan.json @@ -162,6 +162,8 @@ "schema_version": 1, "values": { "default": false, + "description": null, + "icon": null, "id": "My First Project", "name": "My First Project", "parameters": { diff --git a/provisioner/terraform/testdata/resources/presets/presets.tfstate.json b/provisioner/terraform/testdata/resources/presets/presets.tfstate.json index 5d52e6f5f19..b9576b8899e 100644 --- a/provisioner/terraform/testdata/resources/presets/presets.tfstate.json +++ b/provisioner/terraform/testdata/resources/presets/presets.tfstate.json @@ -42,6 +42,8 @@ "schema_version": 1, "values": { "default": false, + "description": null, + "icon": null, "id": "My First Project", "name": "My First Project", "parameters": { diff --git a/provisioner/terraform/testdata/resources/version.txt b/provisioner/terraform/testdata/resources/version.txt index 80138e71466..de646d2fc11 100644 --- a/provisioner/terraform/testdata/resources/version.txt +++ b/provisioner/terraform/testdata/resources/version.txt @@ -1 +1 @@ -1.13.4 +1.16.6 diff --git a/provisioner/terraform/testdata/version.txt b/provisioner/terraform/testdata/version.txt index 80138e71466..de646d2fc11 100644 --- a/provisioner/terraform/testdata/version.txt +++ b/provisioner/terraform/testdata/version.txt @@ -1 +1 @@ -1.13.4 +1.16.6 diff --git a/provisionerd/provisionerd.go b/provisionerd/provisionerd.go index 707c69cde82..9cabba71eb2 100644 --- a/provisionerd/provisionerd.go +++ b/provisionerd/provisionerd.go @@ -532,7 +532,10 @@ func (p *Server) UploadModuleFiles(ctx context.Context, moduleFiles []byte) erro } defer stream.Close() - dataUp, chunks := sdkproto.BytesToDataUpload(sdkproto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, moduleFiles) + dataUp, chunks, err := sdkproto.BytesToDataUpload(sdkproto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, moduleFiles) + if err != nil { + return nil, xerrors.Errorf("prepare module files upload: %w", err) + } err = stream.Send(&proto.UploadFileRequest{Type: &proto.UploadFileRequest_DataUpload{DataUpload: dataUp}}) if err != nil { diff --git a/provisionersdk/proto/dataupload.go b/provisionersdk/proto/dataupload.go index e9b6d9ddfb0..f8832d3616d 100644 --- a/provisionersdk/proto/dataupload.go +++ b/provisionersdk/proto/dataupload.go @@ -9,7 +9,8 @@ import ( ) const ( - ChunkSize = 2 << 20 // 2 MiB + ChunkSize = 2 << 20 // 2 MiB + MaxFileSize = 10 * (10 << 20) // 100 MiB, matches coderd HTTPFileMaxBytes ) type DataBuilder struct { @@ -29,6 +30,21 @@ func NewDataBuilder(req *DataUpload) (*DataBuilder, error) { return nil, xerrors.Errorf("data hash must be 32 bytes, got %d bytes", len(req.DataHash)) } + if req.FileSize < 0 { + return nil, xerrors.Errorf("file size must not be negative, got %d", req.FileSize) + } + if req.FileSize > MaxFileSize { + return nil, xerrors.Errorf("file size %d exceeds maximum allowed %d", req.FileSize, MaxFileSize) + } + if req.Chunks < 0 { + return nil, xerrors.Errorf("chunk count must not be negative, got %d", req.Chunks) + } + //nolint:gosec // FileSize is validated to be <= MaxFileSize, well within int32 range + maxChunks := int32((req.FileSize + ChunkSize - 1) / ChunkSize) + if req.Chunks > maxChunks { + return nil, xerrors.Errorf("chunk count %d exceeds maximum %d for file size %d", req.Chunks, maxChunks, req.FileSize) + } + return &DataBuilder{ Type: req.UploadType, Hash: req.DataHash, @@ -60,7 +76,7 @@ func (b *DataBuilder) Add(chunk *ChunkPiece) (bool, error) { expectedSize := len(b.data) + len(chunk.Data) if expectedSize > int(b.Size) { return b.done(), xerrors.Errorf("data exceeds expected size, data is now %d bytes, %d bytes over the limit of %d", - expectedSize, b.Size-int64(expectedSize), b.Size) + expectedSize, int64(expectedSize)-b.Size, b.Size) } b.data = append(b.data, chunk.Data...) @@ -103,7 +119,11 @@ func (b *DataBuilder) done() bool { return b.chunkIndex >= b.ChunkCount } -func BytesToDataUpload(dataType DataUploadType, data []byte) (*DataUpload, []*ChunkPiece) { +func BytesToDataUpload(dataType DataUploadType, data []byte) (*DataUpload, []*ChunkPiece, error) { + if int64(len(data)) > MaxFileSize { + return nil, nil, xerrors.Errorf("data size %d exceeds maximum allowed %d", len(data), MaxFileSize) + } + fullHash := sha256.Sum256(data) //nolint:gosec // not going over int32 size := int32(len(data)) @@ -135,5 +155,5 @@ func BytesToDataUpload(dataType DataUploadType, data []byte) (*DataUpload, []*Ch chunks = append(chunks, chunk) } - return req, chunks + return req, chunks, nil } diff --git a/provisionersdk/proto/dataupload_test.go b/provisionersdk/proto/dataupload_test.go index 496a7956c9c..d8876240b0d 100644 --- a/provisionersdk/proto/dataupload_test.go +++ b/provisionersdk/proto/dataupload_test.go @@ -2,6 +2,7 @@ package proto_test import ( crand "crypto/rand" + "crypto/sha256" "math/rand" "testing" @@ -10,6 +11,101 @@ import ( "github.com/coder/coder/v2/provisionersdk/proto" ) +func TestNewDataBuilderValidation(t *testing.T) { + t.Parallel() + + validHash := sha256.Sum256([]byte{}) + + t.Run("ExactMaxFileSize", func(t *testing.T) { + t.Parallel() + builder, err := proto.NewDataBuilder(&proto.DataUpload{ + DataHash: validHash[:], + FileSize: proto.MaxFileSize, + Chunks: int32((proto.MaxFileSize + proto.ChunkSize - 1) / proto.ChunkSize), + UploadType: proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, + }) + require.NoError(t, err) + require.NotNil(t, builder) + }) + + t.Run("OversizedFileSize", func(t *testing.T) { + t.Parallel() + _, err := proto.NewDataBuilder(&proto.DataUpload{ + DataHash: validHash[:], + FileSize: proto.MaxFileSize + 1, + Chunks: 1, + UploadType: proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, + }) + require.ErrorContains(t, err, "exceeds maximum allowed") + }) + + t.Run("NegativeFileSize", func(t *testing.T) { + t.Parallel() + _, err := proto.NewDataBuilder(&proto.DataUpload{ + DataHash: validHash[:], + FileSize: -1, + Chunks: 1, + UploadType: proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, + }) + require.ErrorContains(t, err, "must not be negative") + }) + + t.Run("NegativeChunks", func(t *testing.T) { + t.Parallel() + _, err := proto.NewDataBuilder(&proto.DataUpload{ + DataHash: validHash[:], + FileSize: 100, + Chunks: -1, + UploadType: proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, + }) + require.ErrorContains(t, err, "chunk count must not be negative") + }) + + t.Run("ExcessiveChunkCount", func(t *testing.T) { + t.Parallel() + _, err := proto.NewDataBuilder(&proto.DataUpload{ + DataHash: validHash[:], + FileSize: 100, + Chunks: 1000, + UploadType: proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, + }) + require.ErrorContains(t, err, "chunk count 1000 exceeds maximum") + }) + + t.Run("ZeroFileSize", func(t *testing.T) { + t.Parallel() + builder, err := proto.NewDataBuilder(&proto.DataUpload{ + DataHash: validHash[:], + FileSize: 0, + Chunks: 0, + UploadType: proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, + }) + require.NoError(t, err) + require.True(t, builder.IsDone(), "zero-chunk upload should be immediately done") + }) + + t.Run("ValidRoundTrip", func(t *testing.T) { + t.Parallel() + data := make([]byte, 256) + _, _ = crand.Read(data) + + first, chunks, err := proto.BytesToDataUpload(proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, data) + require.NoError(t, err) + + builder, err := proto.NewDataBuilder(first) + require.NoError(t, err) + + for _, chunk := range chunks { + _, err = builder.Add(chunk) + require.NoError(t, err) + } + + got, err := builder.Complete() + require.NoError(t, err) + require.Equal(t, data, got) + }) +} + // Fuzz must be run manually with the `-fuzz` flag to generate random test cases. // By default, it only runs the added seed corpus cases. // go test -fuzz=FuzzBytesToDataUpload @@ -25,7 +121,11 @@ func FuzzBytesToDataUpload(f *testing.F) { } f.Fuzz(func(t *testing.T, data []byte) { - first, chunks := proto.BytesToDataUpload(proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, data) + first, chunks, err := proto.BytesToDataUpload(proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, data) + if err != nil { + // Data exceeds MaxFileSize, which is expected for large fuzz inputs. + return + } builder, err := proto.NewDataBuilder(first) require.NoError(t, err) @@ -62,7 +162,9 @@ func TestBytesToDataUpload(t *testing.T) { _, err := crand.Read(data) require.NoError(t, err) - first, chunks := proto.BytesToDataUpload(proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, data) + first, chunks, err := proto.BytesToDataUpload(proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, data) + require.NoError(t, err) + builder, err := proto.NewDataBuilder(first) require.NoError(t, err) diff --git a/provisionersdk/serve.go b/provisionersdk/serve.go index 3bac226e583..a6441678261 100644 --- a/provisionersdk/serve.go +++ b/provisionersdk/serve.go @@ -84,7 +84,7 @@ func Serve(ctx context.Context, server Server, options *ServeOptions) error { if err != nil { return xerrors.Errorf("register provisioner: %w", err) } - srv := drpcserver.NewWithOptions(&tracing.DRPCHandler{Handler: mux}, drpcserver.Options{ + srv := drpcsdk.NewServer(options.Logger, &tracing.DRPCHandler{Handler: mux}, drpcserver.Options{ Manager: drpcsdk.DefaultDRPCOptions(nil), }) diff --git a/provisionersdk/session.go b/provisionersdk/session.go index 59034a761e0..fa4e7eccb05 100644 --- a/provisionersdk/session.go +++ b/provisionersdk/session.go @@ -148,21 +148,24 @@ func (s *Session) handleRequests() error { s.Logger.Info(s.Context(), "plan response too large, sending modules as stream", slog.F("size_bytes", len(complete.ModuleFiles)), ) - dataUp, chunks := proto.BytesToDataUpload(proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, complete.ModuleFiles) - - complete.ModuleFiles = nil // sent over the stream - complete.ModuleFilesHash = dataUp.DataHash - resp.Type = &proto.Response_Plan{Plan: complete} - - err := s.stream.Send(&proto.Response{Type: &proto.Response_DataUpload{DataUpload: dataUp}}) + dataUp, chunks, err := proto.BytesToDataUpload(proto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, complete.ModuleFiles) if err != nil { - complete.Error = fmt.Sprintf("send data upload: %s", err.Error()) + complete.Error = fmt.Sprintf("prepare module files upload: %s", err.Error()) } else { - for i, chunk := range chunks { - err := s.stream.Send(&proto.Response{Type: &proto.Response_ChunkPiece{ChunkPiece: chunk}}) - if err != nil { - complete.Error = fmt.Sprintf("send data piece upload %d/%d: %s", i, dataUp.Chunks, err.Error()) - break + complete.ModuleFiles = nil // sent over the stream + complete.ModuleFilesHash = dataUp.DataHash + resp.Type = &proto.Response_Plan{Plan: complete} + + err := s.stream.Send(&proto.Response{Type: &proto.Response_DataUpload{DataUpload: dataUp}}) + if err != nil { + complete.Error = fmt.Sprintf("send data upload: %s", err.Error()) + } else { + for i, chunk := range chunks { + err := s.stream.Send(&proto.Response{Type: &proto.Response_ChunkPiece{ChunkPiece: chunk}}) + if err != nil { + complete.Error = fmt.Sprintf("send data piece upload %d/%d: %s", i, dataUp.Chunks, err.Error()) + break + } } } } diff --git a/scaletest/agentconn/run.go b/scaletest/agentconn/run.go index b0990d9cb11..ab6225e0a5a 100644 --- a/scaletest/agentconn/run.go +++ b/scaletest/agentconn/run.go @@ -214,7 +214,7 @@ func verifyConnection(ctx context.Context, logs io.Writer, conn workspacesdk.Age ctx, span := tracing.StartSpan(ctx) defer span.End() - client := agentHTTPClient(conn) + client := conn.AppHTTPClient() for i := 0; i < verifyConnectionAttempts; i++ { _, _ = fmt.Fprintf(logs, "\tVerify connection attempt %d/%d...\n", i+1, verifyConnectionAttempts) verifyCtx, cancel := context.WithTimeout(ctx, defaultRequestTimeout) @@ -258,7 +258,7 @@ func performInitialConnections(ctx context.Context, logs io.Writer, conn workspa defer span.End() _, _ = fmt.Fprintln(logs, "Performing initial service connections...") - client := agentHTTPClient(conn) + client := conn.AppHTTPClient() for i, connSpec := range specs { _, _ = fmt.Fprintf(logs, "\t%d. %s\n", i, connSpec.URL) @@ -292,7 +292,7 @@ func holdConnection(ctx context.Context, logs io.Writer, conn workspacesdk.Agent defer span.End() eg, egCtx := errgroup.WithContext(ctx) - client := agentHTTPClient(conn) + client := conn.AppHTTPClient() if len(specs) > 0 { _, _ = fmt.Fprintln(logs, "\nStarting connection loops...") } @@ -363,26 +363,3 @@ func holdConnection(ctx context.Context, logs io.Writer, conn workspacesdk.Agent return nil } - -func agentHTTPClient(conn workspacesdk.AgentConn) *http.Client { - return &http.Client{ - Transport: &http.Transport{ - DisableKeepAlives: true, - DialContext: func(ctx context.Context, _ string, addr string) (net.Conn, error) { - _, port, err := net.SplitHostPort(addr) - if err != nil { - return nil, xerrors.Errorf("split host port %q: %w", addr, err) - } - - portUint, err := strconv.ParseUint(port, 10, 16) - if err != nil { - return nil, xerrors.Errorf("parse port %q: %w", port, err) - } - - // Addr doesn't matter here, besides the port. DialContext will - // automatically choose the right IP to dial. - return conn.DialContext(ctx, "tcp", fmt.Sprintf("127.0.0.1:%d", portUint)) - }, - }, - } -} diff --git a/scripts/Dockerfile.base b/scripts/Dockerfile.base index da27f98a57d..24fac984f5d 100644 --- a/scripts/Dockerfile.base +++ b/scripts/Dockerfile.base @@ -1,7 +1,7 @@ # This is the base image used for Coder images. It's a multi-arch image that is # built in depot.dev for all supported architectures. Since it's built on real # hardware and not cross-compiled, it can have "RUN" commands. -FROM alpine:3.22.2@sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412 +FROM alpine:3.23.3@sha256:25109184c71bdad752c8312a8623239686a9a2071e8825f20acb8f2198c3f659 # We use a single RUN command to reduce the number of layers in the image. # NOTE: Keep the Terraform version in sync with minTerraformVersion and @@ -27,7 +27,7 @@ RUN apk add --no-cache \ # Terraform was disabled in the edge repo due to a build issue. # https://gitlab.alpinelinux.org/alpine/aports/-/commit/f3e263d94cfac02d594bef83790c280e045eba35 # Using wget for now. Note that busybox unzip doesn't support streaming. -RUN ARCH="$(arch)"; if [ "${ARCH}" == "x86_64" ]; then ARCH="amd64"; elif [ "${ARCH}" == "aarch64" ]; then ARCH="arm64"; elif [ "${ARCH}" == "armv7l" ]; then ARCH="arm"; fi; wget -O /tmp/terraform.zip "https://releases.hashicorp.com/terraform/1.13.4/terraform_1.13.4_linux_${ARCH}.zip" && \ +RUN ARCH="$(arch)"; if [ "${ARCH}" == "x86_64" ]; then ARCH="amd64"; elif [ "${ARCH}" == "aarch64" ]; then ARCH="arm64"; elif [ "${ARCH}" == "armv7l" ]; then ARCH="arm"; fi; wget -O /tmp/terraform.zip "https://releases.hashicorp.com/terraform/1.16.6/terraform_1.16.6_linux_${ARCH}.zip" && \ busybox unzip /tmp/terraform.zip -d /usr/local/bin && \ rm -f /tmp/terraform.zip && \ chmod +x /usr/local/bin/terraform && \ diff --git a/scripts/check_pg_schema.sh b/scripts/check_pg_schema.sh new file mode 100755 index 00000000000..994f94a9501 --- /dev/null +++ b/scripts/check_pg_schema.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +# This script checks that SQL files do not hardcode the "public" schema; +# they should rely on search_path instead to support deployments using +# non-public schemas. +# +# Usage: check_pg_schema.sh