Full Code of hashicorp/levant for AI

main 5492e67eeea9 cached
90 files
304.0 KB
104.9k tokens
196 symbols
1 requests
Download .txt
Showing preview only (326K chars total). Download the full file or copy to clipboard to get everything.
Repository: hashicorp/levant
Branch: main
Commit: 5492e67eeea9
Files: 90
Total size: 304.0 KB

Directory structure:
gitextract_ffa58g6j/

├── .dockerignore
├── .github/
│   ├── CODEOWNERS
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── actionlint.yml
│       ├── build.yml
│       ├── ci.yml
│       ├── nightly-release-readme.md
│       └── nightly-release.yml
├── .gitignore
├── .go-version
├── .golangci.yml
├── .release/
│   ├── ci.hcl
│   ├── levant-artifacts.hcl
│   ├── release-metadata.hcl
│   └── security-scan.hcl
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Dockerfile
├── GNUmakefile
├── LICENSE
├── README.md
├── client/
│   ├── consul.go
│   └── nomad.go
├── command/
│   ├── deploy.go
│   ├── deploy_test.go
│   ├── dispatch.go
│   ├── meta.go
│   ├── plan.go
│   ├── render.go
│   ├── scale_in.go
│   ├── scale_out.go
│   ├── test-fixtures/
│   │   ├── group_canary.nomad
│   │   ├── job_canary.nomad
│   │   └── periodic_batch.nomad
│   └── version.go
├── commands.go
├── docs/
│   ├── README.md
│   ├── clients.md
│   ├── commands.md
│   └── templates.md
├── go.mod
├── go.sum
├── helper/
│   ├── files.go
│   ├── files_test.go
│   ├── kvflag.go
│   ├── kvflag_test.go
│   ├── nomad/
│   │   ├── opts.go
│   │   └── opts_test.go
│   ├── variable.go
│   └── variable_test.go
├── levant/
│   ├── auto_revert.go
│   ├── deploy.go
│   ├── dispatch.go
│   ├── failure_inspector.go
│   ├── job_status_checker.go
│   ├── job_status_checker_test.go
│   ├── plan.go
│   └── structs/
│       └── config.go
├── logging/
│   └── logging.go
├── main.go
├── scale/
│   ├── scale.go
│   └── scale_test.go
├── scripts/
│   ├── docker-entrypoint.sh
│   └── version.sh
├── template/
│   ├── funcs.go
│   ├── render.go
│   ├── render_test.go
│   ├── template.go
│   └── test-fixtures/
│       ├── missing_var.nomad
│       ├── multi_templated.nomad
│       ├── none_templated.nomad
│       ├── single_templated.nomad
│       ├── test-overwrite.yaml
│       ├── test.tf
│       └── test.yaml
├── test/
│   ├── acctest/
│   │   ├── acctest.go
│   │   └── deploy.go
│   ├── deploy_test.go
│   └── fixtures/
│       ├── deploy_alloc_error.nomad
│       ├── deploy_basic.nomad
│       ├── deploy_canary.nomad
│       ├── deploy_count.nomad
│       ├── deploy_driver_error.nomad
│       ├── deploy_lifecycle.nomad
│       ├── deploy_namespace.nomad
│       └── deploy_task_scaling.nomad
└── version/
    ├── version.go
    └── version_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
./bin/
./pkg/
.git


================================================
FILE: .github/CODEOWNERS
================================================
# release configuration
/.release/                              @hashicorp/release-engineering
/.github/workflows/build.yml            @hashicorp/release-engineering


================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributing to Levant

**First** of all welcome and thank you for even considering to contribute to the Levant project. If you're unsure or afraid of anything, just ask or submit the issue or pull request anyways. You won't be yelled at for giving your best effort.

If you wish to work on Levant itself or any of its built-in components, you will first need [Go](https://golang.org/) installed on your machine (version 1.9+ is required) and ensure your [GOPATH](https://golang.org/doc/code.html#GOPATH) is correctly configured.

## Issues

Remember to craft your issues in such a way as to help others who might be facing similar challenges. Give your issues meaningful titles, that offer context. Please try to use complete sentences in your issue. Everything is okay to submit as an issue, even questions or ideas.

Not every contribution requires an issue, but all bugs and significant changes to core functionality do. A pull request to fix a bug or implement a core change will not be accepted without a corresponding bug report.

## What Good Issues Look Like

Levant includes a default issue template, please endeavor to provide as much information as possible.

1. **Avoid raising duplicate issues.** Please use the GitHub issue search feature to check whether your bug report or feature request has been mentioned in the past.

1. **Provide Bug Details.** When filing a bug report, include debug logs, version details and stack traces. Your issue should provide:
    1. Guidance on **how to reproduce the issue.**
    1. Tell us **what you expected to happen.**
    1. Tell us **what actually happened.**
    1. Tell us **what version of Levant and Nomad you're using.**

1. **Provide Feature Details.** When filing a feature request, include background on why you're requesting the feature and how it will be useful to others. If you have a design proposal to implement the feature, please include these details so the maintainers can provide feedback on the proposed approach.    

## Pull Requests

**All pull requests must include a description.** The description should at a minimum, provide background on the purpose of the pull request. Consider providing an overview of why the work is taking place; don’t assume familiarity with the history. If the pull request is related to an issue, make sure to mention the issue number(s).

Try to keep pull requests tidy, and be prepared for feedback. Everyone is welcome to contribute to Levant but we do try to keep a high quality of code standard. Be ready to face this. Feel free to open a pull request for anything, about anything. **Everyone** is welcome.

## Get Early Feedback

If you are contributing, do not feel the need to sit on your contribution until it is perfectly polished and complete. It helps everyone involved for you to seek feedback as early as you possibly can. Submitting an early, unfinished version of your contribution for feedback in no way prejudices your chances of getting that contribution accepted, and can save you from putting a lot of work into a contribution that is not suitable for the project.

## Code Review

Pull requests will not be merged until they've been code reviewed by at least one maintainer. You should implement any code review feedback unless you strongly object to it. In the event that you object to the code review feedback, you should make your case clearly and calmly. If, after doing so, the feedback is judged to still apply, you must either apply the feedback or withdraw your contribution.

# Tests and Checks

As Levant continues to mature, additional test harnesses will be implemented. Once these harnesses are in place, tests will be required for all bugs fixes and features. No exception.

## Linting

All Go code in your pull request must pass `lint` checks. You can run lint on all Golang files using the `make lint` target. All lint checks are automatically enforced by CI tests.

## Formatting

**Do your best to follow existing conventions you see in the codebase**, and ensure your code is formatted with `go fmt`. You can run `fmt` on all Golang files using the `make fmt` target. All format checks are automatically enforced by CI tests.

## Testing

Tests are required for all new functionality where practical; certain portions of Levant have no tests but this should be the exception and not the rule.

# Building

Levant is linted, tested and built using make:

```
make
```

The resulting binary file will be stored in the project root directory and is named `levant-local` which can be invoked as required. The binary is built by default for the host system only. If you wish to build for different architectures and operating systems please see the [golang docs](https://golang.org/doc/install/source) for the whole list of available `GOOS` and `GOARCH` values.


================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
**Description**

<!--
Briefly describe the problem you are having in a few paragraphs.
-->

**Relevant Nomad job specification file**

```
(paste your output here)
```

**Output of `levant version`:**

```
(paste your output here)
```

**Output of `consul version`:**

```
(paste your output here)
```

**Output of `nomad version`:**

```
(paste your output here)
```

**Additional environment details:**

**Debug log outputs from Levant:**

<!--
If the log fragment is particularly long, please paste it into a gist or
similar and paste the link here.
-->


================================================
FILE: .github/dependabot.yml
================================================
version: 2

updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "daily"

================================================
FILE: .github/workflows/actionlint.yml
================================================
# If the repository is public, be sure to change to GitHub hosted runners
name: Lint GitHub Actions Workflows
on:
  pull_request:
permissions:
  contents: read
jobs:
  actionlint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      - name: "Check workflow files"
        uses: docker://docker.mirror.hashicorp.services/rhysd/actionlint:latest


================================================
FILE: .github/workflows/build.yml
================================================
name: build
on:
  push:
  workflow_dispatch:
  workflow_call:

env:
  PKG_NAME: "levant"

jobs:
  get-go-version:
    name: "Determine Go toolchain version"
    runs-on: ubuntu-22.04
    outputs:
      go-version: ${{ steps.get-go-version.outputs.go-version }}
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      - name: Determine Go version
        id: get-go-version
        run: |
          echo "Building with Go $(cat .go-version)"
          echo "go-version=$(cat .go-version)" >> "$GITHUB_OUTPUT"

  get-product-version:
    runs-on: ubuntu-22.04
    outputs:
      product-version: ${{ steps.get-product-version.outputs.product-version }}
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      - name: get product version
        id: get-product-version
        run: |
          make version
          echo "product-version=$(make version)" >> "$GITHUB_OUTPUT"

  generate-metadata-file:
    needs: get-product-version
    runs-on: ubuntu-22.04
    outputs:
      filepath: ${{ steps.generate-metadata-file.outputs.filepath }}
    steps:
      - name: "Checkout directory"
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      - name: Generate metadata file
        id: generate-metadata-file
        uses: hashicorp/actions-generate-metadata@fdbc8803a0e53bcbb912ddeee3808329033d6357 # v1.1.1
        with:
          version: ${{ needs.get-product-version.outputs.product-version }}
          product: ${{ env.PKG_NAME }}
          repository: ${{ env.PKG_NAME }}
          repositoryOwner: "hashicorp"
      - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
        if: ${{ !env.ACT }}
        with:
          name: metadata.json
          path: ${{ steps.generate-metadata-file.outputs.filepath }}

  generate-ldflags:
    needs: get-product-version
    runs-on: ubuntu-22.04
    outputs:
      ldflags: ${{ steps.generate-ldflags.outputs.ldflags }}
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      - name: Generate ld flags
        id: generate-ldflags
        run: |
          echo "ldflags=-X 'github.com/hashicorp/levant/version.GitDescribe=v${{ needs.get-product-version.outputs.product-version }}'" >> "$GITHUB_OUTPUT"

  build:
    needs:
      - get-go-version
      - get-product-version
      - generate-ldflags
    runs-on: ubuntu-22.04
    strategy:
      matrix:
        goos: ["linux", "darwin", "windows", "freebsd"]
        goarch: [ "amd64", "arm64"]
      fail-fast: true
    name: Go ${{ needs.get-go-version.outputs.go-version }} ${{ matrix.goos }} ${{ matrix.goarch }} build
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      - name: Setup go
        uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
        with:
          go-version-file: ".go-version"
      - name: Build Levant
        env:
          GOOS: ${{ matrix.goos }}
          GOARCH: ${{ matrix.goarch }}
          GO_LDFLAGS: ${{ needs.generate-ldflags.outputs.ldflags }}
          CGO_ENABLED: "0"
          BIN_PATH: dist/levant
        uses: hashicorp/actions-go-build@37358f6098ef21b09542d84a9814ebb843aa4e3e # v1.0.0
        with:
          product_name: ${{ env.PKG_NAME }}
          product_version: ${{ needs.get-product-version.outputs.product-version }}
          go_version: ${{ needs.get-go-version.outputs.go-version }}
          os: ${{ matrix.goos }}
          arch: ${{ matrix.goarch }}
          reproducible: nope
          instructions: |-
            make crt
      - if: ${{ matrix.goos == 'linux' }}
        uses: hashicorp/actions-packaging-linux@8d55a640bb30b5508f16757ea908b274564792d4 # v1.7.0
        with:
          name: "levant"
          description: "Levant is a templating and deployment tool for HashiCorp Nomad"
          arch: ${{ matrix.goarch }}
          version: ${{ needs.get-product-version.outputs.product-version }}
          maintainer: "HashiCorp"
          homepage: "https://github.com/hashicorp/levant"
          license: "MPL-2.0"
          binary: "dist/levant"
          deb_depends: "openssl"
          rpm_depends: "openssl"
      - if: ${{ matrix.goos == 'linux' }}
        name: Determine package file names
        run: |
          echo "RPM_PACKAGE=$(basename out/*.rpm)" >> "$GITHUB_ENV"
          echo "DEB_PACKAGE=$(basename out/*.deb)" >> "$GITHUB_ENV"
      - if: ${{ matrix.goos == 'linux' }}
        uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
        with:
          name: ${{ env.RPM_PACKAGE }}
          path: out/${{ env.RPM_PACKAGE }}
          if-no-files-found: error
      - if: ${{ matrix.goos == 'linux' }}
        uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
        with:
          name: ${{ env.DEB_PACKAGE }}
          path: out/${{ env.DEB_PACKAGE }}
          if-no-files-found: error

  build-docker-default:
    needs:
      - get-product-version
      - build
    runs-on: ubuntu-22.04
    strategy:
      matrix:
        arch: ["arm64", "amd64"]
      fail-fast: true
    env:
      version: ${{ needs.get-product-version.outputs.product-version }}
    name: Docker ${{ matrix.arch }} default release build
    steps:
      - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
      - name: Docker Build (Action)
        uses: hashicorp/actions-docker-build@11d43ef520c65f58683d048ce9b47d6617893c9a # v2.0.0
        with:
          smoke_test: |
            TEST_VERSION="$(docker run "${IMAGE_NAME}" version | awk '/Levant/{print $2}')"
            if [ "${TEST_VERSION}" != "v${version}" ]; then
              echo "Test FAILED"
              exit 1
            fi
            echo "Test PASSED"
          version: ${{ needs.get-product-version.outputs.product-version }}
          target: release
          arch: ${{ matrix.arch }}
          tags: |
            docker.io/hashicorp/${{ env.PKG_NAME }}:${{ env.version }}
          dev_tags: |
            docker.io/hashicorppreview/${{ env.PKG_NAME }}:${{ env.version }}-dev
            docker.io/hashicorppreview/${{ env.PKG_NAME }}:${{ env.version }}-${{ github.sha }}

permissions:
  contents: read


================================================
FILE: .github/workflows/ci.yml
================================================
name: ci
on:
  push:
jobs:
  lint-go:
    runs-on: ubuntu-latest
    env:
      GO_TAGS: ''
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
        with:
          go-version-file: ".go-version"
      - name: Setup golangci-lint
        run: |-
          download=https://raw.githubusercontent.com/golangci/golangci-lint/9a8a056e9fe49c0e9ed2287aedce1022c79a115b/install.sh  # v1.52.2
          curl -sSf "$download" | sh -s v1.51.2
          ./bin/golangci-lint version
      - run: make check
  check-deps-go:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
        with:
          go-version-file: ".go-version"
      - run: make check-mod
  test-go:
    runs-on: ubuntu-latest
    env:
      GO_TAGS: ''
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
        with:
          go-version-file: ".go-version"
      - run: make test
  build-go:
    runs-on: ubuntu-latest
    env:
      GO_TAGS: ''
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
        with:
          go-version-file: ".go-version"
      - run: make dev
permissions:
  contents: read


================================================
FILE: .github/workflows/nightly-release-readme.md
================================================
Nightly releases are snapshots of the development activity on the Levant project that may include new features and bug fixes scheduled for upcoming releases. These releases are made available to make it easier for users to test their existing build configurations against the latest Levant code base for potential issues or to experiment with new features, with a chance to provide feedback on ways to improve the changes before being released.

As these releases are snapshots of the latest code, you may encounter an issue compared to the latest stable release. Users are encouraged to run nightly releases in a non production environment. If you encounter an issue, please check our [issue tracker](https://github.com/hashicorp/levant/issues) to see if the issue has already been reported; if a report hasn't been made, please report it so we can review the issue and make any needed fixes.

**Note**: Nightly releases are only available via GitHub Releases, and artifacts are not codesigned or notarized. Distribution via other [Release Channels](https://www.hashicorp.com/official-release-channels) such as the Releases Site or Homebrew is not yet supported.


================================================
FILE: .github/workflows/nightly-release.yml
================================================
# This GitHub action triggers a fresh set of Levant builds and publishes them
# to GitHub Releases under the `nightly` tag.
# Note that artifacts available via GitHub Releases are not codesigned or
# notarized.
# Failures are reported to slack.
name: Nightly Release

on:
  schedule:
    # Runs against the default branch every day overnight
    - cron: "18 3 * * *"

  workflow_dispatch:

jobs:
  # Build a fresh set of artifacts
  build-artifacts:
    uses: ./.github/workflows/build.yml

  github-release:
    needs: build-artifacts
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2

      - name: Download built artifacts
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          path: out/

      # Set BUILD_OUTPUT_LIST to out\<project>-<version>.<fileext>\*,out\...
      # This is needed to attach the build artifacts to the GitHub Release
      - name: Set BUILD_OUTPUT_LIST
        run: |
          (ls -xm1 out/) > tmp.txt
          sed 's:.*:out/&/*:' < tmp.txt > tmp2.txt
          echo "BUILD_OUTPUT_LIST=$(tr '\n' ',' < tmp2.txt | perl -ple 'chop')" >> "$GITHUB_ENV"
          rm -rf tmp.txt && rm -rf tmp2.txt

      - name: Advance nightly tag
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            try {
                await github.rest.git.deleteRef({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  ref: "tags/nightly"
                })
            } catch (e) {
              console.log("Warning: The nightly tag doesn't exist yet, so there's nothing to do. Trace: " + e)
            }
            await github.rest.git.createRef({
              owner: context.repo.owner,
              repo: context.repo.repo,
              ref: "refs/tags/nightly",
              sha: context.sha
            })

      # This will create a new GitHub Release called `nightly`
      # If a release with this name already exists, it will overwrite the existing data
      - name: Create a nightly GitHub prerelease
        id: create_prerelease
        uses: ncipollo/release-action@440c8c1cb0ed28b9f43e4d1d670870f059653174 # v1.16.0
        with:
          name: nightly
          artifacts: "${{ env.BUILD_OUTPUT_LIST }}"
          tag: nightly
          bodyFile: ".github/workflows/nightly-release-readme.md"
          prerelease: true
          allowUpdates: true
          removeArtifacts: true
          draft: false
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Publish nightly GitHub prerelease
        uses: eregon/publish-release@01df127f5e9a3c26935118e22e738d95b59d10ce # v1.0.6
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          release_id: ${{ steps.create_prerelease.outputs.id }}

# Send a slack notification if either job defined above fails
  slack-notify:
    needs:
      - build-artifacts
      - github-release
    if: always() && (needs.build-artifacts.result == 'failure' || needs.github-release.result == 'failure')
    runs-on: ubuntu-latest
    steps:
      - name: Notify Slack on Nightly Release Failure
        uses: hashicorp/actions-slack-status@1a3f63b30bd476aee1f3bd6f9d8f2aacc4f14d81 # v2.0.1
        with:
          failure-message: |-
            :x::moon::nomad-sob: Levant Nightly Release *FAILED* on
          status: failure
          slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}

permissions:
  contents: write


================================================
FILE: .gitignore
================================================
.DS_Store

# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so

# Folders
_obj
_test
dist/

# Build output directory.
/bin
/pkg

# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out

*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*

_testmain.go

*.exe
*.test
*.prof

/pkg
levant-local

.idea
vendor/*/.hg/*

# assets download path when using bob CLI
.bob


================================================
FILE: .go-version
================================================
1.24.4


================================================
FILE: .golangci.yml
================================================
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

run:
  deadline: 5m
  issues-exit-code: 1
  tests: true

output:
  formats: colored-line-number
  print-issued-lines: true
  print-linter-name: true

linters:
  enable:
    - errcheck
    - goconst
    - gocyclo
    - gofmt
    - goimports
    - gosec
    - govet
    - ineffassign
    - misspell
    - prealloc
    - revive
    - unconvert
    - unparam
    - unused
  enable-all: false
  disable-all: true


================================================
FILE: .release/ci.hcl
================================================
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

schema = "1"

project "levant" {
  team = "nomad"

  slack {
    notification_channel = "C03B5EWFW01"
  }

  github {
    organization = "hashicorp"
    repository   = "levant"

    release_branches = [
      "main",
      "release/**",
    ]
  }
}

event "build" {
  action "build" {
    organization = "hashicorp"
    repository   = "levant"
    workflow     = "build"
  }
}

event "prepare" {
  depends = ["build"]

  action "prepare" {
    organization = "hashicorp"
    repository   = "crt-workflows-common"
    workflow     = "prepare"
    depends      = ["build"]
  }

  notification {
    on = "always"
  }
}

## These are promotion and post-publish events
## they should be added to the end of the file after the verify event stanza.

event "trigger-staging" {
  // This event is dispatched by the bob trigger-promotion command
  // and is required - do not delete.
}

event "promote-staging" {
  depends = ["trigger-staging"]

  action "promote-staging" {
    organization = "hashicorp"
    repository   = "crt-workflows-common"
    workflow     = "promote-staging"
    config       = "release-metadata.hcl"
  }

  notification {
    on = "always"
  }
}

event "promote-staging-docker" {
  depends = ["promote-staging"]

  action "promote-staging-docker" {
    organization = "hashicorp"
    repository   = "crt-workflows-common"
    workflow     = "promote-staging-docker"
  }

  notification {
    on = "always"
  }
}

event "trigger-production" {
  // This event is dispatched by the bob trigger-promotion command
  // and is required - do not delete.
}

event "promote-production" {
  depends = ["trigger-production"]

  action "promote-production" {
    organization = "hashicorp"
    repository   = "crt-workflows-common"
    workflow     = "promote-production"
  }

  notification {
    on = "always"
  }
}

event "promote-production-docker" {
  depends = ["promote-production"]

  action "promote-production-docker" {
    organization = "hashicorp"
    repository   = "crt-workflows-common"
    workflow     = "promote-production-docker"
  }

  notification {
    on = "always"
  }
}

event "promote-production-packaging" {
  depends = ["promote-production-docker"]

  action "promote-production-packaging" {
    organization = "hashicorp"
    repository   = "crt-workflows-common"
    workflow     = "promote-production-packaging"
  }

  notification {
    on = "always"
  }
}


================================================
FILE: .release/levant-artifacts.hcl
================================================
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

schema = 1
artifacts {
  zip = [
    "levant_${version}_darwin_amd64.zip",
    "levant_${version}_darwin_arm64.zip",
    "levant_${version}_freebsd_amd64.zip",
    "levant_${version}_freebsd_arm64.zip",
    "levant_${version}_linux_amd64.zip",
    "levant_${version}_linux_arm64.zip",
    "levant_${version}_windows_amd64.zip",
    "levant_${version}_windows_arm64.zip",
  ]
  rpm = [
    "levant-${version_linux}-1.aarch64.rpm",
    "levant-${version_linux}-1.x86_64.rpm",
  ]
  deb = [
    "levant_${version_linux}-1_amd64.deb",
    "levant_${version_linux}-1_arm64.deb",
  ]
  container = [
    "levant_release_linux_amd64_${version}_${commit_sha}.docker.dev.tar",
    "levant_release_linux_amd64_${version}_${commit_sha}.docker.tar",
    "levant_release_linux_arm64_${version}_${commit_sha}.docker.dev.tar",
    "levant_release_linux_arm64_${version}_${commit_sha}.docker.tar",
  ]
}


================================================
FILE: .release/release-metadata.hcl
================================================
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

url_docker_registry_dockerhub = "https://hub.docker.com/r/hashicorp/levant"
url_license                   = "https://github.com/hashicorp/levant/blob/main/LICENSE"
url_source_repository         = "https://github.com/hashicorp/levant"


================================================
FILE: .release/security-scan.hcl
================================================
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

container {
  dependencies    = true
  alpine_security = true

  secrets {
    all = true
  }

  # Triage items that are _safe_ to ignore here. Note that this list should be
  # periodically cleaned up to remove items that are no longer found by the scanner.
  triage {
    suppress {
      vulnerabilities = [
        "GHSA-rx97-6c62-55mf", // https://github.com/github/advisory-database/pull/5759 TODO(dduzgun): remove when dep updated.
        "CVE-2025-46394",      // busybox@1.37.0-r18 TODO(dduzgun): remove when dep updated.
        "CVE-2024-58251",      // busybox@1.37.0-r18 TODO(dduzgun): remove when dep updated.
      ]
    }
  }
}

binary {
  go_modules = true
  osv        = true
  go_stdlib  = true
  oss_index  = false
  nvd        = false

  secrets {
    all = true
  }

  # Triage items that are _safe_ to ignore here. Note that this list should be
  # periodically cleaned up to remove items that are no longer found by the scanner.
  triage {
    suppress {
      vulnerabilities = [
        "GHSA-rx97-6c62-55mf", // https://github.com/github/advisory-database/pull/5759 TODO(dduzgun): remove when dep updated.
        "CVE-2025-46394",      // busybox@1.37.0-r18 TODO(dduzgun): remove when dep updated.
        "CVE-2024-58251",      // busybox@1.37.0-r18 TODO(dduzgun): remove when dep updated.
      ]
    }
  }
}


================================================
FILE: CHANGELOG.md
================================================
## 0.4.0 (June 26, 2025)

__BACKWARDS INCOMPATIBILITIES:__
* cli: Levant no longer supports the deprecated Vault token workflow.

IMPROVEMENT:
* build: Now builds with Go v1.24
* deps: Updated Nomad API to v1.10.2

## 0.3.3 (October 5, 2023)

IMPROVEMENTS:
* build: Now builds with Go v1.21.1 [[GH-507](https://github.com/hashicorp/levant/pull/507)]
* deps: Updated Nomad dependency to v1.6.2 [[GH-507](https://github.com/hashicorp/levant/pull/507)]

## 0.3.2 (October 20, 2022)

IMPROVEMENTS:
 * build: Now builds with go v1.19.1 [[GH-464](https://github.com/hashicorp/levant/pull/464)]
 * deps: Updated Nomad dependency to v1.4.1. [[GH-464](https://github.com/hashicorp/levant/pull/464)]
 * deps: Updated golang.org/x/text to v0.4.0 [[GH-465](https://github.com/hashicorp/levant/pull/465)]

## 0.3.1 (February 14, 2022)

IMPROVEMENTS:
* build: Updated Nomad dependency to 1.2.4. [[GH-438](https://github.com/hashicorp/levant/pull/438)]

## 0.3.0 (March 09, 2021)

__BACKWARDS INCOMPATIBILITIES:__
 * template: existing Levant functions that share a name with [sprig](https://github.com/Masterminds/sprig) functions have been renamed to include the prefix `levant` such as `levantEnv`.

BUG FIXES:
 * cli: Fixed panic when dispatching a job. [[GH-348](https://github.com/hashicorp/levant/pull/348)]
 * status-checker: Pass the namespace to the query options when calling the Nomad API [[GH-356](https://github.com/hashicorp/levant/pull/356)]
 * template: Fixed issue with default variables file not being used. [[GH-353](https://github.com/hashicorp/levant/pull/353)]

IMPROVEMENTS:
 * build: Updated Nomad dependency to 1.0.4. [[GH-399](https://github.com/hashicorp/levant/pull/399)]
 * cli: Added `log-level` and `log-format` flags to render command. [[GH-346](https://github.com/hashicorp/levant/pull/346)]
 * render: when rendering, send logging to stderr if stdout is not a terminal [[GH-386](https://github.com/hashicorp/levant/pull/386)]
 * template: Added [sprig](https://github.com/Masterminds/sprig) template functions. [[GH-347](https://github.com/hashicorp/levant/pull/347)]
 * template: Added `spewDump` and `spewPrintf` functions for easier debugging. [[GH-344](https://github.com/hashicorp/levant/pull/344)]

## 0.2.9 (27 December 2019)

IMPROVEMENTS:
 * Update vendoered version of Nomad to 0.9.6 [GH-313](https://github.com/jrasell/levant/pull/313)
 * Update to go 1.13 and use modules rather than dep [GH-319](https://github.com/jrasell/levant/pull/319)
 * Remove use of vendor nomad/structs import to allow easier vendor [GH-320](https://github.com/jrasell/levant/pull/320)
 * Add template replace function [GH-291](https://github.com/jrasell/levant/pull/291)

BUG FIXES:
 * Use info level logs when no changes are detected [GH-303](https://github.com/jrasell/levant/pull/303)

## 0.2.8 (14 September 2019)

IMPROVEMENTS:
 * Add `-force` flag to deploy CLI command which allows for forcing a deployment even if Levant detects 0 changes on plan [GH-296](https://github.com/jrasell/levant/pull/296)

BUG FIXES:
 * Fix segfault when logging deployID details [GH-286](https://github.com/jrasell/levant/pull/286)
 * Fix error message within scale-in which incorrectly referenced scale-out [GH-285](https://github.com/jrasell/levant/pull/285/files)

## 0.2.7 (19 March 2019)

IMPROVEMENTS:
 * Use `missingkey=zero` rather than error which allows better use of standard go templating, particulary conditionals [GH-275](https://github.com/jrasell/levant/pull/275)
 * Added maths functions add, subtract, multiply, divide and modulo to the template rendering process [GH-277](https://github.com/jrasell/levant/pull/277)

## 0.2.6 (25 February 2019)

IMPROVEMENTS:
 * Add the ability to supply a Vault token to a job during deployment via either a `vault` or `vault-token` flag [GH-258](https://github.com/jrasell/levant/pull/258)
 * New `fileContents` template function which allows the entire contents of a file to be read into the template [GH-261](https://github.com/jrasell/levant/pull/261)

BUG FIXES:
 * Fix a panic when running scale* deployment watcher due to incorrectly initialized client config [GH-253](https://github.com/jrasell/levant/pull/253)
 * Fix incorrect behavior when flag `ignore-no-changes` was set [GH-264](https://github.com/jrasell/levant/pull/264)
 * Fix endless deployment loop when Nomad doesn't return a deployment ID [GH-268](https://github.com/jrasell/levant/pull/268)

## 0.2.5 (25 October 2018)

BUG FIXES:
 * Fix panic in deployment where count is not specified due to unsafe count checking on task groups [GH-249](https://github.com/jrasell/levant/pull/249)

## 0.2.4 (24 October 2018)

BUG FIXES:
 * Fix panic in scale commands due to an incorrectly initialized configuration struct [GH-244](https://github.com/jrasell/levant/pull/244)
 * Fix bug where job deploys with taskgroup counts of 0 would hang for 1 hour [GH-246](https://github.com/jrasell/levant/pull/246)

## 0.2.3 (2 October 2018)

IMPROVEMENTS:
 * New `env` template function allows the lookup and substitution of variables by environemnt variables [GH-225](https://github.com/jrasell/levant/pull/225)
 * Add plan command to allow running a plan whilst using templating [GH-234](https://github.com/jrasell/levant/pull/234)
 * Add `toUpper` and `toLower` template funcs [GH-237](https://github.com/jrasell/levant/pull/237)

## 0.2.2 (6 August 2018)

BUG FIXES:
 * Fix an issue where if an evaluation had filtered nodes Levant would exit immediately rather than tracking the deployment which could still succeed [GH-221](https://github.com/jrasell/levant/pull/221)
 * Fixed failure inspector to report on tasks that are restarting [GH-82](https://github.com/jrasell/levant/pull/82)

## 0.2.1 (20 July 2018)

IMPROVEMENTS:
 * JSON can now be used as a variable file format [GH-210](https://github.com/jrasell/levant/pull/210)
 * The template funcs now include numerous parse functions to provide greater flexibility [GH-212](https://github.com/jrasell/levant/pull/212)
 * Ability to configure allow-stale Nomad setting when performing calls to help in environments with high network latency [GH-185](https://github.com/jrasell/levant/pull/185)

BUG FIXES:
 * Update vendored package of Nomad to fix failures when interacting with jobs configured with update progress_deadline params [GH-216](https://github.com/jrasell/levant/pull/216)

## 0.2.0 (4 July 2018)

IMPROVEMENTS:
 * New `scale-in` and `scale-out` commands  allow an operator to manually scale jobs and task groups based on counts or percentages [GH-172](https://github.com/jrasell/levant/pull/172)
 * New template functions allowing the lookup of variables from Consul KVs, ISO-8601 timestamp generation and loops [GH-175](https://github.com/jrasell/levant/pull/175), [GH-202](https://github.com/jrasell/levant/pull/202)
 * Multiple variable files can be passed on each run, allowing for common configuration to be shared across jobs [GH-180](https://github.com/jrasell/levant/pull/180)
 * Provide better command help for deploy and render commands [GH-183](https://github.com/jrasell/levant/pull/184)
 * Add `-ignore-no-changes` flag to deploy CLI command which allows the changing on behaviour to exit 0 even if Levant detects 0 changes on plan [GH-196](https://github.com/jrasell/levant/pull/196)

BUG FIXES:
 * Fix formatting with version summary output which had erronous quote [GH-170](https://github.com/jrasell/levant/pull/170)

## 0.1.1 (13 May 2018)

IMPROVEMENTS:
 * Use govvv for builds and to supply additional version information in the version command output [GH-151](https://github.com/jrasell/levant/pull/151)
 * Levant will now run Nomad plan before deployments to log the plan diff [GH-153](https://github.com/jrasell/levant/pull/153)
 * Logging can now be output in JSON format and uses contextual data for better processing ability [GH-157](https://github.com/jrasell/levant/pull/157)

BUG FIXES:
 * Fix occasional panic when performing deployment check of a batch job deployment [GH-150](https://github.com/jrasell/levant/pull/150)

## 0.1.0 (18 April 2018)

IMPROVEMENTS:
 * New 'dispatch' command which allows Levant to dispatch Nomad jobs which will go through Levants additional job checking [GH-128](https://github.com/jrasell/levant/pull/128)
 * New 'force-batch' deploy flag which allows users to trigger a periodic run on deployment independent of the schedule [GH-110](https://github.com/jrasell/levant/pull/110)
 * Enhanced job status checking for non-service type jobs [GH-96](https://github.com/jrasell/levant/pull/96), [GH-109](https://github.com/jrasell/levant/pull/109)
 * Implement config struct for Levant to track config during run [GH-102](https://github.com/jrasell/levant/pull/102)
 * Test and build Levant with Go version 1.10 [GH-119](https://github.com/jrasell/levant/pull/119), [GH-116](https://github.com/jrasell/levant/pull/116)
 * Add a catchall for unhandled failure cases to log more useful information for the operator [GH-138](https://github.com/jrasell/levant/pull/138)
 * Updated vendored dependancy of Nomad to 0.8.0 [GH-137](https://github.com/jrasell/levant/pull/137)

BUG FIXES:
 * Service jobs that don't have an update stanza do not produce deployments and should skip the deployment watcher [GH-99](https://github.com/jrasell/levant/pull/99)
 * Ensure the count updater ignores jobs that are in stopped state [GH-106](https://github.com/jrasell/levant/pull/106)
 * Fix a small formatting issue with the deploy command arg help [GH-111](https://github.com/jrasell/levant/pull/111)
 * Do not run the auto-revert inspector if auto-promote fails [GH-122](https://github.com/jrasell/levant/pull/122)
 * Fix issue where allocationStatusChecker logged incorrectly [GH-131](https://github.com/jrasell/levant/pull/131)
 * Add retry to auto-revert checker to ensure the correct deployment is monitored, and not the original [GH-134](https://github.com/jrasell/levant/pull/134)

## 0.0.4 (25 January 2018)

IMPROVEMENTS:
 * Job types of `batch` now undergo checking to confirm the job reaches status of `running` [GH-73](https://github.com/jrasell/levant/pull/73)
 * Vendored Nomad version has been increased to 0.7.1 allowing use of Nomad ACL tokens [GH-76](https://github.com/jrasell/levant/pull/76)
 * Log messages now includes the date, time and timezone [GH-80](https://github.com/jrasell/levant/pull/80)

BUG FIXES:
 * Skip health checks for task groups without canaries when performing canary auto-promote health checking [GH-83](https://github.com/jrasell/levant/pull/83)
 * Fix issue where jobs without specified count caused panic [GH-89](https://github.com/jrasell/levant/pull/89)

## 0.0.3 (23 December 2017)

IMPROVEMENTS:
 * Levant can now track Nomad auto-revert of a failed deployment [GH-55](https://github.com/jrasell/levant/pull/55)
 * Provide greater feedback around variables file passed, CLI variables passed and which variables are being used by Levant.[GH-62](https://github.com/jrasell/levant/pull/62)
 * Levant supports autoloading of default files when running `levant deploy` [GH-37](https://github.com/jrasell/levant/pull/37)

BUG FIXES:
 * Fix issue where Levant did not correctly handle deploying jobs of type `batch` [GH-52](https://github.com/jrasell/levant/pull/52)
 * Fix issue where evaluations errors were not being fully checked [GH-66](https://github.com/jrasell/levant/pull/66)
 * Fix issue in failure_inspector incorrectly handling multi-groups [GH-69](https://github.com/jrasell/levant/pull/69)

## 0.0.2 (29 November 2017)

IMPROVEMENTS:
 * Introduce `-force-count` flag into deploy command which disables dynamic count updating; meaning Levant will explicity use counts defined in the job specification template [GH-33](https://github.com/jrasell/levant/pull/33)
 * Levant deployments now inspect the evaluation results and log any error messages [GH-40](https://github.com/jrasell/levant/pull/40)

BUG FIXES:
 * Fix formatting issue in render command help [GH-28](https://github.com/jrasell/levant/pull/28)
 * Update failure_inspector to cover more failure use cases [GH-27](https://github.com/jrasell/levant/pull/27)
 * Fix a bug in handling Nomad job types incorrectly [GH-32](https://github.com/jrasell/levant/pull/32)
 * Fix issue where jobs deployed with all task group counts at 0 would cause a failure as no deployment ID is returned [GH-36](https://github.com/jrasell/levant/pull/36)

## 0.0.1 (30 October 2017)

- Initial release.


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Code of Conduct

## 1. Purpose

A primary goal of Levant is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).

This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.

We invite all those who participate in Levant to help us create safe and positive experiences for everyone.

## 2. Open Source Citizenship

A supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community.

Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.

If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know.

## 3. Expected Behavior

The following behaviors are expected and requested of all community members:

*   Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
*   Exercise consideration and respect in your speech and actions.
*   Attempt collaboration before conflict.
*   Refrain from demeaning, discriminatory, or harassing behavior and speech.
*   Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential.
*   Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations.

## 4. Unacceptable Behavior

The following behaviors are considered harassment and are unacceptable within our community:

*   Violence, threats of violence or violent language directed against another person.
*   Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language.
*   Posting or displaying sexually explicit or violent material.
*   Posting or threatening to post other people’s personally identifying information ("doxing").
*   Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
*   Inappropriate photography or recording.
*   Inappropriate physical contact. You should have someone’s consent before touching them.
*   Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances.
*   Deliberate intimidation, stalking or following (online or in person).
*   Advocating for, or encouraging, any of the above behavior.
*   Sustained disruption of community events, including talks and presentations.

## 5. Consequences of Unacceptable Behavior

Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated.

Anyone asked to stop unacceptable behavior is expected to comply immediately.

If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event).

## 6. Reporting Guidelines

If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. jamesrasell@gmail.com.



Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress.

## 7. Addressing Grievances

If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Jrasell with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.



## 8. Scope

We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues–online and in-person–as well as in all one-on-one communications pertaining to community business.

This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members.

## 9. Contact info

jamesrasell@gmail.com

## 10. License and attribution

This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).

Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).

Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/)


================================================
FILE: Dockerfile
================================================
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

# This Dockerfile contains multiple targets.
# Use 'docker build --target=<name> .' to build one.

# ===================================
#   Non-release images.
# ===================================

# devbuild compiles the binary
# -----------------------------------
FROM golang:1.24 AS devbuild

# Disable CGO to make sure we build static binaries
ENV CGO_ENABLED=0

# Escape the GOPATH
WORKDIR /build
COPY . ./
RUN go build -o levant .

# dev runs the binary from devbuild
# -----------------------------------
FROM alpine:3.22 AS dev

COPY --from=devbuild /build/levant /bin/
COPY ./scripts/docker-entrypoint.sh /

ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["help"]


# ===================================
#   Release images.
# ===================================

FROM alpine:3.22 AS release

ARG PRODUCT_NAME=levant
ARG PRODUCT_VERSION
ARG PRODUCT_REVISION
# TARGETARCH and TARGETOS are set automatically when --platform is provided.
ARG TARGETOS TARGETARCH

LABEL maintainer="Nomad Team <nomad@hashicorp.com>"
LABEL version=${PRODUCT_VERSION}
LABEL revision=${PRODUCT_REVISION}

COPY dist/$TARGETOS/$TARGETARCH/levant /bin/
COPY ./scripts/docker-entrypoint.sh /

# Create a non-root user to run the software.
RUN addgroup $PRODUCT_NAME && \
    adduser -S -G $PRODUCT_NAME $PRODUCT_NAME

USER $PRODUCT_NAME
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["help"]

# ===================================
#   Set default target to 'dev'.
# ===================================
FROM dev


================================================
FILE: GNUmakefile
================================================
SHELL = bash
default: lint test check-mod dev

GIT_COMMIT := $(shell git rev-parse --short HEAD)
GIT_DIRTY := $(if $(shell git status --porcelain),+CHANGES)

GO_LDFLAGS := "$(GO_LDFLAGS) -X github.com/hashicorp/levant/version.GitCommit=$(GIT_COMMIT)$(GIT_DIRTY)"

.PHONY: tools
tools: ## Install the tools used to test and build
	@echo "==> Installing tools..."
	go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.5
	go install github.com/hashicorp/hcl/v2/cmd/hclfmt@d0c4fa8b0bbc2e4eeccd1ed2a32c2089ed8c5cf1
	@echo "==> Done"


pkg/%/levant: GO_OUT ?= $@
pkg/windows_%/levant: GO_OUT = $@.exe
pkg/%/levant: ## Build Levant for GOOS_GOARCH, e.g. pkg/linux_amd64/levant
	@echo "==> Building $@ with tags $(GO_TAGS)..."
	@CGO_ENABLED=0 \
		GOOS=$(firstword $(subst _, ,$*)) \
		GOARCH=$(lastword $(subst _, ,$*)) \
		go build -trimpath -ldflags $(GO_LDFLAGS) -tags "$(GO_TAGS)" -o "$(GO_OUT)"

.PRECIOUS: pkg/%/levant
pkg/%.zip: pkg/%/levant ## Build and zip Levant for GOOS_GOARCH, e.g. pkg/linux_amd64.zip
	@echo "==> Packaging for $@..."
	zip -j $@ $(dir $<)*

.PHONY: crt
crt:
	@CGO_ENABLED=0 go build -trimpath -ldflags $(GO_LDFLAGS) -tags "$(GO_TAGS)" -o "$(BIN_PATH)"


.PHONY: dev
dev: #check ## Build for the current development version
	@echo "==> Building Levant..."
	@CGO_ENABLED=0 GO111MODULE=on \
	go build \
	-ldflags $(GO_LDFLAGS) \
	-o ./bin/levant
	@echo "==> Done"

.PHONY: test
test: ## Test the source code
	@echo "==> Testing source code..."
	@go test -cover -v -tags -race \
		"$(BUILDTAGS)" $(shell go list ./... |grep -v vendor |grep -v test)

.PHONY: acceptance-test
acceptance-test: ## Run the Levant acceptance tests
	@echo "==> Running $@..."
	go test -timeout 300s github.com/hashicorp/levant/test -v

.PHONY: check
check: tools lint check-mod ## Lint the source code and check other properties

.PHONY: lint
lint: hclfmt ## Lint the source code
	@echo "==> Linting source code..."
	@golangci-lint run -j 1
	@echo "==> Done"

.PHONY: hclfmt
hclfmt: ## Format HCL files with hclfmt
	@echo "--> Formatting HCL"
	@find . -name '.git' -prune \
					-o -name '*fixtures*' -prune \
	        -o \( -name '*.nomad' -o -name '*.hcl' -o -name '*.tf' \) \
	      -print0 | xargs -0 hclfmt -w
	@if (git status -s | grep -q -e '\.hcl$$' -e '\.nomad$$' -e '\.tf$$'); then echo The following HCL files are out of sync; git status -s | grep -e '\.hcl$$' -e '\.nomad$$' -e '\.tf$$'; exit 1; fi

.PHONY: check-mod
check-mod: ## Checks the Go mod is tidy
	@echo "==> Checking Go mod..."
	@GO111MODULE=on go mod tidy
	@if (git status --porcelain | grep -q go.mod); then \
		echo go.mod needs updating; \
		git --no-pager diff go.mod; \
		exit 1; fi
	@echo "==> Done"

HELP_FORMAT="    \033[36m%-25s\033[0m %s\n"
.PHONY: help
help: ## Display this usage information
	@echo "Levant make commands:"
	@grep -E '^[^ ]+:.*?## .*$$' $(MAKEFILE_LIST) | \
		sort | \
		awk 'BEGIN {FS = ":.*?## "}; \
			{printf $(HELP_FORMAT), $$1, $$2}'

.PHONY: version
version:
ifneq (,$(wildcard version/version_ent.go))
	@$(CURDIR)/scripts/version.sh version/version.go version/version_ent.go
else
	@$(CURDIR)/scripts/version.sh version/version.go version/version.go
endif


================================================
FILE: LICENSE
================================================
Copyright (c) 2017 HashiCorp, Inc.

Mozilla Public License, version 2.0

1. Definitions

1.1. "Contributor"

     means each individual or legal entity that creates, contributes to the
     creation of, or owns Covered Software.

1.2. "Contributor Version"

     means the combination of the Contributions of others (if any) used by a
     Contributor and that particular Contributor's Contribution.

1.3. "Contribution"

     means Covered Software of a particular Contributor.

1.4. "Covered Software"

     means Source Code Form to which the initial Contributor has attached the
     notice in Exhibit A, the Executable Form of such Source Code Form, and
     Modifications of such Source Code Form, in each case including portions
     thereof.

1.5. "Incompatible With Secondary Licenses"
     means

     a. that the initial Contributor has attached the notice described in
        Exhibit B to the Covered Software; or

     b. that the Covered Software was made available under the terms of
        version 1.1 or earlier of the License, but not also under the terms of
        a Secondary License.

1.6. "Executable Form"

     means any form of the work other than Source Code Form.

1.7. "Larger Work"

     means a work that combines Covered Software with other material, in a
     separate file or files, that is not Covered Software.

1.8. "License"

     means this document.

1.9. "Licensable"

     means having the right to grant, to the maximum extent possible, whether
     at the time of the initial grant or subsequently, any and all of the
     rights conveyed by this License.

1.10. "Modifications"

     means any of the following:

     a. any file in Source Code Form that results from an addition to,
        deletion from, or modification of the contents of Covered Software; or

     b. any new file in Source Code Form that contains any Covered Software.

1.11. "Patent Claims" of a Contributor

      means any patent claim(s), including without limitation, method,
      process, and apparatus claims, in any patent Licensable by such
      Contributor that would be infringed, but for the grant of the License,
      by the making, using, selling, offering for sale, having made, import,
      or transfer of either its Contributions or its Contributor Version.

1.12. "Secondary License"

      means either the GNU General Public License, Version 2.0, the GNU Lesser
      General Public License, Version 2.1, the GNU Affero General Public
      License, Version 3.0, or any later versions of those licenses.

1.13. "Source Code Form"

      means the form of the work preferred for making modifications.

1.14. "You" (or "Your")

      means an individual or a legal entity exercising rights under this
      License. For legal entities, "You" includes any entity that controls, is
      controlled by, or is under common control with You. For purposes of this
      definition, "control" means (a) the power, direct or indirect, to cause
      the direction or management of such entity, whether by contract or
      otherwise, or (b) ownership of more than fifty percent (50%) of the
      outstanding shares or beneficial ownership of such entity.


2. License Grants and Conditions

2.1. Grants

     Each Contributor hereby grants You a world-wide, royalty-free,
     non-exclusive license:

     a. under intellectual property rights (other than patent or trademark)
        Licensable by such Contributor to use, reproduce, make available,
        modify, display, perform, distribute, and otherwise exploit its
        Contributions, either on an unmodified basis, with Modifications, or
        as part of a Larger Work; and

     b. under Patent Claims of such Contributor to make, use, sell, offer for
        sale, have made, import, and otherwise transfer either its
        Contributions or its Contributor Version.

2.2. Effective Date

     The licenses granted in Section 2.1 with respect to any Contribution
     become effective for each Contribution on the date the Contributor first
     distributes such Contribution.

2.3. Limitations on Grant Scope

     The licenses granted in this Section 2 are the only rights granted under
     this License. No additional rights or licenses will be implied from the
     distribution or licensing of Covered Software under this License.
     Notwithstanding Section 2.1(b) above, no patent license is granted by a
     Contributor:

     a. for any code that a Contributor has removed from Covered Software; or

     b. for infringements caused by: (i) Your and any other third party's
        modifications of Covered Software, or (ii) the combination of its
        Contributions with other software (except as part of its Contributor
        Version); or

     c. under Patent Claims infringed by Covered Software in the absence of
        its Contributions.

     This License does not grant any rights in the trademarks, service marks,
     or logos of any Contributor (except as may be necessary to comply with
     the notice requirements in Section 3.4).

2.4. Subsequent Licenses

     No Contributor makes additional grants as a result of Your choice to
     distribute the Covered Software under a subsequent version of this
     License (see Section 10.2) or under the terms of a Secondary License (if
     permitted under the terms of Section 3.3).

2.5. Representation

     Each Contributor represents that the Contributor believes its
     Contributions are its original creation(s) or it has sufficient rights to
     grant the rights to its Contributions conveyed by this License.

2.6. Fair Use

     This License is not intended to limit any rights You have under
     applicable copyright doctrines of fair use, fair dealing, or other
     equivalents.

2.7. Conditions

     Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
     Section 2.1.


3. Responsibilities

3.1. Distribution of Source Form

     All distribution of Covered Software in Source Code Form, including any
     Modifications that You create or to which You contribute, must be under
     the terms of this License. You must inform recipients that the Source
     Code Form of the Covered Software is governed by the terms of this
     License, and how they can obtain a copy of this License. You may not
     attempt to alter or restrict the recipients' rights in the Source Code
     Form.

3.2. Distribution of Executable Form

     If You distribute Covered Software in Executable Form then:

     a. such Covered Software must also be made available in Source Code Form,
        as described in Section 3.1, and You must inform recipients of the
        Executable Form how they can obtain a copy of such Source Code Form by
        reasonable means in a timely manner, at a charge no more than the cost
        of distribution to the recipient; and

     b. You may distribute such Executable Form under the terms of this
        License, or sublicense it under different terms, provided that the
        license for the Executable Form does not attempt to limit or alter the
        recipients' rights in the Source Code Form under this License.

3.3. Distribution of a Larger Work

     You may create and distribute a Larger Work under terms of Your choice,
     provided that You also comply with the requirements of this License for
     the Covered Software. If the Larger Work is a combination of Covered
     Software with a work governed by one or more Secondary Licenses, and the
     Covered Software is not Incompatible With Secondary Licenses, this
     License permits You to additionally distribute such Covered Software
     under the terms of such Secondary License(s), so that the recipient of
     the Larger Work may, at their option, further distribute the Covered
     Software under the terms of either this License or such Secondary
     License(s).

3.4. Notices

     You may not remove or alter the substance of any license notices
     (including copyright notices, patent notices, disclaimers of warranty, or
     limitations of liability) contained within the Source Code Form of the
     Covered Software, except that You may alter any license notices to the
     extent required to remedy known factual inaccuracies.

3.5. Application of Additional Terms

     You may choose to offer, and to charge a fee for, warranty, support,
     indemnity or liability obligations to one or more recipients of Covered
     Software. However, You may do so only on Your own behalf, and not on
     behalf of any Contributor. You must make it absolutely clear that any
     such warranty, support, indemnity, or liability obligation is offered by
     You alone, and You hereby agree to indemnify every Contributor for any
     liability incurred by such Contributor as a result of warranty, support,
     indemnity or liability terms You offer. You may include additional
     disclaimers of warranty and limitations of liability specific to any
     jurisdiction.

4. Inability to Comply Due to Statute or Regulation

   If it is impossible for You to comply with any of the terms of this License
   with respect to some or all of the Covered Software due to statute,
   judicial order, or regulation then You must: (a) comply with the terms of
   this License to the maximum extent possible; and (b) describe the
   limitations and the code they affect. Such description must be placed in a
   text file included with all distributions of the Covered Software under
   this License. Except to the extent prohibited by statute or regulation,
   such description must be sufficiently detailed for a recipient of ordinary
   skill to be able to understand it.

5. Termination

5.1. The rights granted under this License will terminate automatically if You
     fail to comply with any of its terms. However, if You become compliant,
     then the rights granted under this License from a particular Contributor
     are reinstated (a) provisionally, unless and until such Contributor
     explicitly and finally terminates Your grants, and (b) on an ongoing
     basis, if such Contributor fails to notify You of the non-compliance by
     some reasonable means prior to 60 days after You have come back into
     compliance. Moreover, Your grants from a particular Contributor are
     reinstated on an ongoing basis if such Contributor notifies You of the
     non-compliance by some reasonable means, this is the first time You have
     received notice of non-compliance with this License from such
     Contributor, and You become compliant prior to 30 days after Your receipt
     of the notice.

5.2. If You initiate litigation against any entity by asserting a patent
     infringement claim (excluding declaratory judgment actions,
     counter-claims, and cross-claims) alleging that a Contributor Version
     directly or indirectly infringes any patent, then the rights granted to
     You by any and all Contributors for the Covered Software under Section
     2.1 of this License shall terminate.

5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
     license agreements (excluding distributors and resellers) which have been
     validly granted by You or Your distributors under this License prior to
     termination shall survive termination.

6. Disclaimer of Warranty

   Covered Software is provided under this License on an "as is" basis,
   without warranty of any kind, either expressed, implied, or statutory,
   including, without limitation, warranties that the Covered Software is free
   of defects, merchantable, fit for a particular purpose or non-infringing.
   The entire risk as to the quality and performance of the Covered Software
   is with You. Should any Covered Software prove defective in any respect,
   You (not any Contributor) assume the cost of any necessary servicing,
   repair, or correction. This disclaimer of warranty constitutes an essential
   part of this License. No use of  any Covered Software is authorized under
   this License except under this disclaimer.

7. Limitation of Liability

   Under no circumstances and under no legal theory, whether tort (including
   negligence), contract, or otherwise, shall any Contributor, or anyone who
   distributes Covered Software as permitted above, be liable to You for any
   direct, indirect, special, incidental, or consequential damages of any
   character including, without limitation, damages for lost profits, loss of
   goodwill, work stoppage, computer failure or malfunction, or any and all
   other commercial damages or losses, even if such party shall have been
   informed of the possibility of such damages. This limitation of liability
   shall not apply to liability for death or personal injury resulting from
   such party's negligence to the extent applicable law prohibits such
   limitation. Some jurisdictions do not allow the exclusion or limitation of
   incidental or consequential damages, so this exclusion and limitation may
   not apply to You.

8. Litigation

   Any litigation relating to this License may be brought only in the courts
   of a jurisdiction where the defendant maintains its principal place of
   business and such litigation shall be governed by laws of that
   jurisdiction, without reference to its conflict-of-law provisions. Nothing
   in this Section shall prevent a party's ability to bring cross-claims or
   counter-claims.

9. Miscellaneous

   This License represents the complete agreement concerning the subject
   matter hereof. If any provision of this License is held to be
   unenforceable, such provision shall be reformed only to the extent
   necessary to make it enforceable. Any law or regulation which provides that
   the language of a contract shall be construed against the drafter shall not
   be used to construe this License against a Contributor.


10. Versions of the License

10.1. New Versions

      Mozilla Foundation is the license steward. Except as provided in Section
      10.3, no one other than the license steward has the right to modify or
      publish new versions of this License. Each version will be given a
      distinguishing version number.

10.2. Effect of New Versions

      You may distribute the Covered Software under the terms of the version
      of the License under which You originally received the Covered Software,
      or under the terms of any subsequent version published by the license
      steward.

10.3. Modified Versions

      If you create software not governed by this License, and you want to
      create a new license for such software, you may create and use a
      modified version of this License if you rename the license and remove
      any references to the name of the license steward (except to note that
      such modified license differs from this License).

10.4. Distributing Source Code Form that is Incompatible With Secondary
      Licenses If You choose to distribute Source Code Form that is
      Incompatible With Secondary Licenses under the terms of this version of
      the License, the notice described in Exhibit B of this License must be
      attached.

Exhibit A - Source Code Form License Notice

      This Source Code Form is subject to the
      terms of the Mozilla Public License, v.
      2.0. If a copy of the MPL was not
      distributed with this file, You can
      obtain one at
      http://mozilla.org/MPL/2.0/.

If it is not possible or desirable to put the notice in a particular file,
then You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a
notice.

You may add additional accurate notices of copyright ownership.

Exhibit B - "Incompatible With Secondary Licenses" Notice

      This Source Code Form is "Incompatible
      With Secondary Licenses", as defined by
      the Mozilla Public License, v. 2.0.


================================================
FILE: README.md
================================================
# Levant

_Levant v0.4.0 is the final release of Levant. All users are encouraged to migrate to [Nomad Pack](https://github.com/hashicorp/nomad-pack)._

[![Build Status](https://circleci.com/gh/hashicorp/levant.svg?style=svg)](https://circleci.com/gh/hashicorp/levant) [![Discuss](https://img.shields.io/badge/discuss-nomad-00BC7F?style=flat)](https://discuss.hashicorp.com/c/nomad)

Levant is an open source templating and deployment tool for [HashiCorp Nomad][] jobs that provides
realtime feedback and detailed failure messages upon deployment issues.

## Features

- **Realtime Feedback**: Using watchers, Levant provides realtime feedback on Nomad job deployments
  allowing for greater insight and knowledge about application deployments.

- **Advanced Job Status Checking**: Particularly for system and batch jobs, Levant ensures the job,
  evaluations and allocations all reach the desired state providing feedback at every stage.

- **Dynamic Job Group Counts**: If the Nomad job is currently running on the cluster, Levant dynamically
  updates the rendered template with the relevant job group counts before deployment.

- **Failure Inspection**: Upon a deployment failure, Levant inspects each allocation and logs information
  about each event, providing useful information for debugging without the need for querying the cluster
  retrospectively.

- **Canary Auto Promotion**: In environments with advanced automation and alerting, automatic promotion
  of canary deployments may be desirable after a certain time threshold. Levant allows the user to
  specify a `canary-auto-promote` time period, which if reached with a healthy set of canaries,
  automatically promotes the deployment.

- **Multiple Variable File Formats**: Currently Levant supports `.json`, `.tf`, `.yaml`, and `.yml`
  file extensions for the declaration of template variables.

- **Auto Revert Checking**: In the event that a job deployment does not pass its healthy threshold
  and the job has auto-revert enabled; Levant tracks the resulting rollback deployment so you can
  see the exact outcome of the deployment process.

## Download & Install

- Official Levant binaries can be downloaded from the [HashiCorp releases site][releases-hashicorp].

- Levant can be installed via the go toolkit using `go get github.com/hashicorp/levant && go install github.com/hashicorp/levant`

- A docker image can be found on [Docker Hub][levant-docker]. The latest version can be downloaded
  using `docker pull hashicorp/levant`.

- Levant can be built from source by firstly cloning the repository `git clone git://github.com/hashicorp/levant.git`.
  Once cloned, a binary can be built using the `make dev` command which will be available at
  `./bin/levant`.

- There is a [Levant Ansible role][levant-ansible] available to help installation on machines. Thanks
  to @stevenscg for this.

- Pre-built binaries of Levant from versions 0.2.9 and earlier can be downloaded from the [GitHub releases page][releases] page. These binaries were released prior to the migration to the HashiCorp organization. For example: `curl -L https://github.com/hashicorp/levant/releases/download/0.2.9/linux-amd64-levant -o levant`

## Templating

Levant includes functionality to perform template variables substitution as well as trigger built-in
template functions to add timestamps or retrieve information from Consul. For full details please
consult the [templates][] documentation page.

## Commands

Levant supports a number of command line arguments which provide control over the Levant binary. For
detail about each command and its supported flags, please consult the [commands][] documentation page.

## Clients

Levant utilizes the Nomad and Consul official clients and configuration can be done via a number of
environment variables. For detail about these please read through the [clients][] documentation page.

## Contributing

Community contributions to Levant are encouraged. Please refer to the [contribution guide][] for
details about hacking on Levant.

[clients]: ./docs/clients.md
[commands]: ./docs/commands.md
[templates]: ./docs/templates.md
[contribution guide]: https://github.com/hashicorp/levant/blob/master/.github/CONTRIBUTING.md
[hashicorp nomad]: https://www.nomadproject.io/
[releases]: https://github.com/hashicorp/levant/releases
[levant-docker]: https://hub.docker.com/r/hashicorp/levant/
[levant-ansible]: https://github.com/stevenscg/ansible-role-levant
[releases-hashicorp]: https://releases.hashicorp.com/levant/


================================================
FILE: client/consul.go
================================================
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package client

import (
	consul "github.com/hashicorp/consul/api"
)

// NewConsulClient is used to create a new client to interact with Consul.
func NewConsulClient(addr string) (*consul.Client, error) {
	config := consul.DefaultConfig()

	if addr != "" {
		config.Address = addr
	}

	c, err := consul.NewClient(config)
	if err != nil {
		return nil, err
	}

	return c, nil
}


================================================
FILE: client/nomad.go
================================================
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package client

import (
	nomad "github.com/hashicorp/nomad/api"
)

// NewNomadClient is used to create a new client to interact with Nomad.
func NewNomadClient(addr string) (*nomad.Client, error) {
	config := nomad.DefaultConfig()

	if addr != "" {
		config.Address = addr
	}

	c, err := nomad.NewClient(config)
	if err != nil {
		return nil, err
	}

	return c, nil
}


================================================
FILE: command/deploy.go
================================================
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package command

import (
	"fmt"
	"strings"

	"github.com/hashicorp/levant/helper"
	"github.com/hashicorp/levant/levant"
	"github.com/hashicorp/levant/levant/structs"
	"github.com/hashicorp/levant/logging"
	"github.com/hashicorp/levant/template"
	nomad "github.com/hashicorp/nomad/api"
)

// DeployCommand is the command implementation that allows users to deploy a
// Nomad job based on passed templates and variables.
type DeployCommand struct {
	Meta
}

// Help provides the help information for the deploy command.
func (c *DeployCommand) Help() string {
	helpText := `
Usage: levant deploy [options] [TEMPLATE]

  Deploy a Nomad job based on input templates and variable files. The deploy
  command supports passing variables individually on the command line. Multiple
  commands can be passed in the format of -var 'key=value'. Variables passed
  via the command line take precedence over the same variable declared within
  a passed variable file.

Arguments:

  TEMPLATE nomad job template
    If no argument is given we look for a single *.nomad file

General Options:

  -address=<http_address>
    The Nomad HTTP API address including port which Levant will use to make
    calls.

  -allow-stale
    Allow stale consistency mode for requests into nomad.

  -canary-auto-promote=<seconds>
    The time in seconds, after which Levant will auto-promote a canary job
    if all canaries within the deployment are healthy.

  -consul-address=<addr>
    The Consul host and port to use when making Consul KeyValue lookups for
    template rendering.

  -force
    Execute deployment even though there were no changes.

  -force-batch
    Forces a new instance of the periodic job. A new instance will be created
    even if it violates the job's prohibit_overlap settings.

  -force-count
    Use the taskgroup count from the Nomad jobfile instead of the count that
    is currently set in a running job.

  -ignore-no-changes
    By default if no changes are detected when running a deployment Levant will
    exit with a status 1 to indicate a deployment didn't happen. This behaviour
    can be changed using this flag so that Levant will exit cleanly ensuring CD
    pipelines don't fail when no changes are detected.

  -log-level=<level>
    Specify the verbosity level of Levant's logs. Valid values include DEBUG,
    INFO, and WARN, in decreasing order of verbosity. The default is INFO.

  -log-format=<format>
    Specify the format of Levant's logs. Valid values are HUMAN or JSON. The
    default is HUMAN.

  -var-file=<file>
    Path to a file containing user variables used when rendering the job
    template. You can repeat this flag multiple times to supply multiple
    var-files. Defaults to levant.(json|yaml|yml|tf).
    [default: levant.(json|yaml|yml|tf)]
`
	return strings.TrimSpace(helpText)
}

// Synopsis is provides a brief summary of the deploy command.
func (c *DeployCommand) Synopsis() string {
	return "Render and deploy a Nomad job from a template"
}

// Run triggers a run of the Levant template and deploy functions.
func (c *DeployCommand) Run(args []string) int {

	var err error
	var level, format string

	config := &levant.DeployConfig{
		Client:   &structs.ClientConfig{},
		Deploy:   &structs.DeployConfig{},
		Plan:     &structs.PlanConfig{},
		Template: &structs.TemplateConfig{},
	}

	flags := c.Meta.FlagSet("deploy", FlagSetVars)
	flags.Usage = func() { c.UI.Output(c.Help()) }

	flags.StringVar(&config.Client.Addr, "address", "", "")
	flags.BoolVar(&config.Client.AllowStale, "allow-stale", false, "")
	flags.IntVar(&config.Deploy.Canary, "canary-auto-promote", 0, "")
	flags.StringVar(&config.Client.ConsulAddr, "consul-address", "", "")
	flags.BoolVar(&config.Deploy.Force, "force", false, "")
	flags.BoolVar(&config.Deploy.ForceBatch, "force-batch", false, "")
	flags.BoolVar(&config.Deploy.ForceCount, "force-count", false, "")
	flags.BoolVar(&config.Plan.IgnoreNoChanges, "ignore-no-changes", false, "")
	flags.StringVar(&level, "log-level", "INFO", "")
	flags.StringVar(&format, "log-format", "HUMAN", "")

	flags.Var((*helper.FlagStringSlice)(&config.Template.VariableFiles), "var-file", "")

	if err = flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()

	if err = logging.SetupLogger(level, format); err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	if len(args) == 1 {
		config.Template.TemplateFile = args[0]
	} else if len(args) == 0 {
		if config.Template.TemplateFile = helper.GetDefaultTmplFile(); config.Template.TemplateFile == "" {
			c.UI.Error(c.Help())
			c.UI.Error("\nERROR: Template arg missing and no default template found")
			return 1
		}
	} else {
		c.UI.Error(c.Help())
		return 1
	}

	config.Template.Job, err = template.RenderJob(config.Template.TemplateFile,
		config.Template.VariableFiles, config.Client.ConsulAddr, &c.Meta.flagVars)
	if err != nil {
		c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
		return 1
	}

	if config.Deploy.Canary > 0 {
		if err = c.checkCanaryAutoPromote(config.Template.Job, config.Deploy.Canary); err != nil {
			c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
			return 1
		}
	}

	if config.Deploy.ForceBatch {
		if err = c.checkForceBatch(config.Template.Job, config.Deploy.ForceBatch); err != nil {
			c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
			return 1
		}
	}

	if !config.Deploy.Force {
		p := levant.PlanConfig{
			Client:   config.Client,
			Plan:     config.Plan,
			Template: config.Template,
		}

		planSuccess, changes := levant.TriggerPlan(&p)
		if !planSuccess {
			return 1
		} else if !changes && p.Plan.IgnoreNoChanges {
			return 0
		}
	}

	success := levant.TriggerDeployment(config, nil)
	if !success {
		return 1
	}

	return 0
}

func (c *DeployCommand) checkCanaryAutoPromote(job *nomad.Job, canaryAutoPromote int) error {
	if canaryAutoPromote == 0 {
		return nil
	}

	if job.Update != nil && job.Update.Canary != nil && *job.Update.Canary > 0 {
		return nil
	}

	for _, group := range job.TaskGroups {
		if group.Update != nil && group.Update.Canary != nil && *group.Update.Canary > 0 {
			return nil
		}
	}

	return fmt.Errorf("canary-auto-update of %v passed but job is not canary enabled", canaryAutoPromote)
}

// checkForceBatch ensures that if the force-batch flag is passed, the job is
// periodic.
func (c *DeployCommand) checkForceBatch(job *nomad.Job, forceBatch bool) error {

	if forceBatch && job.IsPeriodic() {
		return nil
	}

	return fmt.Errorf("force-batch passed but job is not periodic")
}


================================================
FILE: command/deploy_test.go
================================================
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package command

import (
	"testing"

	"github.com/hashicorp/levant/template"
)

func TestDeploy_checkCanaryAutoPromote(t *testing.T) {

	fVars := make(map[string]interface{})
	depCommand := &DeployCommand{}
	canaryPromote := 30

	cases := []struct {
		File          string
		CanaryPromote int
		Output        error
	}{
		{
			File:          "test-fixtures/job_canary.nomad",
			CanaryPromote: canaryPromote,
			Output:        nil,
		},
		{
			File:          "test-fixtures/group_canary.nomad",
			CanaryPromote: canaryPromote,
			Output:        nil,
		},
	}

	for i, c := range cases {
		job, err := template.RenderJob(c.File, []string{}, "", &fVars)
		if err != nil {
			t.Fatalf("case %d failed: %v", i, err)
		}

		out := depCommand.checkCanaryAutoPromote(job, c.CanaryPromote)
		if out != c.Output {
			t.Fatalf("case %d: got \"%v\"; want %v", i, out, c.Output)
		}
	}
}

func TestDeploy_checkForceBatch(t *testing.T) {

	fVars := make(map[string]interface{})
	depCommand := &DeployCommand{}
	forceBatch := true

	cases := []struct {
		File       string
		ForceBatch bool
		Output     error
	}{
		{
			File:       "test-fixtures/periodic_batch.nomad",
			ForceBatch: forceBatch,
			Output:     nil,
		},
	}

	for i, c := range cases {
		job, err := template.RenderJob(c.File, []string{}, "", &fVars)
		if err != nil {
			t.Fatalf("case %d failed: %v", i, err)
		}

		out := depCommand.checkForceBatch(job, c.ForceBatch)
		if out != c.Output {
			t.Fatalf("case %d: got \"%v\"; want %v", i, out, c.Output)
		}
	}
}


================================================
FILE: command/dispatch.go
================================================
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package command

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/hashicorp/levant/levant"
	"github.com/hashicorp/levant/logging"
	flaghelper "github.com/hashicorp/nomad/helper/flags"
)

// DispatchCommand is the command implementation that allows users to
// dispatch a Nomad job.
type DispatchCommand struct {
	Meta
}

// Help provides the help information for the dispatch command.
func (c *DispatchCommand) Help() string {
	helpText := `
Usage: levant dispatch [options] <parameterized job> [input source]

  Dispatch creates an instance of a parameterized job. A data payload to the
  dispatched instance can be provided via stdin by using "-" or by specifying a
  path to a file. Metadata can be supplied by using the meta flag one or more
  times. 

General Options:

  -address=<http_address>
    The Nomad HTTP API address including port which Levant will use to make
    calls.

  -log-level=<level>
    Specify the verbosity level of Levant's logs. Valid values include DEBUG,
    INFO, and WARN, in decreasing order of verbosity. The default is INFO.

  -log-format=<format>
    Specify the format of Levant's logs. Valid values are HUMAN or JSON. The
    default is HUMAN.

Dispatch Options:

  -meta <key>=<value>
    Meta takes a key/value pair separated by "=". The metadata key will be
    merged into the job's metadata. The job may define a default value for the
    key which is overridden when dispatching. The flag can be provided more 
    than once to inject multiple metadata key/value pairs. Arbitrary keys are
    not allowed. The parameterized job must allow the key to be merged.
`
	return strings.TrimSpace(helpText)
}

// Synopsis is provides a brief summary of the dispatch command.
func (c *DispatchCommand) Synopsis() string {
	return "Dispatch an instance of a parameterized job"
}

// Run triggers a run of the Levant dispatch functions.
func (c *DispatchCommand) Run(args []string) int {

	var meta []string
	var addr, logLevel, logFormat string

	flags := c.Meta.FlagSet("dispatch", FlagSetVars)
	flags.Usage = func() { c.UI.Output(c.Help()) }
	flags.Var((*flaghelper.StringFlag)(&meta), "meta", "")
	flags.StringVar(&addr, "address", "", "")
	flags.StringVar(&logLevel, "log-level", "INFO", "")
	flags.StringVar(&logFormat, "log-format", "human", "")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()
	if l := len(args); l < 1 || l > 2 {
		c.UI.Error(c.Help())
		return 1
	}

	err := logging.SetupLogger(logLevel, logFormat)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error setting up logging: %v", err))
	}

	job := args[0]
	var payload []byte
	var readErr error

	if len(args) == 2 {
		switch args[1] {
		case "-":
			payload, readErr = io.ReadAll(os.Stdin)
		default:
			payload, readErr = os.ReadFile(args[1])
		}
		if readErr != nil {
			c.UI.Error(fmt.Sprintf("Error reading input data: %v", readErr))
			return 1
		}
	}

	metaMap := make(map[string]string, len(meta))
	for _, m := range meta {
		split := strings.SplitN(m, "=", 2)
		if len(split) != 2 {
			c.UI.Error(fmt.Sprintf("Error parsing meta value: %v", m))
			return 1
		}
		metaMap[split[0]] = split[1]
	}

	success := levant.TriggerDispatch(job, metaMap, payload, addr)
	if !success {
		return 1
	}

	return 0
}


================================================
FILE: command/meta.go
================================================
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package command

import (
	"bufio"
	"flag"
	"io"

	"github.com/hashicorp/levant/helper"
	"github.com/mitchellh/cli"
)

// FlagSetFlags is an enum to define what flags are present in the
// default FlagSet returned by Meta.FlagSet
type FlagSetFlags uint

// Consts which helps us track meta CLI falgs.
const (
	FlagSetNone        FlagSetFlags = 0
	FlagSetBuildFilter FlagSetFlags = 1 << iota
	FlagSetVars
)

// Meta contains the meta-options and functionality that nearly every
// Levant command inherits.
type Meta struct {
	UI cli.Ui

	// These are set by command-line flags
	flagVars map[string]interface{}
}

// FlagSet returns a FlagSet with the common flags that every
// command implements.
func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {
	f := flag.NewFlagSet(n, flag.ContinueOnError)

	// FlagSetVars tells us what variables to use
	if fs&FlagSetVars != 0 {
		f.Var((*helper.Flag)(&m.flagVars), "var", "")
	}

	// Create an io.Writer that writes to our Ui properly for errors.
	errR, errW := io.Pipe()
	errScanner := bufio.NewScanner(errR)
	go func() {
		for errScanner.Scan() {
			m.UI.Error(errScanner.Text())
		}
	}()
	f.SetOutput(errW)

	return f
}


================================================
FILE: command/plan.go
================================================
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package command

import (
	"fmt"
	"strings"

	"github.com/hashicorp/levant/helper"
	"github.com/hashicorp/levant/levant"
	"github.com/hashicorp/levant/levant/structs"
	"github.com/hashicorp/levant/logging"
	"github.com/hashicorp/levant/template"
)

// PlanCommand is the command implementation that allows users to plan a
// Nomad job based on passed templates and variables.
type PlanCommand struct {
	Meta
}

// Help provides the help information for the plan command.
func (c *PlanCommand) Help() string {
	helpText := `
Usage: levant plan [options] [TEMPLATE]

  Perform a Nomad plan based on input templates and variable files. The plan
  command supports passing variables individually on the command line. Multiple
  commands can be passed in the format of -var 'key=value'. Variables passed
  via the command line take precedence over the same variable declared within
  a passed variable file.

Arguments:

  TEMPLATE nomad job template
    If no argument is given we look for a single *.nomad file

General Options:

  -address=<http_address>
    The Nomad HTTP API address including port which Levant will use to make
    calls.

  -allow-stale
    Allow stale consistency mode for requests into nomad.
		
  -consul-address=<addr>
    The Consul host and port to use when making Consul KeyValue lookups for
    template rendering.

  -force-count
    Use the taskgroup count from the Nomad jobfile instead of the count that
    is currently set in a running job.

  -ignore-no-changes
    By default if no changes are detected when running a plan Levant will
    exit with a status 1 to indicate there are no changes. This behaviour
    can be changed using this flag so that Levant will exit cleanly ensuring CD
    pipelines don't fail when no changes are detected.

  -log-level=<level>
    Specify the verbosity level of Levant's logs. Valid values include DEBUG,
    INFO, and WARN, in decreasing order of verbosity. The default is INFO.

  -log-format=<format>
    Specify the format of Levant's logs. Valid values are HUMAN or JSON. The
    default is HUMAN.

  -var-file=<file>
    Path to a file containing user variables used when rendering the job
    template. You can repeat this flag multiple times to supply multiple
    var-files. Defaults to levant.(json|yaml|yml|tf).
    [default: levant.(json|yaml|yml|tf)]
`
	return strings.TrimSpace(helpText)
}

// Synopsis is provides a brief summary of the plan command.
func (c *PlanCommand) Synopsis() string {
	return "Render and perform a Nomad job plan from a template"
}

// Run triggers a run of the Levant template and plan functions.
func (c *PlanCommand) Run(args []string) int {

	var err error
	var level, format string
	config := &levant.PlanConfig{
		Client:   &structs.ClientConfig{},
		Plan:     &structs.PlanConfig{},
		Template: &structs.TemplateConfig{},
	}

	flags := c.Meta.FlagSet("plan", FlagSetVars)
	flags.Usage = func() { c.UI.Output(c.Help()) }

	flags.StringVar(&config.Client.Addr, "address", "", "")
	flags.BoolVar(&config.Client.AllowStale, "allow-stale", false, "")
	flags.StringVar(&config.Client.ConsulAddr, "consul-address", "", "")
	flags.BoolVar(&config.Plan.IgnoreNoChanges, "ignore-no-changes", false, "")
	flags.StringVar(&level, "log-level", "INFO", "")
	flags.StringVar(&format, "log-format", "HUMAN", "")
	flags.Var((*helper.FlagStringSlice)(&config.Template.VariableFiles), "var-file", "")

	if err = flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()

	if err = logging.SetupLogger(level, format); err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	if len(args) == 1 {
		config.Template.TemplateFile = args[0]
	} else if len(args) == 0 {
		if config.Template.TemplateFile = helper.GetDefaultTmplFile(); config.Template.TemplateFile == "" {
			c.UI.Error(c.Help())
			c.UI.Error("\nERROR: Template arg missing and no default template found")
			return 1
		}
	} else {
		c.UI.Error(c.Help())
		return 1
	}

	config.Template.Job, err = template.RenderJob(config.Template.TemplateFile,
		config.Template.VariableFiles, config.Client.ConsulAddr, &c.Meta.flagVars)

	if err != nil {
		c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
		return 1
	}

	success, changes := levant.TriggerPlan(config)
	if !success {
		return 1
	} else if !changes && config.Plan.IgnoreNoChanges {
		return 0
	}

	return 0
}


================================================
FILE: command/render.go
================================================
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package command

import (
	"bytes"
	"fmt"
	"os"
	"strings"

	"github.com/hashicorp/levant/helper"
	"github.com/hashicorp/levant/logging"
	"github.com/hashicorp/levant/template"
)

// RenderCommand is the command implementation that allows users to render a
// Nomad job template based on passed templates and variables.
type RenderCommand struct {
	Meta
}

// Help provides the help information for the template command.
func (c *RenderCommand) Help() string {
	helpText := `
Usage: levant render [options] [TEMPLATE]

  Render a Nomad job template, useful for debugging. Like deploy, the render
  command also supports passing variables individually on the command line. 
  Multiple vars can be passed in the format of -var 'key=value'. Variables 
  passed via the command line take precedence over the same variable declared
  within a passed variable file.

Arguments:

  TEMPLATE  nomad job template
    If no argument is given we look for a single *.nomad file

General Options:

  -consul-address=<addr>
    The Consul host and port to use when making Consul KeyValue lookups for
    template rendering.

  -log-level=<level>
    Specify the verbosity level of Levant's logs. Valid values include DEBUG,
    INFO, and WARN, in decreasing order of verbosity. The default is INFO.

  -log-format=<format>
    Specify the format of Levant's logs. Valid values are HUMAN or JSON. The
    default is HUMAN.

  -out=<file>
    Specify the path to write the rendered template out to, if a file exists at
    the specified path it will be truncated before rendering. The template will be
    rendered to stdout if this is not set.

  -var-file=<file>
    The variables file to render the template with. You can repeat this flag multiple
    times to supply multiple var-files. [default: levant.(json|yaml|yml|tf)]
`
	return strings.TrimSpace(helpText)
}

// Synopsis is provides a brief summary of the template command.
func (c *RenderCommand) Synopsis() string {
	return "Render a Nomad job from a template"
}

// Run triggers a run of the Levant template functions.
func (c *RenderCommand) Run(args []string) int {

	var addr, outPath, templateFile string
	var variables []string
	var err error
	var tpl *bytes.Buffer
	var level, format string

	flags := c.Meta.FlagSet("render", FlagSetVars)
	flags.Usage = func() { c.UI.Output(c.Help()) }

	flags.StringVar(&addr, "consul-address", "", "")
	flags.StringVar(&level, "log-level", "INFO", "")
	flags.StringVar(&format, "log-format", "HUMAN", "")
	flags.Var((*helper.FlagStringSlice)(&variables), "var-file", "")
	flags.StringVar(&outPath, "out", "", "")

	if err = flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()

	if err = logging.SetupLogger(level, format); err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	if len(args) == 1 {
		templateFile = args[0]
	} else if len(args) == 0 {
		if templateFile = helper.GetDefaultTmplFile(); templateFile == "" {
			c.UI.Error(c.Help())
			c.UI.Error("\nERROR: Template arg missing and no default template found")
			return 1
		}
	} else {
		c.UI.Error(c.Help())
		return 1
	}

	tpl, err = template.RenderTemplate(templateFile, variables, addr, &c.Meta.flagVars)
	if err != nil {
		c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
		return 1
	}

	out := os.Stdout
	if outPath != "" {
		out, err = os.Create(outPath)
		if err != nil {
			c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
			return 1
		}
	}

	_, err = tpl.WriteTo(out)
	if err != nil {
		c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
		return 1
	}

	return 0
}


================================================
FILE: command/scale_in.go
================================================
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package command

import (
	"strings"

	"github.com/hashicorp/levant/levant/structs"
	"github.com/hashicorp/levant/logging"
	"github.com/hashicorp/levant/scale"
)

// ScaleInCommand is the command implementation that allows users to scale a
// Nomad job out.
type ScaleInCommand struct {
	Meta
}

// Help provides the help information for the scale-in command.
func (c *ScaleInCommand) Help() string {
	helpText := `
Usage: levant scale-in [options] <job-id>

  Scale a Nomad job and optional task group out.

General Options:

  -address=<http_address>
    The Nomad HTTP API address including port which Levant will use to make
    calls.

  -allow-stale
    Allow stale consistency mode for requests into nomad.
  
  -log-level=<level>
    Specify the verbosity level of Levant's logs. Valid values include DEBUG,
    INFO, and WARN, in decreasing order of verbosity. The default is INFO.
  
  -log-format=<format>
    Specify the format of Levant's logs. Valid values are HUMAN or JSON. The
    default is HUMAN.
	
Scale In Options:

  -count=<num>
    The count by which the job and task groups should be scaled in by. Only
    one of count or percent can be passed.

  -percent=<num>
    A percentage value by which the job and task groups should be scaled in
    by. Counts will be rounded up, to ensure required capacity is met. Only 
    one of count or percent can be passed.

  -task-group=<name>
    The name of the task group you wish to target for scaling. If this is not
    specified, all task groups within the job will be scaled.
`
	return strings.TrimSpace(helpText)
}

// Synopsis is provides a brief summary of the scale-in command.
func (c *ScaleInCommand) Synopsis() string {
	return "Scale in a Nomad job"
}

// Run triggers a run of the Levant scale-in functions.
func (c *ScaleInCommand) Run(args []string) int {

	var err error
	var logL, logF string

	config := &scale.Config{
		Client: &structs.ClientConfig{},
		Scale: &structs.ScaleConfig{
			Direction: structs.ScalingDirectionIn,
		},
	}

	flags := c.Meta.FlagSet("scale-in", FlagSetVars)
	flags.Usage = func() { c.UI.Output(c.Help()) }

	flags.StringVar(&config.Client.Addr, "address", "", "")
	flags.BoolVar(&config.Client.AllowStale, "allow-stale", false, "")
	flags.StringVar(&logL, "log-level", "INFO", "")
	flags.StringVar(&logF, "log-format", "HUMAN", "")
	flags.IntVar(&config.Scale.Count, "count", 0, "")
	flags.IntVar(&config.Scale.Percent, "percent", 0, "")
	flags.StringVar(&config.Scale.TaskGroup, "task-group", "", "")

	if err = flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()

	if len(args) != 1 {
		c.UI.Error("This command takes one argument: <job-name>")
		return 1
	}

	config.Scale.JobID = args[0]

	if config.Scale.Count == 0 && config.Scale.Percent == 0 || config.Scale.Count > 0 && config.Scale.Percent > 0 {
		c.UI.Error("You must set either -count or -percent flag to scale-in")
		return 1
	}

	if config.Scale.Count > 0 {
		config.Scale.DirectionType = structs.ScalingDirectionTypeCount
	}

	if config.Scale.Percent > 0 {
		config.Scale.DirectionType = structs.ScalingDirectionTypePercent
	}

	if err = logging.SetupLogger(logL, logF); err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	success := scale.TriggerScalingEvent(config)
	if !success {
		return 1
	}

	return 0
}


================================================
FILE: command/scale_out.go
================================================
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package command

import (
	"strings"

	"github.com/hashicorp/levant/levant/structs"
	"github.com/hashicorp/levant/logging"
	"github.com/hashicorp/levant/scale"
)

// ScaleOutCommand is the command implementation that allows users to scale a
// Nomad job out.
type ScaleOutCommand struct {
	Meta
}

// Help provides the help information for the scale-out command.
func (c *ScaleOutCommand) Help() string {
	helpText := `
Usage: levant scale-out [options] <job-id>

  Scale a Nomad job and optional task group out.

General Options:

  -address=<http_address>
    The Nomad HTTP API address including port which Levant will use to make
    calls.

  -allow-stale
    Allow stale consistency mode for requests into nomad.
  
  -log-level=<level>
    Specify the verbosity level of Levant's logs. Valid values include DEBUG,
    INFO, and WARN, in decreasing order of verbosity. The default is INFO.
  
  -log-format=<format>
    Specify the format of Levant's logs. Valid values are HUMAN or JSON. The
    default is HUMAN.
	
Scale Out Options:

  -count=<num>
    The count by which the job and task groups should be scaled out by. Only
    one of count or percent can be passed.

  -percent=<num>
    A percentage value by which the job and task groups should be scaled out
    by. Counts will be rounded up, to ensure required capacity is met. Only 
    one of count or percent can be passed.

  -task-group=<name>
    The name of the task group you wish to target for scaling. Is this is not
    specified all task groups within the job will be scaled.
`
	return strings.TrimSpace(helpText)
}

// Synopsis is provides a brief summary of the scale-out command.
func (c *ScaleOutCommand) Synopsis() string {
	return "Scale out a Nomad job"
}

// Run triggers a run of the Levant scale-out functions.
func (c *ScaleOutCommand) Run(args []string) int {

	var err error
	var logL, logF string

	config := &scale.Config{
		Client: &structs.ClientConfig{},
		Scale: &structs.ScaleConfig{
			Direction: structs.ScalingDirectionOut,
		},
	}

	flags := c.Meta.FlagSet("scale-out", FlagSetVars)
	flags.Usage = func() { c.UI.Output(c.Help()) }

	flags.StringVar(&config.Client.Addr, "address", "", "")
	flags.BoolVar(&config.Client.AllowStale, "allow-stale", false, "")
	flags.StringVar(&logL, "log-level", "INFO", "")
	flags.StringVar(&logF, "log-format", "HUMAN", "")
	flags.IntVar(&config.Scale.Count, "count", 0, "")
	flags.IntVar(&config.Scale.Percent, "percent", 0, "")
	flags.StringVar(&config.Scale.TaskGroup, "task-group", "", "")

	if err = flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()

	if len(args) != 1 {
		c.UI.Error("This command takes one argument: <job-name>")
		return 1
	}

	config.Scale.JobID = args[0]

	if config.Scale.Count == 0 && config.Scale.Percent == 0 || config.Scale.Count > 0 && config.Scale.Percent > 0 {
		c.UI.Error("You must set either -count or -percent flag to scale-out")
		return 1
	}

	if config.Scale.Count > 0 {
		config.Scale.DirectionType = structs.ScalingDirectionTypeCount
	}

	if config.Scale.Percent > 0 {
		config.Scale.DirectionType = structs.ScalingDirectionTypePercent
	}

	if err = logging.SetupLogger(logL, logF); err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	success := scale.TriggerScalingEvent(config)
	if !success {
		return 1
	}

	return 0
}


================================================
FILE: command/test-fixtures/group_canary.nomad
================================================
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

job "example" {
  datacenters = ["dc1"]
  type = "service"

  group "cache" {
    update {
      max_parallel     = 1
      min_healthy_time = "10s"
      healthy_deadline = "1m"
      auto_revert      = true
      canary           = 1
    }
    count = 1
    restart {
      attempts = 10
      interval = "5m"
      delay = "25s"
      mode = "delay"
    }
    ephemeral_disk {
      size = 300
    }
    task "redis" {
      artifact {
        source = "google.com"
      }

      driver = "docker"
      config {
        image = "redis:3.2"
        port_map {
          db = 6379
        }
      }
      resources {
        cpu    = 500
        memory = 256
        network {
          mbits = 10
          port "db" {}
        }
      }
      service {
        name = "global-redis-check"
        tags = ["global", "cache"]
        port = "db"
        check {
          name     = "alive"
          type     = "tcp"
          interval = "10s"
          timeout  = "2s"
        }
      }
    }
  }
}


================================================
FILE: command/test-fixtures/job_canary.nomad
================================================
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

job "example" {
  datacenters = ["dc1"]
  type = "service"
  update {
    max_parallel     = 1
    min_healthy_time = "10s"
    healthy_deadline = "1m"
    auto_revert      = true
    canary           = 1
  }
  group "cache" {
    count = 1
    restart {
      attempts = 10
      interval = "5m"
      delay = "25s"
      mode = "delay"
    }
    ephemeral_disk {
      size = 300
    }
    task "redis" {
      artifact {
        source = "google.com"
      }

      driver = "docker"
      config {
        image = "redis:3.2"
        port_map {
          db = 6379
        }
      }
      resources {
        cpu    = 500
        memory = 256
        network {
          mbits = 10
          port "db" {}
        }
      }
      service {
        name = "global-redis-check"
        tags = ["global", "cache"]
        port = "db"
        check {
          name     = "alive"
          type     = "tcp"
          interval = "10s"
          timeout  = "2s"
        }
      }
    }
  }
}


================================================
FILE: command/test-fixtures/periodic_batch.nomad
================================================
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

job "periodic_batch_test" {
  datacenters = ["dc1"]
  region      = "global"
  type        = "batch"
  priority    = 75
  periodic {
    cron             = "* 1 * * * *"
    prohibit_overlap = true
  }
  group "periodic_batch" {
    task "periodic_batch" {
      driver = "docker"
      config {
        image = "cogniteev/echo"
      }
      resources {
        cpu    = 100
        memory = 128
        network {
          mbits = 1
        }
      }
    }
  }
}


================================================
FILE: command/version.go
================================================
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package command

import (
	"github.com/mitchellh/cli"
)

var _ cli.Command = &VersionCommand{}

// VersionCommand is a Command implementation that prints the version.
type VersionCommand struct {
	Version string
	UI      cli.Ui
}

// Help provides the help information for the version command.
func (c *VersionCommand) Help() string {
	return ""
}

// Synopsis is provides a brief summary of the version command.
func (c *VersionCommand) Synopsis() string {
	return "Prints the Levant version"
}

// Run executes the version command.
func (c *VersionCommand) Run(_ []string) int {
	c.UI.Info(c.Version)
	return 0
}


================================================
FILE: commands.go
================================================
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package main

import (
	"fmt"
	"os"

	"github.com/hashicorp/levant/command"
	"github.com/hashicorp/levant/version"
	"github.com/mitchellh/cli"
)

// Commands returns the mapping of CLI commands for Levant. The meta parameter
// lets you set meta options for all commands.
func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
	if metaPtr == nil {
		metaPtr = new(command.Meta)
	}

	meta := *metaPtr
	if meta.UI == nil {
		meta.UI = &cli.BasicUi{
			Reader:      os.Stdin,
			Writer:      os.Stdout,
			ErrorWriter: os.Stderr,
		}
	}

	return map[string]cli.CommandFactory{

		"deploy": func() (cli.Command, error) {
			return &command.DeployCommand{
				Meta: meta,
			}, nil
		},
		"dispatch": func() (cli.Command, error) {
			return &command.DispatchCommand{
				Meta: meta,
			}, nil
		},
		"plan": func() (cli.Command, error) {
			return &command.PlanCommand{
				Meta: meta,
			}, nil
		},
		"render": func() (cli.Command, error) {
			return &command.RenderCommand{
				Meta: meta,
			}, nil
		},
		"scale-in": func() (cli.Command, error) {
			return &command.ScaleInCommand{
				Meta: meta,
			}, nil
		},
		"scale-out": func() (cli.Command, error) {
			return &command.ScaleOutCommand{
				Meta: meta,
			}, nil
		},
		"version": func() (cli.Command, error) {
			return &command.VersionCommand{
				Version: fmt.Sprintf("Levant %s", version.GetHumanVersion()),
				UI:      meta.UI,
			}, nil
		},
	}
}


================================================
FILE: docs/README.md
================================================
# Levant Documentation

Welcome to the Levant documentation. The `docs` directory aims to host detailed and thorough documentation about Levant including design rational. Levant is an open source templating and deployment tool for [HashiCorp Nomad](https://www.nomadproject.io/) jobs that provides realtime feedback and detailed failure messages upon deployment issues.

## Documentation Pages

* [Commands](./commands.md) - detail about the Levant CLI and flags associated with each command.
* [Templates](./templates.md) - detail about Levant's templating engine and functions.
* [Clients](./clients.md) - information on Nomad and Consul client configuration options.

## Contributing

If there is something missing or wrong, contributions to the Levant docs are very welcome and greatly appreciated.


================================================
FILE: docs/clients.md
================================================
## Clients

Levant uses Nomad and Consul clients in order to perform its work. Currently only the HTTP address client parameter can be configured for each client via CLI flags; a choice made to keep the number of flags low. In order to further configure the clients you can use environment variables as detailed below.

### Nomad Client

The project uses the Nomad [Default API Client](https://github.com/hashicorp/nomad/blob/master/api/api.go#L201) which means the following Nomad client parameters used by Levant are configurable via environment variables:

 * **NOMAD_ADDR** - The address of the Nomad server.
 * **NOMAD_REGION** - The region of the Nomad servers to forward commands to.
 * **NOMAD_NAMESPACE** - The target namespace for queries and actions bound to a namespace.
 * **NOMAD_CACERT** - Path to a PEM encoded CA cert file to use to verify the Nomad server SSL certificate.
 * **NOMAD_CAPATH** - Path to a directory of PEM encoded CA cert files to verify the Nomad server SSL certificate.
 * **NOMAD_CLIENT_CERT** - Path to a PEM encoded client certificate for TLS authentication to the Nomad server.
 * **NOMAD_CLIENT_KEY** - Path to an unencrypted PEM encoded private key matching the client certificate from `NOMAD_CLIENT_CERT`.
 * **NOMAD_SKIP_VERIFY** - Do not verify TLS certificate.
 * **NOMAD_TOKEN** - The SecretID of an ACL token to use to authenticate API requests with.

### Consul Client

The project also uses the Consul [Default API Client](https://github.com/hashicorp/consul/blob/master/api/api.go#L282) which means the following Consul client parameters used by Levant are configurable via environment variables:

 * **CONSUL_CACERT** - Path to a CA file to use for TLS when communicating with Consul.
 * **CONSUL_CAPATH** - Path to a directory of CA certificates to use for TLS when communicating with Consul.
 * **CONSUL_CLIENT_CERT** - Path to a client cert file to use for TLS when 'verify_incoming' is enabled.
 * **CONSUL_CLIENT_KEY** - Path to a client key file to use for TLS when 'verify_incoming' is enabled.
 * **CONSUL_HTTP_ADDR** - The `address` and port of the Consul HTTP agent. The value can be an IP address or DNS address, but it must also include the port.
 * **CONSUL_TLS_SERVER_NAME** - The server name to use as the SNI host when connecting via TLS.
 * **CONSUL_HTTP_TOKEN** - ACL token to use in the request. If unspecified, the query will default to the token of the Consul agent at the HTTP address.


================================================
FILE: docs/commands.md
================================================
## Commands

Levant supports a number of command line arguments which provide control over the Levant binary. Each command supports the `--help` flag to provide usage assistance.

### Command: `deploy`

`deploy` is the main entry point into Levant for deploying a Nomad job and supports the following flags which should then be proceeded by the Nomad job template you whish to deploy. Levant also supports autoloading files by which Levant will look in the current working directory for a `levant.[yaml,yml,tf]` file and a single `*.nomad` file to use for the command actions.

* **-address** (string: "http://localhost:4646") The HTTP API endpoint for Nomad where all calls will be made.

* **-allow-stale** (bool: false) Allow stale consistency mode for requests into nomad.

* **-canary-auto-promote** (int: 0) The time period in seconds that Levant should wait for before attempting to promote a canary deployment.

* **-consul-address** (string: "localhost:8500") The Consul host and port to use when making Consul KeyValue lookups for template rendering.

* **-force** (bool: false) Execute deployment even though there were no changes.

* **-force-batch** (bool: false) Forces a new instance of the periodic job. A new instance will be created even if it violates the job's prohibit_overlap settings.

* **-force-count** (bool: false) Use the taskgroup count from the Nomad job file instead of the count that is obtained from the running job count.

* **-ignore-no-changes** (bool: false) By default if no changes are detected when running a deployment Levant will exit with a status 1 to indicate a deployment didn't happen. This behaviour can be changed using this flag so that Levant will exit cleanly ensuring CD pipelines don't fail when no changes are detected

* **-log-level** (string: "INFO") The level at which Levant will log to. Valid values are DEBUG, INFO, WARN, ERROR and FATAL.

* **-log-format** (string: "HUMAN") Specify the format of Levant's logs. Valid values are HUMAN or JSON

* **-var-file** (string: "") The variables file to render the template with. This flag can be specified multiple times to supply multiple variables files.

The `deploy` command also supports passing variables individually on the command line. Multiple commands can be passed in the format of `-var 'key=value'`. Variables passed via the command line take precedence over the same variable declared within a passed variable file.

Full example:

```
levant deploy -log-level=debug -address=nomad.devoops -var-file=var.yaml -var 'var=test' example.nomad
```

### Dispatch: `dispatch`

`dispatch` allows you to dispatch an instance of a Nomad parameterized job and utilise Levant's advanced job checking features to ensure the job reaches the correct running state.

* **-address** (string: "http://localhost:4646") The HTTP API endpoint for Nomad where all calls will be made.

* **-log-level** (string: "INFO") The level at which Levant will log to. Valid values are DEBUG, INFO, WARN, ERROR and FATAL.

* **-log-format** (string: "HUMAN") Specify the format of Levant's logs. Valid values are HUMAN or JSON

* **-meta** (string: "key=value") The metadata key will be merged into the job's metadata. The job may define a default value for the key which is overridden when dispatching. The flag can be provided more than once to inject multiple metadata key/value pairs. Arbitrary keys are not allowed. The parameterized job must allow the key to be merged.

The command also supports the ability to send data payload to the dispatched instance. This can be provided via stdin by using "-" for the input source or by specifying a path to a file.

Full example:

```
levant dispatch -log-level=debug -address=nomad.devoops -meta key=value dispatch_job payload_item
```

### Plan: `plan`

`plan` allows you to perform a Nomad plan of a rendered template job. This is useful for seeing the expected changes before larger deploys.

* **-address** (string: "http://localhost:4646") The HTTP API endpoint for Nomad where all calls will be made.

* **-allow-stale** (bool: false) Allow stale consistency mode for requests into nomad.

* **-consul-address** (string: "localhost:8500") The Consul host and port to use when making Consul KeyValue lookups for template rendering.

* **-force-count** (bool: false) Use the taskgroup count from the Nomad job file instead of the count that is obtained from the running job count.

* **-ignore-no-changes** (bool: false) By default if no changes are detected when running a deployment Levant will exit with a status 1 to indicate a deployment didn't happen. This behaviour can be changed using this flag so that Levant will exit cleanly ensuring CD pipelines don't fail when no changes are detected

* **-log-level** (string: "INFO") The level at which Levant will log to. Valid values are DEBUG, INFO, WARN, ERROR and FATAL.

* **-log-format** (string: "HUMAN") Specify the format of Levant's logs. Valid values are HUMAN or JSON

* **-var-file** (string: "") The variables file to render the template with. This flag can be specified multiple times to supply multiple variables files.

The `plan` command also supports passing variables individually on the command line. Multiple commands can be passed in the format of `-var 'key=value'`. Variables passed via the command line take precedence over the same variable declared within a passed variable file.

Full example:

```
levant plan -log-level=debug -address=nomad.devoops -var-file=var.yaml -var 'var=test' example.nomad
```

### Command: `render`

`render` allows rendering of a Nomad job template without deploying, useful when testing or debugging. Levant also supports autoloading files by which Levant will look in the current working directory for a `levant.[yaml,yml,tf]` file and a single `*.nomad` file to use for the command actions.

* **-consul-address** (string: "localhost:8500") The Consul host and port to use when making Consul KeyValue lookups for template rendering.

* **-log-level** (string: "DEBUG") The level at which Levant will log to. Valid values are DEBUG, INFO, WARN, ERROR and FATAL.

* **-log-format** (string: "JSON") Specify the format of Levant's logs. Valid values are HUMAN or JSON

* **-var-file** (string: "") The variables file to render the template with. This flag can be specified multiple times to supply multiple variables files.

* **-out** (string: "") The path to write the rendered template to. The template will be rendered to stdout if this is not set.

Like `deploy`, the `render` command also supports passing variables individually on the command line. Multiple vars can be passed in the format of `-var 'key=value'`. Variables passed via the command line take precedence over the same variable declared within a passed variable file.

Full example:

```
levant render -var-file=var.yaml -var 'var=test' example.nomad
```

### Command: `scale-in`

The `scale-in` command allows the operator to scale a Nomad job and optional task-group within that job in/down in number. This can be helpful particulary in development and testing of new Nomad jobs or resizing.

* **-address** (string: "http://localhost:4646") The HTTP API endpoint for Nomad where all calls will be made.

* **-count** (int: 0) The count by which the job and task groups should be scaled in by. Only one of count or percent can be passed.

* **-log-level** (string: "INFO") The level at which Levant will log to. Valid values are DEBUG, INFO, WARN, ERROR and FATAL.

* **-log-format** (string: "HUMAN") Specify the format of Levant's logs. Valid values are HUMAN or JSON

* **-percent** (int: 0) A percentage value by which the job and task groups should be scaled in by. Counts will be rounded up, to ensure required capacity is met. Only one of count or percent can be passed.

* **-task-group** (string: "") The name of the task group you wish to target for scaling. If this is not specified, all task groups within the job will be scaled.

Full example:

```
levant scale-in -count 3 -task-group cache example
```

### Command: `scale-out`

The `scale-out` command allows the operator to scale a Nomad job and optional task-group within that job out/up in number. This can be helpful particulary in development and testing of new Nomad jobs or resizing.

* **-address** (string: "http://localhost:4646") The HTTP API endpoint for Nomad where all calls will be made.

* **-count** (int: 0) The count by which the job and task groups should be scaled out by. Only one of count or percent can be passed.

* **-log-level** (string: "INFO") The level at which Levant will log to. Valid values are DEBUG, INFO, WARNING, ERROR and FATAL.

* **-log-format** (string: "HUMAN") Specify the format of Levant's logs. Valid values are HUMAN or JSON

* **-percent** (int: 0) A percentage value by which the job and task groups should be scaled out by. Counts will be rounded up, to ensure required capacity is met. Only one of count or percent can be passed.

* **-task-group** (string: "") The name of the task group you wish to target for scaling. If this is not specified, all task groups within the job will be scaled.

Full example:

```
levant scale-out -percent 30 -task-group cache example
```

### Command: `version`

The `version` command displays build information about the running binary, including the release version.


================================================
FILE: docs/templates.md
================================================
## Templates

Alongside enhanced deployments of Nomad jobs; Levant provides templating functionality allowing for greater flexibility throughout your Nomad jobs files. It also allows the same job file to be used across each environment you have, meaning your operation maturity is kept high. 

### Template Substitution

Levant currently supports `.json`, `.tf`, `.yaml`, and `.yml` file extensions for the declaration of template variables and uses opening and closing double squared brackets `[[ ]]` within the templated job file. This is to ensure there is no clash with existing Nomad interpolation which uses the standard `{{ }}` notation.

#### JSON

JSON as well as YML provide the most flexible variable file format. It allows for descriptive and well organised jobs and variables file as shown below.

Example job template:
```hcl
resources {
    cpu    = [[.resources.cpu]]
    memory = [[.resources.memory]]

    network {
        mbits = [[.resources.network.mbits]]
    }
}
```

Example variable file:
```json
{
    "resources":{
        "cpu":250,
        "memory":512,
        "network":{
            "mbits":10
        }
    }
}
```

#### Terraform

Terraform (.tf) is probably the most inflexible of the variable file formats but does provide an easy to follow, descriptive manner in which to work. It may also be advantageous to use this format if you use Terraform for infrastructure as code thus allow you to use a consistant file format.

Example job template:
```hcl
resources {
    cpu    = [[.resources_cpu]]
    memory = [[.resources_memory]]

    network {
        mbits = [[.resources_network_mbits]]
    }
}
```

Example variable file:
```hcl
variable "resources_cpu" {
  description = "the CPU in MHz to allocate to the task group"
  type        = "string"
  default     = 250
}

variable "resources_memory" {
  description = "the memory in MB to allocate to the task group"
  type        = "string"
  default     = 512
}

variable "resources_network_mbits" {
  description = "the network bandwidth in MBits to allocate"
  type        = "string"
  default     = 10
}
```

#### YAML

Example job template:
```hcl
resources {
    cpu    = [[.resources.cpu]]
    memory = [[.resources.memory]]

    network {
        mbits = [[.resources.network.mbits]]
    }
}
```

Example variable file:
```yaml
---
resources:
  cpu: 250
  memory: 512
  network:
    mbits: 10
```

### Template Functions

Levant's template rendering supports a number of functions which provide flexibility when deploying jobs. As with the variable substitution, it uses opening and closing double squared brackets `[[ ]]` as not to conflict with Nomad's templating standard. Levant parses job files using the [Go Template library](https://golang.org/pkg/text/template/) which makes available the features of that library as well as the functions described below.

If you require any additional functions please raise a feature request against the project.

#### consulKey

Query Consul for the value at the given key path and render the template with the value. In the below example the value at the Consul KV path `service/config/cpu` would be `250`.

Example:
```
[[ consulKey "service/config/cpu" ]]
```

Render:
```
250
```

#### consulKeyExists

Query Consul for the value at the given key path. If the key exists, this will return true, false otherwise. This is helpful in controlling the template flow, and adding conditional logic into particular sections of the job file. In this example we could try and control where the job is configured with a particular "alerting" setup by checking for the existance of a KV in Consul.

Example:
```
{{ if consulKeyExists "service/config/alerting" }}
  <configure alerts>
{{ else }}
  <skip configure alerts>
{{ end }}
```

#### consulKeyOrDefault

Query Consul for the value at the given key path. If the key does not exist, the default value will be used instead. In the following example we query the Consul KV path `service/config/database-addr` but there is nothing at that location. If a value did exist at the path, the rendered value would be the KV value. This can be helpful when configuring jobs which defaults which are appropriate for local testing and development.

Example:
```
[[ consulKeyOrDefault "service/config/database-addr" "localhost:3306" ]]
```

Render:
```
localhost:3306
```

#### env

Returns the value of the given environment variable.

Example:
```
[[ env "HOME" ]]
[[ or (env "NON_EXISTENT") "foo" ]]
```

Render:
```
/bin/bash
foo
```


#### fileContents

Reads the entire contents of the specified file and adds it to the template.

Example file contents:
```
---
yaml:
  - is: everywhere
```

Example job template:
```
[[ fileContents "/etc/myapp/config" ]]
```

Render:
```
---
yaml:
  - is: everywhere
```


#### loop

Accepts varying parameters and differs its behavior based on those parameters as detailed below.

If loop is given a single int input, it will loop up to, but not including the given integer from index 0:

Example:
```
[[ range $i := loop 3 ]]
this-is-loop[[ $i ]][[ end ]]
```

Render:
```
this-is-output0
this-is-output1
this-is-output2
```

If given two integers, this function will begin at the first integer and loop up to but not including the second integer:

Example:
```
[[ range $i := loop 3 6 ]]
this-is-loop[[ $i ]][[ end ]]
```

Render:
```
this-is-output3
this-is-output4
this-is-output5
```

#### parseBool

Takes the given string and parses it as a boolean value which can be helpful in performing conditional checks. In the below example if the key has a value of "true" we could use it to alter what tags are added to the job:

Example:
```
[[ if "true" | parseBool ]][[ "beta-release" ]][[ end ]]
```

Render:
```
beta-release
```

#### parseFloat

Takes the given string and parses it as a base-10 float64.

Example:
```
[[ "3.14159265359" | parseFloat ]]
```

Render:
```
3.14159265359
```

#### parseInt

Takes the given string and parses it as a base-10 int64 and is typically combined with other helpers such as loop:

Example:
```
[[ with $i := consulKey "service/config/conn_pool" | parseInt ]][[ range $d := loop $i ]]
conn-pool-id-[[ $d ]][[ end ]][[ end ]]
```

Render:
```
conn-pool-id-0
conn-pool-id-1
conn-pool-id-2
```

#### parseJSON

Takes the given input and parses the result as JSON. This can allow you to wrap an entire job template as shown below and pull variables from Consul KV for template rendering. The below example is based on the template substitution above and expects the Consul KV to be `{"resources":{"cpu":250,"memory":512,"network":{"mbits":10}}}`:

Example:
```
[[ with $data := consulKey "service/config/variables" | parseJSON ]]
resources {
    cpu    = [[.resources.cpu]]
    memory = [[.resources.memory]]

    network {
        mbits = [[.resources.network.mbits]]
    }
}
[[ end ]]
```

Render:
```
resources {
    cpu    = 250
    memory = 512

    network {
        mbits = 10
    }
}
```

#### parseUint

Takes the given string and parses it as a base-10 int64.

Example:
```
[[ "100" | parseUint ]]
```

Render:
```
100
```

#### replace

Replaces all occurrences of the search string with the replacement string.

Example:
```
[[ "Batman and Robin" | replace "Robin" "Catwoman" ]]
```

Render:
```
Batman and Catwoman
```

#### timeNow

Returns the current ISO_8601 standard timestamp as a string in the timezone of the machine the rendering was triggered on.

Example:
```
[[ timeNow ]]
```

Render:
```
2018-06-25T09:45:08+02:00
```

#### timeNowUTC

Returns the current ISO_8601 standard timestamp as a string in UTC.

Example:
```
[[ timeNowUTC ]]
```

Render:
```
2018-06-25T07:45:08Z
```

#### timeNowTimezone

Returns the current ISO_8601 standard timestamp as a string of the timezone specified. The timezone must be specified according to the entries in the IANA Time Zone database, such as "ASIA/SEOUL". Details of the entries can be found on [wikipedia](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) or your local workstation (Mac or BSD) by searching within `/usr/share/zoneinfo`.

Example:
```
[[ timeNowTimezone "ASIA/SEOUL" ]]
```

Render:
```
2018-06-25T16:45:08+09:00
```

#### toLower

Takes the argument as a string and converts it to lowercase.

Example:
```
[[ "QUEUE-NAME" | toLower ]]
```

Render:
```
queue-name
```

#### toUpper

Takes the argument as a string and converts it to uppercase.

Example:
```
[[ "queue-name" | toUpper ]]
```

Render:
```
QUEUE-NAME
```

#### add

Returns the sum of the two passed values.

Examples:
```
[[ add 5 2 ]]
```

Render:
```
7
```

#### subtract

Returns the difference of the second value from the first.

Example:
```
[[ subtract 2 5 ]]
```

Render:
```
3
```

#### multiply

Returns the product of the two values.

Example:
```
[[ multiply 4 4 ]]
```

Render:
```
16
```

#### divide

Returns the division of the second value from the first.

Example:
```
[[ divide 2 6 ]]
```

Render:
```
3
```

#### modulo

Returns the modulo of the second value from the first.

Example:
```
[[ modulo 2 5 ]]
```

Render:
```
1
```

#### Access Variable Globally

Example config file:
```yaml
my_i32: 1
my_array:
  - "a"
  - "b"
  - "c"
my_nested:
  my_data1: "lorempium"
  my_data2: "faker"
```

Template:
```
[[ $.my_i32 ]]
[[ range $c := $.my_array ]][[ $c ]]-[[ $.my_i32 ]],[[ end ]]
```

Render:
```
1
a1,b1,c1,
```

#### Sprig Template
More about Sprig here: [Sprig](https://masterminds.github.io/sprig/)

#### Sprig Join String

Template: 
```
[[ $.my_array | sprigJoin `-` ]]
```

Render:
```
a-b-c
```

#### Define variable

Template:
```
[[ with $data := $.my_nested ]]
ENV_x1=[[ $data.my_data1 ]]
ENV_x2=[[ $data.my_data2 ]]
[[ end ]] 
```

Render:
```

ENV_x1=lorempium
ENV_x2=faker

```


================================================
FILE: go.mod
================================================
module github.com/hashicorp/levant

go 1.24.4

// Use the same version of go-metrics as Nomad.
replace github.com/armon/go-metrics => github.com/armon/go-metrics v0.0.0-20230509193637-d9ca9af9f1f9

require (
	github.com/Masterminds/sprig/v3 v3.3.0
	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
	github.com/hashicorp/consul/api v1.32.1
	github.com/hashicorp/go-multierror v1.1.1
	github.com/hashicorp/nomad v1.10.2
	github.com/hashicorp/nomad/api v0.0.0-20250620152331-1030760d3f77
	github.com/hashicorp/terraform v0.13.5
	github.com/mattn/go-isatty v0.0.20
	github.com/mitchellh/cli v1.1.5
	github.com/pkg/errors v0.9.1
	github.com/rs/zerolog v1.6.0
	github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627
	github.com/stretchr/testify v1.10.0
	gopkg.in/yaml.v2 v2.4.0
)

require (
	dario.cat/mergo v1.0.1 // indirect
	github.com/Masterminds/goutils v1.1.1 // indirect
	github.com/Masterminds/semver/v3 v3.3.0 // indirect
	github.com/agext/levenshtein v1.2.2 // indirect
	github.com/apparentlymart/go-cidr v1.1.0 // indirect
	github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
	github.com/apparentlymart/go-versions v1.0.0 // indirect
	github.com/armon/go-metrics v0.4.1 // indirect
	github.com/armon/go-radix v1.0.0 // indirect
	github.com/bgentry/speakeasy v0.1.0 // indirect
	github.com/bmatcuk/doublestar v1.1.5 // indirect
	github.com/fatih/color v1.18.0 // indirect
	github.com/google/go-cmp v0.7.0 // indirect
	github.com/google/uuid v1.6.0 // indirect
	github.com/gorilla/websocket v1.5.3 // indirect
	github.com/hashicorp/cronexpr v1.1.2 // indirect
	github.com/hashicorp/errwrap v1.1.0 // indirect
	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
	github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840 // indirect
	github.com/hashicorp/go-hclog v1.6.3 // indirect
	github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
	github.com/hashicorp/go-metrics v0.5.4 // indirect
	github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
	github.com/hashicorp/go-rootcerts v1.0.2 // indirect
	github.com/hashicorp/go-version v1.7.0 // indirect
	github.com/hashicorp/golang-lru v1.0.2 // indirect
	github.com/hashicorp/hcl/v2 v2.20.2-0.20240517235513-55d9c02d147d // indirect
	github.com/hashicorp/hil v0.0.0-20210521165536-27a72121fd40 // indirect
	github.com/hashicorp/serf v0.10.2 // indirect
	github.com/hashicorp/terraform-svchost v0.0.0-20191011084731-65d371908596 // indirect
	github.com/huandu/xstrings v1.5.0 // indirect
	github.com/mattn/go-colorable v0.1.14 // indirect
	github.com/mitchellh/copystructure v1.2.0 // indirect
	github.com/mitchellh/go-homedir v1.1.0 // indirect
	github.com/mitchellh/go-wordwrap v1.0.1 // indirect
	github.com/mitchellh/mapstructure v1.5.0 // indirect
	github.com/mitchellh/reflectwalk v1.0.2 // indirect
	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
	github.com/posener/complete v1.2.3 // indirect
	github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5 // indirect
	github.com/shopspring/decimal v1.4.0 // indirect
	github.com/spf13/afero v1.2.2 // indirect
	github.com/spf13/cast v1.7.0 // indirect
	github.com/zclconf/go-cty v1.16.3 // indirect
	github.com/zclconf/go-cty-yaml v1.1.0 // indirect
	golang.org/x/crypto v0.38.0 // indirect
	golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
	golang.org/x/mod v0.25.0 // indirect
	golang.org/x/net v0.40.0 // indirect
	golang.org/x/oauth2 v0.30.0 // indirect
	golang.org/x/sync v0.15.0 // indirect
	golang.org/x/sys v0.33.0 // indirect
	golang.org/x/text v0.25.0 // indirect
	golang.org/x/tools v0.33.0 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)


================================================
FILE: go.sum
================================================
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/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/Azure/azure-sdk-for-go v45.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.0/go.mod h1:JljT387FplPzBA31vUcvsetLKF3pec5bdAxjVU4kI2s=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/Azure/go-autorest/autorest/validation v0.3.0/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
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/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
github.com/ChrisTrenkamp/goxpath v0.0.0-20190607011252-c5096ec8773d/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=
github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190329064014-6e358769c32a/go.mod h1:T9M45xf79ahXVelWoOBmH0y4aC1t5kXO5BxwyakgIGA=
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190103054945-8205d1f41e70/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible/go.mod h1:LDQHRZylxvcg8H7wBIDfvO5g/cy4/sz1iucBlc2l3Jw=
github.com/antchfx/xpath v0.0.0-20190129040759-c8489ed3251e/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
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-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I=
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/apparentlymart/go-userdirs v0.0.0-20200915174352-b0c018a67c13/go.mod h1:7kfpUbyCdGJ9fDRCp3fopPQi5+cKNHgTE4ZuNrO71Cw=
github.com/apparentlymart/go-versions v1.0.0 h1:4A4CekGuwDUQqc+uTXCrdb9Y98JZsML2sdfNTeVjsK4=
github.com/apparentlymart/go-versions v1.0.0/go.mod h1:YF5j7IQtrOAOnsGkniupEA5bfCjzd7i14yu0shZavyM=
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20230509193637-d9ca9af9f1f9 h1:51N4T44k8crLrlHy1zgBKGdYKjzjquaXw/RPbq/bH+o=
github.com/armon/go-metrics v0.0.0-20230509193637-d9ca9af9f1f9/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bmatcuk/doublestar v1.1.5 h1:2bNwBOmhyFEFcoB3tGvTD5xanq+4kyOZlB8wFYbMjkk=
github.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.0/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
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/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=
github.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1/go.mod h1:lcy9/2gH1jn/VCLouHA6tOEwLoNVd4GW6zhuKLmHC2Y=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
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/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
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.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
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.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.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
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/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/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/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/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.6.1-0.20191122030953-d8ac278c1c9d/go.mod h1:ozGNgr9KYOVATV5jsgHl/ceCDXGuguqOZAzoQ/2vcNM=
github.com/gophercloud/gophercloud v0.10.1-0.20200424014253-c3bfe50899e5/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss=
github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d/go.mod h1:ehWUbLQJPqS0Ep+CxeD559hsm9pthPXadJNKwZkp43w=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/aws-sdk-go-base v0.6.0/go.mod h1:2fRjWDv3jJBeN6mVWFHV6hFTNeFBx2gpDLQaZNxUVAY=
github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089 h1:1eDpXAxTh0iPv+1kc9/gfSI2pxRERDsTk/lNGolwHn8=
github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE=
github.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4=
github.com/hashicorp/consul/sdk v0.16.2 h1:cGX/djeEe9r087ARiKVWwVWCF64J+yW0G6ftZMZYbj0=
github.com/hashicorp/consul/sdk v0.16.2/go.mod h1:onxcZjYVsPx5XMveAC/OtoIsdr32fykB7INFltDoRE8=
github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A=
github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-azure-helpers v0.12.0/go.mod h1:Zc3v4DNeX6PDdy7NljlYpnrdac1++qNW0I4U+ofGwpg=
github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840 h1:kgvybwEeu0SXktbB2y3uLHX9lklLo+nzUwh59A3jzQc=
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA=
github.com/hashicorp/go-getter v1.4.2-0.20200106182914-9813cbd4eb02/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY=
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
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-immutable-radix v0.0.0-20180129170900-7f3cd4390caa/go.mod h1:6ij3Z20p+OhOkCSrA0gImAWoHYQRGbnlcuk6XYTiaRw=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY=
github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI=
github.com/hashicorp/go-msgpack v0.5.4/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-msgpack v1.1.6-0.20240304204939-8824e8ccc35f h1:/xqzTen8ftnKv3cKa87WEoOLtsDJYFU0ArjrKaPTTkc=
github.com/hashicorp/go-msgpack/v2 v2.1.3 h1:cB1w4Zrk0O3jQBTcFMKqYQWRFfsSQ/TYKNyUUVyCP2c=
github.com/hashicorp/go-msgpack/v2 v2.1.3/go.mod h1:SjlwKKFnwBXvxD/I1bEcfJIBbEJ+MCUn39TxymNR5ZU=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-plugin v1.3.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0=
github.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
github.com/hashicorp/go-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8=
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw=
github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw=
github.com/hashicorp/go-tfe v0.8.1/go.mod h1:XAV72S4O1iP8BDaqiaPLmL2B4EE6almocnOn8E8stHc=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
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.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.2.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.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90=
github.com/hashicorp/hcl/v2 v2.6.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
github.com/hashicorp/hcl/v2 v2.20.2-0.20240517235513-55d9c02d147d h1:7abftkc86B+tlA/0cDy5f6C4LgWfFOCpsGg3RJZsfbw=
github.com/hashicorp/hcl/v2 v2.20.2-0.20240517235513-55d9c02d147d/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
github.com/hashicorp/hil v0.0.0-20190212112733-ab17b08d6590/go.mod h1:n2TSygSNwsLJ76m8qFXTSc7beTb+auJxYdqrnoqwZWE=
github.com/hashicorp/hil v0.0.0-20210521165536-27a72121fd40 h1:ExwaL+hUy1ys2AWDbsbh/lxQS2EVCYxuj0LoyLTdB3Y=
github.com/hashicorp/hil v0.0.0-20210521165536-27a72121fd40/go.mod h1:n2TSygSNwsLJ76m8qFXTSc7beTb+auJxYdqrnoqwZWE=
github.com/hashicorp/memberlist v0.1.0/go.mod h1:ncdBp14cuox2iFOq3kDiquKU6fqsTBc3W6JvZwjxxsE=
github.com/hashicorp/memberlist v0.5.3 h1:tQ1jOCypD0WvMemw/ZhhtH+PWpzcftQvgCorLu0hndk=
github.com/hashicorp/memberlist v0.5.3/go.mod h1:h60o12SZn/ua/j0B6iKAZezA4eDaGsIuPO70eOaJ6WE=
github.com/hashicorp/nomad v1.10.2 h1:0yKNxoCgBGWIeDOcYBzXXdLh6JBsuF7nO9td64flC/s=
github.com/hashicorp/nomad v1.10.2/go.mod h1:S/FXen41m3/BPsKEXfatDxmGJdp8lMhWg9FLBbTKsz4=
github.com/hashicorp/nomad/api v0.0.0-20250620152331-1030760d3f77 h1:5PhbqQJDNugTEhPbA/0uVdK21SKhgkfMmRifTICCqIw=
github.com/hashicorp/nomad/api v0.0.0-20250620152331-1030760d3f77/go.mod h1:y4olHzVXiQolzyk6QD/gqJxQTnnchlTf/QtczFFKwOI=
github.com/hashicorp/serf v0.0.0-20160124182025-e4ec8cc423bb/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE=
github.com/hashicorp/serf v0.10.2 h1:m5IORhuNSjaxeljg5DeQVDlQyVkhRIjJDimbkCa8aAc=
github.com/hashicorp/serf v0.10.2/go.mod h1:T1CmSGfSeGfnfNy/w0odXQUR1rfECGd2Qdsp84DjOiY=
github.com/hashicorp/terraform v0.13.5 h1:QgPgb/pOBclUMymDji9255FlseySf9dRcUMzJws9BAQ=
github.com/hashicorp/terraform v0.13.5/go.mod h1:1H1qcnppNc/bBGc7poOfnmmBeQMlF0stEN3haY3emCU=
github.com/hashicorp/terraform-config-inspect v0.0.0-20191212124732-c6ae6269b9d7/go.mod h1:p+ivJws3dpqbp1iP84+npOyAmTTOLMgCzrXd3GSdn/A=
github.com/hashicorp/terraform-svchost v0.0.0-20191011084731-65d371908596 h1:hjyO2JsNZUKT1ym+FAdlBEkGPevazYsmVgIMw7dVELg=
github.com/hashicorp/terraform-svchost v0.0.0-20191011084731-65d371908596/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg=
github.com/hashicorp/vault v0.10.4/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/keybase/go-crypto v0.0.0-20161004153544-93f5b35093ba/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.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=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/likexian/gokit v0.0.0-20190309162924-0a377eecf7aa/go.mod h1:QdfYv6y6qPA9pbBA2qXtoT8BMKha6UyNbxWGWl/9Jfk=
github.com/likexian/gokit v0.0.0-20190418170008-ace88ad0983b/go.mod h1:KKqSnk/VVSW8kEyO2vVCXoanzEutKdlBAPohmGXkxCk=
github.com/likexian/gokit v0.0.0-20190501133040-e77ea8b19cdc/go.mod h1:3kvONayqCaj+UgrRZGpgfXzHdMYCAO0KAt4/8n0L57Y=
github.com/likexian/gokit v0.20.15/go.mod h1:kn+nTv3tqh6yhor9BC4Lfiu58SmH8NmQ2PmEl+uM6nU=
github.com/likexian/simplejson-go v0.0.0-20190409170913-40473a74d76d/go.mod h1:Typ1BfnATYtZ/+/shXfFYLrovhFyuKvzwrdOnIDHlmg=
github.com/likexian/simplejson-go v0.0.0-20190419151922-c1f9f0b4f084/go.mod h1:U4O1vIJvIKwbMZKUJ62lppfdvkCdVd2nfMimHK81eec=
github.com/likexian/simplejson-go v0.0.0-20190502021454-d8787b4bfa0b/go.mod h1:3BWwtmKP9cXWwYCr5bkoVDEfLywacOv0s06OBEDpyt8=
github.com/lusis/go-artifactory v0.0.0-20160115162124-7e4ce345df82/go.mod h1:y54tfGmO3NKssKveTEFFzH8C/akrSOy/iW9qEAUDV84=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88/go.mod h1:a2HXwefeat3evJHxFXSayvRHpYEPJYtErl4uIzfaUqY=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
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=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-shellwords v1.0.4/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng=
github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
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.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-linereader v0.0.0-20190213213312-1b945b3263eb/go.mod h1:OaY7UOoTkkrX3wRwjpYRKafIkkyeD0UtweSHAWWiqQM=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/panicwrap v1.0.0/go.mod h1:pKvZHwWrZowLUzftuFq7coarnxbBXU4aQh3N0BJOeeA=
github.com/mitchellh/prefixedio v0.0.0-20190213213902-5733675afd51/go.mod h1:kB1naBgV9ORnkiTVeyJOI1DavaJkG4oNIq0Af6ZVKUo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.1/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DKN0g6uH7E=
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/zerolog v1.6.0 h1:MIbb0f94AuCzp0f4qdwfbi20VM8JcjoLbahiBHJEqwY=
github.com/rs/zerolog v1.6.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627 h1:Tn2Iev07a4oOcAuFna8AJxDOF/M+6OkNbpEZLX30D6M=
github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627/go.mod h1:7zjs06qF79/FKAJpBvFx3P8Ww4UTIMAe+lpNXDHziac=
github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5 h1:D07EBYJLI26GmLRKNtrs47p8vs/5QqpUX3VcwsAPkEo=
github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6qbIiqJ6/Bqeq25bCLbL7YFmpaFfJDuM=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shoenig/test v1.12.1 h1:mLHfnMv7gmhhP44WrvT+nKSxKkPDiNkIuHGdIGI9RLU=
github.com/shoenig/test v1.12.1/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d/go.mod h1:BSTlc8jOjh0niykqEGVXOLXdi9o0r0kR8tCYiMvjFgw=
github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c/go.mod h1:wk2XFUg6egk4tSDNZtXeKfe2G6690UVyt163PuUxBZk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tombuildsstuff/giovanni v0.12.0/go.mod h1:qJ5dpiYWkRsuOSXO8wHbee7+wElkLNfWVolcf59N84E=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
github.com/zclconf/go-cty v1.5.1/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk=
github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zclconf/go-cty-yaml v1.0.2/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=
github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=
github.com/zclconf/go-cty-yaml v1.1.0/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JAprQNgxSZ1Gyxs=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/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-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/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-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
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/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/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-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
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.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/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-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-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-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
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=
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/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/
Download .txt
gitextract_ffa58g6j/

├── .dockerignore
├── .github/
│   ├── CODEOWNERS
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       ├── actionlint.yml
│       ├── build.yml
│       ├── ci.yml
│       ├── nightly-release-readme.md
│       └── nightly-release.yml
├── .gitignore
├── .go-version
├── .golangci.yml
├── .release/
│   ├── ci.hcl
│   ├── levant-artifacts.hcl
│   ├── release-metadata.hcl
│   └── security-scan.hcl
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Dockerfile
├── GNUmakefile
├── LICENSE
├── README.md
├── client/
│   ├── consul.go
│   └── nomad.go
├── command/
│   ├── deploy.go
│   ├── deploy_test.go
│   ├── dispatch.go
│   ├── meta.go
│   ├── plan.go
│   ├── render.go
│   ├── scale_in.go
│   ├── scale_out.go
│   ├── test-fixtures/
│   │   ├── group_canary.nomad
│   │   ├── job_canary.nomad
│   │   └── periodic_batch.nomad
│   └── version.go
├── commands.go
├── docs/
│   ├── README.md
│   ├── clients.md
│   ├── commands.md
│   └── templates.md
├── go.mod
├── go.sum
├── helper/
│   ├── files.go
│   ├── files_test.go
│   ├── kvflag.go
│   ├── kvflag_test.go
│   ├── nomad/
│   │   ├── opts.go
│   │   └── opts_test.go
│   ├── variable.go
│   └── variable_test.go
├── levant/
│   ├── auto_revert.go
│   ├── deploy.go
│   ├── dispatch.go
│   ├── failure_inspector.go
│   ├── job_status_checker.go
│   ├── job_status_checker_test.go
│   ├── plan.go
│   └── structs/
│       └── config.go
├── logging/
│   └── logging.go
├── main.go
├── scale/
│   ├── scale.go
│   └── scale_test.go
├── scripts/
│   ├── docker-entrypoint.sh
│   └── version.sh
├── template/
│   ├── funcs.go
│   ├── render.go
│   ├── render_test.go
│   ├── template.go
│   └── test-fixtures/
│       ├── missing_var.nomad
│       ├── multi_templated.nomad
│       ├── none_templated.nomad
│       ├── single_templated.nomad
│       ├── test-overwrite.yaml
│       ├── test.tf
│       └── test.yaml
├── test/
│   ├── acctest/
│   │   ├── acctest.go
│   │   └── deploy.go
│   ├── deploy_test.go
│   └── fixtures/
│       ├── deploy_alloc_error.nomad
│       ├── deploy_basic.nomad
│       ├── deploy_canary.nomad
│       ├── deploy_count.nomad
│       ├── deploy_driver_error.nomad
│       ├── deploy_lifecycle.nomad
│       ├── deploy_namespace.nomad
│       └── deploy_task_scaling.nomad
└── version/
    ├── version.go
    └── version_test.go
Download .txt
SYMBOL INDEX (196 symbols across 41 files)

FILE: client/consul.go
  function NewConsulClient (line 11) | func NewConsulClient(addr string) (*consul.Client, error) {

FILE: client/nomad.go
  function NewNomadClient (line 11) | func NewNomadClient(addr string) (*nomad.Client, error) {

FILE: command/deploy.go
  type DeployCommand (line 20) | type DeployCommand struct
    method Help (line 25) | func (c *DeployCommand) Help() string {
    method Synopsis (line 92) | func (c *DeployCommand) Synopsis() string {
    method Run (line 97) | func (c *DeployCommand) Run(args []string) int {
    method checkCanaryAutoPromote (line 193) | func (c *DeployCommand) checkCanaryAutoPromote(job *nomad.Job, canaryA...
    method checkForceBatch (line 213) | func (c *DeployCommand) checkForceBatch(job *nomad.Job, forceBatch boo...

FILE: command/deploy_test.go
  function TestDeploy_checkCanaryAutoPromote (line 12) | func TestDeploy_checkCanaryAutoPromote(t *testing.T) {
  function TestDeploy_checkForceBatch (line 48) | func TestDeploy_checkForceBatch(t *testing.T) {

FILE: command/dispatch.go
  type DispatchCommand (line 19) | type DispatchCommand struct
    method Help (line 24) | func (c *DispatchCommand) Help() string {
    method Synopsis (line 60) | func (c *DispatchCommand) Synopsis() string {
    method Run (line 65) | func (c *DispatchCommand) Run(args []string) int {

FILE: command/meta.go
  type FlagSetFlags (line 17) | type FlagSetFlags
  constant FlagSetNone (line 21) | FlagSetNone        FlagSetFlags = 0
  constant FlagSetBuildFilter (line 22) | FlagSetBuildFilter FlagSetFlags = 1 << iota
  constant FlagSetVars (line 23) | FlagSetVars
  type Meta (line 28) | type Meta struct
    method FlagSet (line 37) | func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {

FILE: command/plan.go
  type PlanCommand (line 19) | type PlanCommand struct
    method Help (line 24) | func (c *PlanCommand) Help() string {
    method Synopsis (line 80) | func (c *PlanCommand) Synopsis() string {
    method Run (line 85) | func (c *PlanCommand) Run(args []string) int {

FILE: command/render.go
  type RenderCommand (line 19) | type RenderCommand struct
    method Help (line 24) | func (c *RenderCommand) Help() string {
    method Synopsis (line 66) | func (c *RenderCommand) Synopsis() string {
    method Run (line 71) | func (c *RenderCommand) Run(args []string) int {

FILE: command/scale_in.go
  type ScaleInCommand (line 16) | type ScaleInCommand struct
    method Help (line 21) | func (c *ScaleInCommand) Help() string {
    method Synopsis (line 63) | func (c *ScaleInCommand) Synopsis() string {
    method Run (line 68) | func (c *ScaleInCommand) Run(args []string) int {

FILE: command/scale_out.go
  type ScaleOutCommand (line 16) | type ScaleOutCommand struct
    method Help (line 21) | func (c *ScaleOutCommand) Help() string {
    method Synopsis (line 63) | func (c *ScaleOutCommand) Synopsis() string {
    method Run (line 68) | func (c *ScaleOutCommand) Run(args []string) int {

FILE: command/version.go
  type VersionCommand (line 13) | type VersionCommand struct
    method Help (line 19) | func (c *VersionCommand) Help() string {
    method Synopsis (line 24) | func (c *VersionCommand) Synopsis() string {
    method Run (line 29) | func (c *VersionCommand) Run(_ []string) int {

FILE: commands.go
  function Commands (line 17) | func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {

FILE: helper/files.go
  function GetDefaultTmplFile (line 15) | func GetDefaultTmplFile() (templateFile string) {
  function GetDefaultVarFile (line 28) | func GetDefaultVarFile() (varFile string) {

FILE: helper/files_test.go
  function TestHelper_GetDefaultTmplFile (line 12) | func TestHelper_GetDefaultTmplFile(t *testing.T) {
  function TestHelper_GetDefaultVarFile (line 55) | func TestHelper_GetDefaultVarFile(t *testing.T) {

FILE: helper/kvflag.go
  type Flag (line 13) | type Flag
    method String (line 15) | func (v *Flag) String() string {
    method Set (line 21) | func (v *Flag) Set(raw string) error {
  type FlagStringSlice (line 58) | type FlagStringSlice
    method String (line 60) | func (v *FlagStringSlice) String() string {
    method Set (line 66) | func (v *FlagStringSlice) Set(raw string) error {

FILE: helper/kvflag_test.go
  function TestHelper_Set (line 14) | func TestHelper_Set(t *testing.T) {

FILE: helper/nomad/opts.go
  function GenerateBlockingQueryOptions (line 10) | func GenerateBlockingQueryOptions(ns *string) *api.QueryOptions {

FILE: helper/nomad/opts_test.go
  function Test_GenerateBlockingQueryOptions (line 13) | func Test_GenerateBlockingQueryOptions(t *testing.T) {
  function stringToPtr (line 44) | func stringToPtr(s string) *string { return &s }

FILE: helper/variable.go
  function VariableMerge (line 13) | func VariableMerge(fileVars, flagVars *map[string]interface{}) map[strin...

FILE: helper/variable_test.go
  function TestHelper_VariableMerge (line 11) | func TestHelper_VariableMerge(t *testing.T) {

FILE: levant/auto_revert.go
  method autoRevert (line 14) | func (l *levantDeployment) autoRevert(dep *nomad.Deployment) {
  method checkAutoRevert (line 56) | func (l *levantDeployment) checkAutoRevert(dep *nomad.Deployment) {

FILE: levant/deploy.go
  constant jobStatusRunning (line 19) | jobStatusRunning = "running"
  type levantDeployment (line 24) | type levantDeployment struct
    method preDeployValidate (line 89) | func (l *levantDeployment) preDeployValidate() (success bool) {
    method deploy (line 115) | func (l *levantDeployment) deploy() (success bool) {
    method evaluationInspector (line 208) | func (l *levantDeployment) evaluationInspector(evalID *string) error {
    method deploymentWatcher (line 270) | func (l *levantDeployment) deploymentWatcher(depID string) (success bo...
    method checkDeploymentStatus (line 324) | func (l *levantDeployment) checkDeploymentStatus(dep *nomad.Deployment...
    method canaryAutoPromote (line 348) | func (l *levantDeployment) canaryAutoPromote(depID string, waitTime in...
    method checkCanaryDeploymentHealth (line 385) | func (l *levantDeployment) checkCanaryDeploymentHealth(depID string) (...
    method triggerPeriodic (line 422) | func (l *levantDeployment) triggerPeriodic(jobID *string) (evalID stri...
    method getDeploymentID (line 436) | func (l *levantDeployment) getDeploymentID(evalID string) (depID strin...
    method dynamicGroupCountUpdater (line 467) | func (l *levantDeployment) dynamicGroupCountUpdater() error {
    method isJobZeroCount (line 507) | func (l *levantDeployment) isJobZeroCount() bool {
  type DeployConfig (line 30) | type DeployConfig struct
  function newLevantDeployment (line 39) | func newLevantDeployment(config *DeployConfig, nomadClient *nomad.Client...
  function TriggerDeployment (line 62) | func TriggerDeployment(config *DeployConfig, nomadClient *nomad.Client) ...

FILE: levant/dispatch.go
  function TriggerDispatch (line 15) | func TriggerDispatch(job string, metaMap map[string]string, payload []by...
  method dispatch (line 41) | func (l *levantDeployment) dispatch(job string, metaMap map[string]strin...

FILE: levant/failure_inspector.go
  method checkFailedDeployment (line 16) | func (l *levantDeployment) checkFailedDeployment(depID *string) {
  method allocInspector (line 55) | func (l *levantDeployment) allocInspector(allocID string, wg *sync.WaitG...

FILE: levant/job_status_checker.go
  type TaskCoordinate (line 13) | type TaskCoordinate struct
  method jobStatusChecker (line 21) | func (l *levantDeployment) jobStatusChecker(evalID *string) bool {
  method simpleJobStatusChecker (line 46) | func (l *levantDeployment) simpleJobStatusChecker() bool {
  method jobAllocationChecker (line 82) | func (l *levantDeployment) jobAllocationChecker(evalID *string) bool {
  function allocationStatusChecker (line 124) | func allocationStatusChecker(levantTasks map[TaskCoordinate]string, allo...

FILE: levant/job_status_checker_test.go
  function TestJobStatusChecker_allocationStatusChecker (line 12) | func TestJobStatusChecker_allocationStatusChecker(t *testing.T) {

FILE: levant/plan.go
  constant diffTypeAdded (line 16) | diffTypeAdded  = "Added"
  constant diffTypeEdited (line 17) | diffTypeEdited = "Edited"
  constant diffTypeNone (line 18) | diffTypeNone   = "None"
  type levantPlan (line 21) | type levantPlan struct
    method plan (line 75) | func (lp *levantPlan) plan() (bool, error) {
  type PlanConfig (line 27) | type PlanConfig struct
  function newPlan (line 33) | func newPlan(config *PlanConfig) (*levantPlan, error) {
  function TriggerPlan (line 48) | func TriggerPlan(config *PlanConfig) (bool, bool) {
  function planDiff (line 109) | func planDiff(plan *nomad.JobDiff) {
  function recurseObjDiff (line 135) | func recurseObjDiff(g, t string, objDiff *nomad.ObjectDiff) {
  function logDiffObj (line 160) | func logDiffObj(g, t, objName, fName, fOld, fNew string) {

FILE: levant/structs/config.go
  constant JobIDContextField (line 11) | JobIDContextField = "job_id"
  constant ScalingDirectionOut (line 14) | ScalingDirectionOut = "Out"
  constant ScalingDirectionIn (line 17) | ScalingDirectionIn = "In"
  constant ScalingDirectionTypeCount (line 20) | ScalingDirectionTypeCount = "Count"
  constant ScalingDirectionTypePercent (line 23) | ScalingDirectionTypePercent = "Percent"
  type DeployConfig (line 28) | type DeployConfig struct
  type ClientConfig (line 52) | type ClientConfig struct
  type PlanConfig (line 67) | type PlanConfig struct
  type TemplateConfig (line 75) | type TemplateConfig struct
  type ScaleConfig (line 89) | type ScaleConfig struct

FILE: logging/logging.go
  function SetupLogger (line 25) | func SetupLogger(level, format string) (err error) {
  function setLogLevel (line 38) | func setLogLevel(level string) error {
  function setLogFormat (line 57) | func setLogFormat(format string) error {

FILE: main.go
  function main (line 13) | func main() {
  function Run (line 19) | func Run(args []string) int {
  function RunCustom (line 24) | func RunCustom(args []string, commands map[string]cli.CommandFactory) int {

FILE: scale/scale.go
  type Config (line 15) | type Config struct
  function TriggerScalingEvent (line 22) | func TriggerScalingEvent(config *Config) bool {
  function updateJob (line 57) | func updateJob(client *nomad.Client, config *Config) *nomad.Job {
  function updateTaskGroup (line 96) | func updateTaskGroup(config *Config, group *nomad.TaskGroup) {
  function calculateCountBasedOnPercent (line 130) | func calculateCountBasedOnPercent(count, percent int) int {

FILE: scale/scale_test.go
  function TestScale_updateTaskGroup (line 13) | func TestScale_updateTaskGroup(t *testing.T) {
  function TestScale_calculateCountBasedOnPercent (line 56) | func TestScale_calculateCountBasedOnPercent(t *testing.T) {
  function buildScalingConfig (line 89) | func buildScalingConfig(direction, dType string, number int) *Config {
  function buildTaskGroup (line 108) | func buildTaskGroup(count int) *nomad.TaskGroup {

FILE: template/funcs.go
  function funcMap (line 28) | func funcMap(consulClient *consul.Client) template.FuncMap {
  constant SprigVersion (line 87) | SprigVersion = "3.1.0"
  function sprigVersionFunc (line 89) | func sprigVersionFunc() func(string) (string, error) {
  function consulKeyFunc (line 95) | func consulKeyFunc(consulClient *consul.Client) func(string) (string, er...
  function consulKeyExistsFunc (line 119) | func consulKeyExistsFunc(consulClient *consul.Client) func(string) (bool...
  function consulKeyOrDefaultFunc (line 141) | func consulKeyOrDefaultFunc(consulClient *consul.Client) func(string, st...
  function loop (line 167) | func loop(ints ...int64) (<-chan int64, error) {
  function parseBool (line 191) | func parseBool(s string) (bool, error) {
  function parseFloat (line 203) | func parseFloat(s string) (float64, error) {
  function parseInt (line 215) | func parseInt(s string) (int64, error) {
  function parseJSON (line 227) | func parseJSON(s string) (interface{}, error) {
  function parseUint (line 239) | func parseUint(s string) (uint64, error) {
  function replace (line 251) | func replace(input, from, to string) string {
  function timeNowFunc (line 255) | func timeNowFunc() string {
  function timeNowUTCFunc (line 259) | func timeNowUTCFunc() string {
  function timeNowTimezoneFunc (line 263) | func timeNowTimezoneFunc() func(string) (string, error) {
  function toLower (line 279) | func toLower(s string) (string, error) {
  function toUpper (line 283) | func toUpper(s string) (string, error) {
  function envFunc (line 287) | func envFunc() func(string) (string, error) {
  function fileContents (line 296) | func fileContents() func(string) (string, error) {
  function add (line 309) | func add(b, a interface{}) (interface{}, error) {
  function subtract (line 360) | func subtract(b, a interface{}) (interface{}, error) {
  function multiply (line 411) | func multiply(b, a interface{}) (interface{}, error) {
  function divide (line 462) | func divide(b, a interface{}) (interface{}, error) {
  function modulo (line 513) | func modulo(b, a interface{}) (interface{}, error) {
  function firstRuneToUpper (line 549) | func firstRuneToUpper(s string) (string, error) {
  function runeToUpper (line 553) | func runeToUpper(inString string, runeIndex int) (string, error) {
  function firstRuneToLower (line 557) | func firstRuneToLower(s string) (string, error) {
  function runeToLower (line 561) | func runeToLower(inString string, runeIndex int) (string, error) {
  function funcOnRune (line 565) | func funcOnRune(inFunc func(rune) rune, inString string, runeIndex int) ...
  function spewDump (line 585) | func spewDump(a interface{}) (string, error) {
  function spewPrintf (line 589) | func spewPrintf(format string, args ...interface{}) (string, error) {

FILE: template/render.go
  function RenderJob (line 25) | func RenderJob(templateFile string, variableFiles []string, addr string,...
  function RenderTemplate (line 37) | func RenderTemplate(templateFile string, variableFiles []string, addr st...
  method parseJSONVars (line 104) | func (t *tmpl) parseJSONVars(variableFile string) (variables map[string]...
  method parseTFVars (line 119) | func (t *tmpl) parseTFVars(variableFile string) (map[string]interface{},...
  method parseYAMLVars (line 137) | func (t *tmpl) parseYAMLVars(variableFile string) (variables map[string]...
  method renderTemplate (line 151) | func (t *tmpl) renderTemplate(src string, variables map[string]interface...

FILE: template/render_test.go
  constant testJobName (line 14) | testJobName           = "levantExample"
  constant testJobNameOverwrite (line 15) | testJobNameOverwrite  = "levantExampleOverwrite"
  constant testJobNameOverwrite2 (line 16) | testJobNameOverwrite2 = "levantExampleOverwrite2"
  constant testDCName (line 17) | testDCName            = "dc13"
  constant testEnvName (line 18) | testEnvName           = "GROUP_NAME_ENV"
  constant testEnvValue (line 19) | testEnvValue          = "cache"
  function TestTemplater_RenderTemplate (line 22) | func TestTemplater_RenderTemplate(t *testing.T) {

FILE: template/template.go
  type tmpl (line 14) | type tmpl struct
    method newTemplate (line 31) | func (t *tmpl) newTemplate() *template.Template {
  constant jsonVarExtension (line 22) | jsonVarExtension      = ".json"
  constant terraformVarExtension (line 23) | terraformVarExtension = ".tf"
  constant yamlVarExtension (line 24) | yamlVarExtension      = ".yaml"
  constant ymlVarExtension (line 25) | ymlVarExtension       = ".yml"
  constant rightDelim (line 26) | rightDelim            = "]]"
  constant leftDelim (line 27) | leftDelim             = "[["

FILE: test/acctest/acctest.go
  type TestCase (line 14) | type TestCase struct
  type TestStep (line 26) | type TestStep struct
  type TestStepRunner (line 41) | type TestStepRunner interface
  type TestStateFunc (line 47) | type TestStateFunc
  type TestState (line 50) | type TestState struct
  function Test (line 57) | func Test(t *testing.T, c TestCase) {
  function CleanupPurgeJob (line 117) | func CleanupPurgeJob(s *TestState) error {
  function CheckDeploymentStatus (line 124) | func CheckDeploymentStatus(status string) TestStateFunc {
  function CheckTaskGroupCount (line 140) | func CheckTaskGroupCount(groupName string, count int) TestStateFunc {
  function newNomadClient (line 163) | func newNomadClient() (*nomad.Client, error) {

FILE: test/acctest/deploy.go
  type DeployTestStepRunner (line 15) | type DeployTestStepRunner struct
    method Run (line 26) | func (c DeployTestStepRunner) Run(s *TestState) error {

FILE: test/deploy_test.go
  function TestDeploy_basic (line 14) | func TestDeploy_basic(t *testing.T) {
  function TestDeploy_namespace (line 28) | func TestDeploy_namespace(t *testing.T) {
  function TestDeploy_driverError (line 57) | func TestDeploy_driverError(t *testing.T) {
  function TestDeploy_allocError (line 79) | func TestDeploy_allocError(t *testing.T) {
  function TestDeploy_count (line 101) | func TestDeploy_count(t *testing.T) {
  function TestDeploy_canary (line 144) | func TestDeploy_canary(t *testing.T) {
  function TestDeploy_lifecycle (line 172) | func TestDeploy_lifecycle(t *testing.T) {
  function TestDeploy_taskScalingStanza (line 186) | func TestDeploy_taskScalingStanza(t *testing.T) {

FILE: version/version.go
  function GetHumanVersion (line 30) | func GetHumanVersion() string {

FILE: version/version_test.go
  function Test_GetHumanVersion (line 12) | func Test_GetHumanVersion(t *testing.T) {
Condensed preview — 90 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (334K chars).
[
  {
    "path": ".dockerignore",
    "chars": 19,
    "preview": "./bin/\n./pkg/\n.git\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 166,
    "preview": "# release configuration\n/.release/                              @hashicorp/release-engineering\n/.github/workflows/build."
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 4809,
    "preview": "# Contributing to Levant\n\n**First** of all welcome and thank you for even considering to contribute to the Levant projec"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 557,
    "preview": "**Description**\n\n<!--\nBriefly describe the problem you are having in a few paragraphs.\n-->\n\n**Relevant Nomad job specifi"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 117,
    "preview": "version: 2\n\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\""
  },
  {
    "path": ".github/workflows/actionlint.yml",
    "chars": 416,
    "preview": "# If the repository is public, be sure to change to GitHub hosted runners\nname: Lint GitHub Actions Workflows\non:\n  pull"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 6303,
    "preview": "name: build\non:\n  push:\n  workflow_dispatch:\n  workflow_call:\n\nenv:\n  PKG_NAME: \"levant\"\n\njobs:\n  get-go-version:\n    na"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1581,
    "preview": "name: ci\non:\n  push:\njobs:\n  lint-go:\n    runs-on: ubuntu-latest\n    env:\n      GO_TAGS: ''\n    steps:\n      - uses: act"
  },
  {
    "path": ".github/workflows/nightly-release-readme.md",
    "chars": 1164,
    "preview": "Nightly releases are snapshots of the development activity on the Levant project that may include new features and bug f"
  },
  {
    "path": ".github/workflows/nightly-release.yml",
    "chars": 3630,
    "preview": "# This GitHub action triggers a fresh set of Levant builds and publishes them\n# to GitHub Releases under the `nightly` t"
  },
  {
    "path": ".gitignore",
    "chars": 409,
    "preview": ".DS_Store\n\n# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\ndist/\n\n#"
  },
  {
    "path": ".go-version",
    "chars": 7,
    "preview": "1.24.4\n"
  },
  {
    "path": ".golangci.yml",
    "chars": 476,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\nrun:\n  deadline: 5m\n  issues-exit-code: 1\n  tests: t"
  },
  {
    "path": ".release/ci.hcl",
    "chars": 2464,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\nschema = \"1\"\n\nproject \"levant\" {\n  team = \"nomad\"\n\n "
  },
  {
    "path": ".release/levant-artifacts.hcl",
    "chars": 956,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\nschema = 1\nartifacts {\n  zip = [\n    \"levant_${versi"
  },
  {
    "path": ".release/release-metadata.hcl",
    "chars": 302,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\nurl_docker_registry_dockerhub = \"https://hub.docker."
  },
  {
    "path": ".release/security-scan.hcl",
    "chars": 1408,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\ncontainer {\n  dependencies    = true\n  alpine_securi"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 12409,
    "preview": "## 0.4.0 (June 26, 2025)\n\n__BACKWARDS INCOMPATIBILITIES:__\n* cli: Levant no longer supports the deprecated Vault token w"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 5459,
    "preview": "# Code of Conduct\n\n## 1. Purpose\n\nA primary goal of Levant is to be inclusive to the largest number of contributors, wit"
  },
  {
    "path": "Dockerfile",
    "chars": 1551,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n# This Dockerfile contains multiple targets.\n# Use '"
  },
  {
    "path": "GNUmakefile",
    "chars": 3177,
    "preview": "SHELL = bash\ndefault: lint test check-mod dev\n\nGIT_COMMIT := $(shell git rev-parse --short HEAD)\nGIT_DIRTY := $(if $(she"
  },
  {
    "path": "LICENSE",
    "chars": 15957,
    "preview": "Copyright (c) 2017 HashiCorp, Inc.\n\nMozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. \"Contributor\"\n\n     means "
  },
  {
    "path": "README.md",
    "chars": 4518,
    "preview": "# Levant\n\n_Levant v0.4.0 is the final release of Levant. All users are encouraged to migrate to [Nomad Pack](https://git"
  },
  {
    "path": "client/consul.go",
    "chars": 447,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage client\n\nimport (\n\tconsul \"github.com/hashi"
  },
  {
    "path": "client/nomad.go",
    "chars": 439,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage client\n\nimport (\n\tnomad \"github.com/hashic"
  },
  {
    "path": "command/deploy.go",
    "chars": 6587,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage command\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"git"
  },
  {
    "path": "command/deploy_test.go",
    "chars": 1589,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage command\n\nimport (\n\t\"testing\"\n\n\t\"github.com"
  },
  {
    "path": "command/dispatch.go",
    "chars": 3323,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage command\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"str"
  },
  {
    "path": "command/meta.go",
    "chars": 1249,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage command\n\nimport (\n\t\"bufio\"\n\t\"flag\"\n\t\"io\"\n\n"
  },
  {
    "path": "command/plan.go",
    "chars": 4412,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage command\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"git"
  },
  {
    "path": "command/render.go",
    "chars": 3642,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage command\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\""
  },
  {
    "path": "command/scale_in.go",
    "chars": 3377,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage command\n\nimport (\n\t\"strings\"\n\n\t\"github.com"
  },
  {
    "path": "command/scale_out.go",
    "chars": 3392,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage command\n\nimport (\n\t\"strings\"\n\n\t\"github.com"
  },
  {
    "path": "command/test-fixtures/group_canary.nomad",
    "chars": 1072,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\njob \"example\" {\n  datacenters = [\"dc1\"]\n  type = \"se"
  },
  {
    "path": "command/test-fixtures/job_canary.nomad",
    "chars": 1057,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\njob \"example\" {\n  datacenters = [\"dc1\"]\n  type = \"se"
  },
  {
    "path": "command/test-fixtures/periodic_batch.nomad",
    "chars": 533,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\njob \"periodic_batch_test\" {\n  datacenters = [\"dc1\"]\n"
  },
  {
    "path": "command/version.go",
    "chars": 685,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage command\n\nimport (\n\t\"github.com/mitchellh/c"
  },
  {
    "path": "commands.go",
    "chars": 1495,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/"
  },
  {
    "path": "docs/README.md",
    "chars": 803,
    "preview": "# Levant Documentation\n\nWelcome to the Levant documentation. The `docs` directory aims to host detailed and thorough doc"
  },
  {
    "path": "docs/clients.md",
    "chars": 2460,
    "preview": "## Clients\n\nLevant uses Nomad and Consul clients in order to perform its work. Currently only the HTTP address client pa"
  },
  {
    "path": "docs/commands.md",
    "chars": 9344,
    "preview": "## Commands\n\nLevant supports a number of command line arguments which provide control over the Levant binary. Each comma"
  },
  {
    "path": "docs/templates.md",
    "chars": 9739,
    "preview": "## Templates\n\nAlongside enhanced deployments of Nomad jobs; Levant provides templating functionality allowing for greate"
  },
  {
    "path": "go.mod",
    "chars": 3659,
    "preview": "module github.com/hashicorp/levant\n\ngo 1.24.4\n\n// Use the same version of go-metrics as Nomad.\nreplace github.com/armon/"
  },
  {
    "path": "go.sum",
    "chars": 73532,
    "preview": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1"
  },
  {
    "path": "helper/files.go",
    "chars": 1418,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage helper\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\n\t"
  },
  {
    "path": "helper/files_test.go",
    "chars": 1603,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage helper\n\nimport (\n\t\"os\"\n\t\"reflect\"\n\t\"testin"
  },
  {
    "path": "helper/kvflag.go",
    "chars": 1707,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage helper\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// F"
  },
  {
    "path": "helper/kvflag_test.go",
    "chars": 1885,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage helper\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\""
  },
  {
    "path": "helper/nomad/opts.go",
    "chars": 432,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage nomad\n\nimport \"github.com/hashicorp/nomad/"
  },
  {
    "path": "helper/nomad/opts_test.go",
    "chars": 901,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage nomad\n\nimport (\n\t\"testing\"\n\n\t\"github.com/h"
  },
  {
    "path": "helper/variable.go",
    "chars": 885,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage helper\n\nimport (\n\t\"github.com/rs/zerolog/l"
  },
  {
    "path": "helper/variable_test.go",
    "chars": 699,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage helper\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\n"
  },
  {
    "path": "levant/auto_revert.go",
    "chars": 2477,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage levant\n\nimport (\n\t\"time\"\n\n\t\"github.com/has"
  },
  {
    "path": "levant/deploy.go",
    "chars": 15837,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage levant\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"time\""
  },
  {
    "path": "levant/dispatch.go",
    "chars": 2402,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage levant\n\nimport (\n\t\"github.com/hashicorp/le"
  },
  {
    "path": "levant/failure_inspector.go",
    "chars": 4509,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage levant\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\""
  },
  {
    "path": "levant/job_status_checker.go",
    "chars": 4790,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage levant\n\nimport (\n\tnomadHelper \"github.com/"
  },
  {
    "path": "levant/job_status_checker_test.go",
    "chars": 2097,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage levant\n\nimport (\n\t\"testing\"\n\n\tnomad \"githu"
  },
  {
    "path": "levant/plan.go",
    "chars": 4686,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage levant\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/hash"
  },
  {
    "path": "levant/structs/config.go",
    "chars": 3611,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage structs\n\nimport nomad \"github.com/hashicor"
  },
  {
    "path": "logging/logging.go",
    "chars": 1978,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage logging\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\tstdlog \"lo"
  },
  {
    "path": "main.go",
    "chars": 1452,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/"
  },
  {
    "path": "scale/scale.go",
    "chars": 4487,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage scale\n\nimport (\n\t\"github.com/hashicorp/lev"
  },
  {
    "path": "scale/scale_test.go",
    "chars": 1920,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage scale\n\nimport (\n\t\"testing\"\n\n\t\"github.com/h"
  },
  {
    "path": "scripts/docker-entrypoint.sh",
    "chars": 153,
    "preview": "#!/usr/bin/env sh\n# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n\nset -e\n\nif [ \"$1\" = 'levant' ]; t"
  },
  {
    "path": "scripts/version.sh",
    "chars": 715,
    "preview": "#!/usr/bin/env bash\n# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n\nversion_file=$1\nversion_metadat"
  },
  {
    "path": "template/funcs.go",
    "chars": 16573,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage template\n\nimport (\n\t\"encoding/json\"\n\t\"erro"
  },
  {
    "path": "template/render.go",
    "chars": 4654,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage template\n\nimport (\n\t\"bytes\"\n\t\"encoding/jso"
  },
  {
    "path": "template/render_test.go",
    "chars": 3894,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage template\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\tnoma"
  },
  {
    "path": "template/template.go",
    "chars": 874,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage template\n\nimport (\n\t\"text/template\"\n\n\tcons"
  },
  {
    "path": "template/test-fixtures/missing_var.nomad",
    "chars": 1079,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\njob \"[[.job_name]]\" {\n  datacenters = [\"dc1\"]\n  type"
  },
  {
    "path": "template/test-fixtures/multi_templated.nomad",
    "chars": 1014,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\njob \"[[.job_name]]\" {\n  datacenters = [\"[[.datacentr"
  },
  {
    "path": "template/test-fixtures/none_templated.nomad",
    "chars": 983,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\njob \"levantExample\" {\n  datacenters = [\"dc1\"]\n  type"
  },
  {
    "path": "template/test-fixtures/single_templated.nomad",
    "chars": 1299,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\njob \"[[.job_name]]\" {\n  datacenters = [\"dc1\"]\n  type"
  },
  {
    "path": "template/test-fixtures/test-overwrite.yaml",
    "chars": 101,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\njob_name: levantExampleOverwrite\n"
  },
  {
    "path": "template/test-fixtures/test.tf",
    "chars": 237,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\nvariable \"job_name\" {\n  default = \"levantExample\"\n}\n"
  },
  {
    "path": "template/test-fixtures/test.yaml",
    "chars": 116,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\njob_name: levantExample\ntask_resource_cpu: 1313\n"
  },
  {
    "path": "test/acctest/acctest.go",
    "chars": 4265,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage acctest\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\tnoma"
  },
  {
    "path": "test/acctest/deploy.go",
    "chars": 1226,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage acctest\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/has"
  },
  {
    "path": "test/deploy_test.go",
    "chars": 4766,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage test\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github"
  },
  {
    "path": "test/fixtures/deploy_alloc_error.nomad",
    "chars": 707,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n# test alloc error with a command failure\n\njob \"[[.j"
  },
  {
    "path": "test/fixtures/deploy_basic.nomad",
    "chars": 718,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n# tests a healthy deployment\n\njob \"[[.job_name]]\" {\n"
  },
  {
    "path": "test/fixtures/deploy_canary.nomad",
    "chars": 800,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n# tests a canary deployment\n\njob \"[[.job_name]]\" {\n "
  },
  {
    "path": "test/fixtures/deploy_count.nomad",
    "chars": 740,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n# tests a healthy deployment with a count\n\njob \"[[.j"
  },
  {
    "path": "test/fixtures/deploy_driver_error.nomad",
    "chars": 694,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\n# tests driver error with an invalid docker image ta"
  },
  {
    "path": "test/fixtures/deploy_lifecycle.nomad",
    "chars": 956,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\njob \"[[.job_name]]\" {\n  datacenters = [\"dc1\"]\n  type"
  },
  {
    "path": "test/fixtures/deploy_namespace.nomad",
    "chars": 739,
    "preview": "# tests a healthy deployment\n\njob \"[[.job_name]]\" {\n  datacenters = [\"dc1\"]\n  type        = \"service\"\n  namespace   = \"t"
  },
  {
    "path": "test/fixtures/deploy_task_scaling.nomad",
    "chars": 1010,
    "preview": "# Copyright (c) HashiCorp, Inc.\n# SPDX-License-Identifier: MPL-2.0\n\njob \"[[.job_name]]\" {\n  datacenters = [\"dc1\"]\n  type"
  },
  {
    "path": "version/version.go",
    "chars": 1433,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage version\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nvar"
  },
  {
    "path": "version/version_test.go",
    "chars": 1074,
    "preview": "// Copyright (c) HashiCorp, Inc.\n// SPDX-License-Identifier: MPL-2.0\n\npackage version\n\nimport (\n\t\"testing\"\n\n\t\"github.com"
  }
]

About this extraction

This page contains the full source code of the hashicorp/levant GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 90 files (304.0 KB), approximately 104.9k tokens, and a symbol index with 196 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!