mirror of
https://github.com/coder/terraform-provider-envbuilder.git
synced 2025-09-18 14:47:15 +00:00
Compare commits
32 commits
Author | SHA1 | Date | |
---|---|---|---|
|
446bafacce |
||
|
2025395601 |
||
|
30fd26107b |
||
|
52535609d0 |
||
|
251def2524 |
||
|
cd9c032708 |
||
|
7b6e7d5fba |
||
|
e298d87ca3 |
||
|
99b0d0257b |
||
|
b86f527238 |
||
|
f98d104f23 |
||
|
3adb53af1a |
||
|
82c856094e |
||
|
dabb7f31aa |
||
|
bceddea8cc |
||
|
1a997fdd60 |
||
|
6795af2ba1 |
||
|
311f01b14e |
||
|
e7dc9a171a |
||
|
32d72a7b4e |
||
|
875d1eb69c |
||
|
cdd57d3b36 |
||
|
437dde9144 |
||
|
e565d7d145 |
||
|
6c66d06af7 |
||
|
4077a87dca |
||
|
6137223cea |
||
|
482a446eb3 |
||
|
23f2cf5f48 |
||
|
e35030b39f |
||
|
11c4b3b088 |
||
|
d6192fcd11 |
17 changed files with 1442 additions and 594 deletions
.github/workflows
.golangci.ymlREADME.mddocs
go.modgo.suminternal
imgutil
provider
cached_image_resource.gocached_image_resource_test.gohelpers.goprovider.goprovider_internal_test.goprovider_test.go
tfutil
testutil/registrytest
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
|
@ -13,16 +13,16 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Unshallow
|
||||
run: git fetch --prune --unshallow
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.22.4
|
||||
- name: Import GPG Key
|
||||
id: import_gpg
|
||||
uses: crazy-max/ghaction-import-gpg@v6.1.0
|
||||
uses: crazy-max/ghaction-import-gpg@v6.2.0
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
|
|
20
.github/workflows/test.yml
vendored
20
.github/workflows/test.yml
vendored
|
@ -22,23 +22,27 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.7
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache: true
|
||||
- run: go mod download
|
||||
- run: go build -v .
|
||||
- name: Run linters
|
||||
uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0
|
||||
uses: golangci/golangci-lint-action@2e788936b09dd82dc280e845628a40d2ba6b204c # v6.3.1
|
||||
with:
|
||||
version: latest
|
||||
|
||||
generate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.7
|
||||
- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
|
||||
with:
|
||||
terraform_version: "1.9.*"
|
||||
terraform_wrapper: false
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache: true
|
||||
|
@ -65,12 +69,12 @@ jobs:
|
|||
- "1.8.*"
|
||||
- "1.9.*"
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.7
|
||||
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache: true
|
||||
- uses: hashicorp/setup-terraform@651471c36a6092792c552e8b1bef71e592b462d8 # v3.1.1
|
||||
- uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
|
||||
with:
|
||||
terraform_version: ${{ matrix.terraform }}
|
||||
terraform_wrapper: false
|
||||
|
|
|
@ -9,19 +9,18 @@ linters:
|
|||
enable:
|
||||
- durationcheck
|
||||
- errcheck
|
||||
- exportloopref
|
||||
- forcetypeassert
|
||||
- godot
|
||||
- gofmt
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- makezero
|
||||
- misspell
|
||||
- nilerr
|
||||
- predeclared
|
||||
- staticcheck
|
||||
- tenv
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- vet
|
||||
- usetesting
|
||||
|
|
|
@ -12,10 +12,10 @@ If it is found that building a particular dev container would produce the same i
|
|||
Take a look at the [`envbuilder_cached_image_resource.tf`](./examples/resources/envbuilder_cached_image/envbuilder_cached_image_resource.tf) example for a detailed usage example.
|
||||
|
||||
For use with [Coder](https://github.com/coder/coder), see the [Dev Containers documentation](https://coder.com/docs/templates/dev-containers) and check out the example templates:
|
||||
- [Docker](https://github.com/coder/coder/tree/main/examples/templates/devcontainer-docker)
|
||||
- [Kubernetes](https://github.com/coder/coder/tree/main/examples/templates/devcontainer-kuberntes)
|
||||
- [AWS VM](https://github.com/coder/coder/tree/main/examples/templates/devcontainer-aws-vm)
|
||||
- [GCP VM](https://github.com/coder/coder/tree/main/examples/templates/devcontainer-gcp-vm)
|
||||
- [Docker](https://github.com/coder/coder/tree/main/examples/templates/docker-devcontainer)
|
||||
- [Kubernetes](https://github.com/coder/coder/tree/main/examples/templates/kubernetes-devcontainer)
|
||||
- [AWS VM](https://github.com/coder/coder/tree/main/examples/templates/aws-devcontainer)
|
||||
- [GCP VM](https://github.com/coder/coder/tree/main/examples/templates/gcp-devcontainer)
|
||||
|
||||
## Requirements
|
||||
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
page_title: "envbuilder Provider"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
|
||||
The Envbuilder provider can be used to check for the presence of a container image previously built by Envbuilder https://github.com/coder/envbuilder.
|
||||
This allows re-using a previously built image pushed to a container registry without having to rebuild it.
|
||||
---
|
||||
|
||||
# envbuilder Provider
|
||||
|
||||
|
||||
The Envbuilder provider can be used to check for the presence of a container image previously built by [Envbuilder](https://github.com/coder/envbuilder).
|
||||
This allows re-using a previously built image pushed to a container registry without having to rebuild it.
|
||||
|
||||
## Example Usage
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ The cached image resource can be used to retrieve a cached image produced by env
|
|||
|
||||
- `base_image_cache_dir` (String) (Envbuilder option) The path to a directory where the base image can be found. This should be a read-only directory solely mounted for the purpose of caching the base image.
|
||||
- `build_context_path` (String) (Envbuilder option) Can be specified when a DockerfilePath is specified outside the base WorkspaceFolder. This path MUST be relative to the WorkspaceFolder path into which the repo is cloned.
|
||||
- `build_secrets` (Map of String) The secrets to use for the build. This is a map of key-value pairs.
|
||||
- `cache_ttl_days` (Number) (Envbuilder option) The number of days to use cached layers before expiring them. Defaults to 7 days.
|
||||
- `devcontainer_dir` (String) (Envbuilder option) The path to the folder containing the devcontainer.json file that will be used to build the workspace and can either be an absolute path or a path relative to the workspace folder. If not provided, defaults to `.devcontainer`.
|
||||
- `devcontainer_json_path` (String) (Envbuilder option) The path to a devcontainer.json file that is either an absolute path or a path relative to DevcontainerDir. This can be used in cases where one wants to substitute an edited devcontainer.json file for the one that exists in the repo.
|
||||
|
@ -37,6 +38,7 @@ The cached image resource can be used to retrieve a cached image produced by env
|
|||
- `git_clone_single_branch` (Boolean) (Envbuilder option) Clone only a single branch of the Git repository.
|
||||
- `git_http_proxy_url` (String) (Envbuilder option) The URL for the HTTP proxy. This is optional.
|
||||
- `git_password` (String, Sensitive) (Envbuilder option) The password to use for Git authentication. This is optional.
|
||||
- `git_ssh_private_key_base64` (String, Sensitive) (Envbuilder option) Base64 encoded SSH private key to be used for Git authentication.
|
||||
- `git_ssh_private_key_path` (String) (Envbuilder option) Path to an SSH private key to be used for Git authentication.
|
||||
- `git_username` (String) (Envbuilder option) The username to use for Git authentication. This is optional.
|
||||
- `ignore_paths` (List of String) (Envbuilder option) The comma separated list of paths to ignore when building the workspace.
|
||||
|
|
95
go.mod
95
go.mod
|
@ -1,28 +1,32 @@
|
|||
module github.com/coder/terraform-provider-envbuilder
|
||||
|
||||
go 1.22.4
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.7
|
||||
|
||||
// We use our own Kaniko fork.
|
||||
replace github.com/GoogleContainerTools/kaniko => github.com/coder/kaniko v0.0.0-20240807142221-ffc5e60fca41
|
||||
replace github.com/GoogleContainerTools/kaniko => github.com/coder/kaniko v0.0.0-20241120132148-131d6094d781
|
||||
|
||||
// Required to import codersdk due to gvisor dependency.
|
||||
replace tailscale.com => github.com/coder/tailscale v1.1.1-0.20240702054557-aa558fbe5374
|
||||
|
||||
require (
|
||||
github.com/GoogleContainerTools/kaniko v1.9.2
|
||||
github.com/coder/envbuilder v1.0.0-rc.5.0.20240815142547-cd63d0b71a40
|
||||
github.com/docker/docker v26.1.5+incompatible
|
||||
github.com/gliderlabs/ssh v0.3.7
|
||||
github.com/go-git/go-billy/v5 v5.5.0
|
||||
github.com/go-git/go-git/v5 v5.12.0
|
||||
github.com/coder/envbuilder v1.1.0
|
||||
github.com/coder/serpent v0.8.0
|
||||
github.com/docker/docker v27.3.1+incompatible
|
||||
github.com/gliderlabs/ssh v0.3.8
|
||||
github.com/go-git/go-billy/v5 v5.6.1
|
||||
github.com/go-git/go-git/v5 v5.13.1
|
||||
github.com/google/go-containerregistry v0.20.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/hashicorp/terraform-plugin-docs v0.19.4
|
||||
github.com/hashicorp/terraform-plugin-docs v0.21.0
|
||||
github.com/hashicorp/terraform-plugin-framework v1.11.0
|
||||
github.com/hashicorp/terraform-plugin-go v0.23.0
|
||||
github.com/hashicorp/terraform-plugin-log v0.9.0
|
||||
github.com/hashicorp/terraform-plugin-testing v1.10.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.10.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -57,7 +61,7 @@ require (
|
|||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.11.7 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.3 // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/akutz/memconn v0.1.0 // indirect
|
||||
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
|
||||
|
@ -84,23 +88,20 @@ require (
|
|||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chainguard-dev/git-urls v1.0.2 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.8.0 // indirect
|
||||
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect
|
||||
github.com/cilium/ebpf v0.12.3 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/coder/coder/v2 v2.10.1-0.20240704130443-c2d44d16a352 // indirect
|
||||
github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 // indirect
|
||||
github.com/coder/quartz v0.1.0 // indirect
|
||||
github.com/coder/retry v1.5.1 // indirect
|
||||
github.com/coder/serpent v0.7.0 // indirect
|
||||
github.com/coder/terraform-provider-coder v0.23.0 // indirect
|
||||
github.com/containerd/cgroups v1.1.0 // indirect
|
||||
github.com/containerd/cgroups/v3 v3.0.2 // indirect
|
||||
github.com/containerd/containerd v1.7.19 // indirect
|
||||
github.com/containerd/containerd v1.7.21 // indirect
|
||||
github.com/containerd/containerd/api v1.7.19 // indirect
|
||||
github.com/containerd/continuity v0.4.3 // indirect
|
||||
github.com/containerd/errdefs v0.1.0 // indirect
|
||||
|
@ -109,17 +110,16 @@ require (
|
|||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
|
||||
github.com/containerd/ttrpc v1.2.5 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.1.1 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.0 // indirect
|
||||
github.com/coreos/go-iptables v0.6.0 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.10.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/distribution/distribution/v3 v3.0.0-alpha.1 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/cli v27.1.1+incompatible // indirect
|
||||
github.com/docker/cli v27.2.1+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.2+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
|
@ -135,23 +135,23 @@ require (
|
|||
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.10 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/nftables v0.2.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b // indirect
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
|
||||
github.com/gorilla/handlers v1.5.1 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
|
||||
github.com/hashicorp/cli v1.1.6 // indirect
|
||||
github.com/hashicorp/cli v1.1.7 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
|
@ -166,12 +166,12 @@ require (
|
|||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/golang-lru/arc/v2 v2.0.5 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect
|
||||
github.com/hashicorp/hc-install v0.8.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hc-install v0.9.1 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.21.0 // indirect
|
||||
github.com/hashicorp/logutils v1.0.0 // indirect
|
||||
github.com/hashicorp/terraform-exec v0.21.0 // indirect
|
||||
github.com/hashicorp/terraform-json v0.22.1 // indirect
|
||||
github.com/hashicorp/terraform-exec v0.22.0 // indirect
|
||||
github.com/hashicorp/terraform-json v0.24.0 // indirect
|
||||
github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 // indirect
|
||||
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
|
||||
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
|
||||
|
@ -185,13 +185,12 @@ require (
|
|||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect
|
||||
github.com/jsimonetti/rtnetlink v1.3.5 // indirect
|
||||
github.com/karrick/godirwalk v1.16.1 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mdlayher/genetlink v1.3.2 // indirect
|
||||
|
@ -207,17 +206,18 @@ require (
|
|||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/buildkit v0.13.1 // indirect
|
||||
github.com/moby/buildkit v0.16.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/swarmkit/v2 v2.0.0-20230315203717-e28e8ba9bc83 // indirect
|
||||
github.com/moby/sys/mount v0.3.3 // indirect
|
||||
github.com/moby/sys/mountinfo v0.7.1 // indirect
|
||||
github.com/moby/sys/mountinfo v0.7.2 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.1 // indirect
|
||||
github.com/moby/sys/symlink v0.2.0 // indirect
|
||||
github.com/moby/sys/user v0.1.0 // indirect
|
||||
github.com/moby/sys/user v0.3.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
|
@ -255,7 +255,6 @@ require (
|
|||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d // indirect
|
||||
github.com/tailscale/golang-x-crypto v0.0.0-20230713185742-f0b76a10a08e // indirect
|
||||
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
|
||||
|
@ -264,6 +263,8 @@ require (
|
|||
github.com/tailscale/wireguard-go v0.0.0-20231121184858-cc193a0b3272 // indirect
|
||||
github.com/tcnksm/go-httpstat v0.2.0 // indirect
|
||||
github.com/tinylib/msgp v1.1.8 // indirect
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
|
||||
github.com/twpayne/go-vfs/v5 v5.0.4 // indirect
|
||||
github.com/u-root/uio v0.0.0-20240209044354-b3d14b93376a // indirect
|
||||
github.com/valyala/fasthttp v1.55.0 // indirect
|
||||
github.com/vbatts/tar-split v0.11.5 // indirect
|
||||
|
@ -274,9 +275,9 @@ require (
|
|||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/yuin/goldmark v1.7.1 // indirect
|
||||
github.com/yuin/goldmark v1.7.7 // indirect
|
||||
github.com/yuin/goldmark-meta v1.1.0 // indirect
|
||||
github.com/zclconf/go-cty v1.15.0 // indirect
|
||||
github.com/zclconf/go-cty v1.16.2 // indirect
|
||||
github.com/zeebo/errs v1.3.0 // indirect
|
||||
go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
|
||||
go.etcd.io/etcd/raft/v3 v3.5.6 // indirect
|
||||
|
@ -293,17 +294,17 @@ require (
|
|||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect
|
||||
go4.org/netipx v0.0.0-20230728180743-ad4cb58a6516 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/oauth2 v0.20.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.23.0 // indirect
|
||||
golang.org/x/term v0.23.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/crypto v0.35.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.36.0 // indirect
|
||||
golang.org/x/oauth2 v0.27.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/term v0.29.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
golang.org/x/tools v0.23.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
||||
|
|
191
go.sum
191
go.sum
|
@ -81,8 +81,8 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
|
|||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ=
|
||||
github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg=
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
|
||||
github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
|
||||
|
@ -144,8 +144,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
|||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s=
|
||||
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E=
|
||||
github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs=
|
||||
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
|
||||
|
@ -178,36 +178,34 @@ github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb2
|
|||
github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4=
|
||||
github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
|
||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
||||
github.com/coder/coder/v2 v2.10.1-0.20240704130443-c2d44d16a352 h1:L/EjCuZxs5tOcqqCaASj/nu65TRYEFcTt8qRQfHZXX0=
|
||||
github.com/coder/coder/v2 v2.10.1-0.20240704130443-c2d44d16a352/go.mod h1:P1KoQSgnKEAG6Mnd3YlGzAophty+yKA9VV48LpfNRvo=
|
||||
github.com/coder/envbuilder v1.0.0-rc.5.0.20240815142547-cd63d0b71a40 h1:XxZpRd+bjCAZJJHCFBVsbIrYZSYcnZBm0kp/FgfZg0o=
|
||||
github.com/coder/envbuilder v1.0.0-rc.5.0.20240815142547-cd63d0b71a40/go.mod h1:m7kjZGXpP8jAZgKwfBaMbXe0bg29ERZP3g4PMpQLR4k=
|
||||
github.com/coder/kaniko v0.0.0-20240807142221-ffc5e60fca41 h1:1Ye7AcLnuT5IDv6il7Fxo+aqpzlWfedkpraCCwx8Lyo=
|
||||
github.com/coder/kaniko v0.0.0-20240807142221-ffc5e60fca41/go.mod h1:YMK7BlxerzLlMwihGxNWUaFoN9LXCij4P+w/8/fNlcM=
|
||||
github.com/coder/envbuilder v1.1.0 h1:OcICg3FzwFHzBDw+60tW7dgCxfkTxt/C6faz/cAfwDE=
|
||||
github.com/coder/envbuilder v1.1.0/go.mod h1:WgqCgSz6XzXSoTGMMIuBf+0D38iofTqAwQCIlEmohX0=
|
||||
github.com/coder/kaniko v0.0.0-20241120132148-131d6094d781 h1:/4SMdrjLQL1BseLSnMd9nYQSI+E63CXcyFGC7ZHHj8I=
|
||||
github.com/coder/kaniko v0.0.0-20241120132148-131d6094d781/go.mod h1:3rM/KOQ4LgF8mE+O1P6pLDa/E57mzxIxNdUOMKi1qpg=
|
||||
github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs=
|
||||
github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc=
|
||||
github.com/coder/quartz v0.1.0 h1:cLL+0g5l7xTf6ordRnUMMiZtRE8Sq5LxpghS63vEXrQ=
|
||||
github.com/coder/quartz v0.1.0/go.mod h1:vsiCc+AHViMKH2CQpGIpFgdHIEQsxwm8yCscqKmzbRA=
|
||||
github.com/coder/retry v1.5.1 h1:iWu8YnD8YqHs3XwqrqsjoBTAVqT9ml6z9ViJ2wlMiqc=
|
||||
github.com/coder/retry v1.5.1/go.mod h1:blHMk9vs6LkoRT9ZHyuZo360cufXEhrxqvEzeMtRGoY=
|
||||
github.com/coder/serpent v0.7.0 h1:zGpD2GlF3lKIVkMjNGKbkip88qzd5r/TRcc30X/SrT0=
|
||||
github.com/coder/serpent v0.7.0/go.mod h1:REkJ5ZFHQUWFTPLExhXYZ1CaHFjxvGNRlLXLdsI08YA=
|
||||
github.com/coder/serpent v0.8.0 h1:6OR+k6fekhSeEDmwwzBgnSjaa7FfGGrMlc3GoAEH9dg=
|
||||
github.com/coder/serpent v0.8.0/go.mod h1:cZFW6/fP+kE9nd/oRkEHJpG6sXCtQ+AX7WMMEHv0Y3Q=
|
||||
github.com/coder/tailscale v1.1.1-0.20240702054557-aa558fbe5374 h1:a5Eg7D5e2oAc0tN56ee4yxtiTo76ztpRlk6geljaZp8=
|
||||
github.com/coder/tailscale v1.1.1-0.20240702054557-aa558fbe5374/go.mod h1:rp6BIJxCp127/hvvDWNkHC9MxAlKvQfoOtBr8s5sCqo=
|
||||
github.com/coder/terraform-provider-coder v0.23.0 h1:DuNLWxhnGlXyG0g+OCAZRI6xd8+bJjIEnE4F3hYgA4E=
|
||||
github.com/coder/terraform-provider-coder v0.23.0/go.mod h1:wMun9UZ9HT2CzF6qPPBup1odzBpVUc0/xSFoXgdI3tk=
|
||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
||||
github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0=
|
||||
github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE=
|
||||
github.com/containerd/containerd v1.7.19 h1:/xQ4XRJ0tamDkdzrrBAUy/LE5nCcxFKdBm4EcPrSMEE=
|
||||
github.com/containerd/containerd v1.7.19/go.mod h1:h4FtNYUUMB4Phr6v+xG89RYKj9XccvbNSCKjdufCrkc=
|
||||
github.com/containerd/containerd v1.7.21 h1:USGXRK1eOC/SX0L195YgxTHb0a00anxajOzgfN0qrCA=
|
||||
github.com/containerd/containerd v1.7.21/go.mod h1:e3Jz1rYRUZ2Lt51YrH9Rz0zPyJBOlSvB3ghr2jbVD8g=
|
||||
github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA=
|
||||
github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig=
|
||||
github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8=
|
||||
|
@ -224,19 +222,17 @@ github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G
|
|||
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
|
||||
github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oLU=
|
||||
github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4=
|
||||
github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0=
|
||||
github.com/containerd/typeurl/v2 v2.2.0 h1:6NBDbQzr7I5LHgp34xAXYF5DOTQDn05X58lsPEmzLso=
|
||||
github.com/containerd/typeurl/v2 v2.2.0/go.mod h1:8XOOxnyatxSWuG8OfsZXVnAF4iZfedjS/8UHSPJnX4g=
|
||||
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
|
||||
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||
github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU=
|
||||
github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
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=
|
||||
|
@ -252,12 +248,12 @@ github.com/distribution/distribution/v3 v3.0.0-alpha.1 h1:jn7I1gvjOvmLztH1+1cLiU
|
|||
github.com/distribution/distribution/v3 v3.0.0-alpha.1/go.mod h1:LCp4JZp1ZalYg0W/TN05jarCQu+h4w7xc7ZfQF4Y/cY=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
|
||||
github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v27.2.1+incompatible h1:U5BPtiD0viUzjGAjV1p0MGB8eVA3L3cbIrnyWmSJI70=
|
||||
github.com/docker/cli v27.2.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v26.1.5+incompatible h1:NEAxTwEjxV6VbBMBoGG3zPqbiJosIApZjxlbrG9q3/g=
|
||||
github.com/docker/docker v26.1.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
|
@ -275,8 +271,8 @@ github.com/ePirat/docker-credential-gitlabci v1.0.0 h1:YRkUSvkON6rT88vtscClAmPEY
|
|||
github.com/ePirat/docker-credential-gitlabci v1.0.0/go.mod h1:Ptmh+D0lzBQtgb6+QHjXl9HqOn3T1P8fKUHldiSQQGA=
|
||||
github.com/ebitengine/purego v0.6.0-alpha.5 h1:EYID3JOAdmQ4SNZYJHu9V6IqOeRQDBYxqKAg9PyoHFY=
|
||||
github.com/ebitengine/purego v0.6.0-alpha.5/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ=
|
||||
github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
|
@ -303,20 +299,20 @@ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
|||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
|
||||
github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo=
|
||||
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
||||
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
||||
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
||||
github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA=
|
||||
github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
||||
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
|
||||
github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||
github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
|
||||
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
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=
|
||||
|
@ -360,8 +356,9 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
|||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
|
||||
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
|
@ -408,8 +405,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
|||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/nftables v0.2.0 h1:PbJwaBmbVLzpeldoeUKGkE2RjstrjPKMl6oLrfEJ6/8=
|
||||
github.com/google/nftables v0.2.0/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4=
|
||||
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b h1:h9U78+dx9a4BKdQkBBos92HalKpaGKHrp+3Uo6yTodo=
|
||||
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
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=
|
||||
|
@ -423,8 +420,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
|
|||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
|
||||
github.com/hashicorp/cli v1.1.6 h1:CMOV+/LJfL1tXCOKrgAX0uRKnzjj/mpmqNXloRSy2K8=
|
||||
github.com/hashicorp/cli v1.1.6/go.mod h1:MPon5QYlgjjo0BSoAiN0ESeT5fRzDjVRp+uioJ0piz4=
|
||||
github.com/hashicorp/cli v1.1.7 h1:/fZJ+hNdwfTSfsxMBa9WWMlfjUZbX8/LnUxgAd7lCVU=
|
||||
github.com/hashicorp/cli v1.1.7/go.mod h1:e6Mfpga9OCT1vqzFuoGZiiF/KaG9CbUfO5s3ghU3YgU=
|
||||
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=
|
||||
|
@ -460,20 +457,20 @@ github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iP
|
|||
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw=
|
||||
github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hc-install v0.8.0 h1:LdpZeXkZYMQhoKPCecJHlKvUkQFixN/nvyR1CdfOLjI=
|
||||
github.com/hashicorp/hc-install v0.8.0/go.mod h1:+MwJYjDfCruSD/udvBmRB22Nlkwwkwf5sAB6uTIhSaU=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hc-install v0.9.1 h1:gkqTfE3vVbafGQo6VZXcy2v5yoz2bE0+nhZXruCuODQ=
|
||||
github.com/hashicorp/hc-install v0.9.1/go.mod h1:pWWvN/IrfeBK4XPeXXYkL6EjMufHkCK5DvwxeLKuBf0=
|
||||
github.com/hashicorp/hcl/v2 v2.21.0 h1:lve4q/o/2rqwYOgUg3y3V2YPyD1/zkCLGjIV74Jit14=
|
||||
github.com/hashicorp/hcl/v2 v2.21.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ=
|
||||
github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg=
|
||||
github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7orfb5Ltvec=
|
||||
github.com/hashicorp/terraform-json v0.22.1/go.mod h1:JbWSQCLFSXFFhg42T7l9iJwdGXBYV8fmmD6o/ML4p3A=
|
||||
github.com/hashicorp/terraform-plugin-docs v0.19.4 h1:G3Bgo7J22OMtegIgn8Cd/CaSeyEljqjH3G39w28JK4c=
|
||||
github.com/hashicorp/terraform-plugin-docs v0.19.4/go.mod h1:4pLASsatTmRynVzsjEhbXZ6s7xBlUw/2Kt0zfrq8HxA=
|
||||
github.com/hashicorp/terraform-exec v0.22.0 h1:G5+4Sz6jYZfRYUCg6eQgDsqTzkNXV+fP8l+uRmZHj64=
|
||||
github.com/hashicorp/terraform-exec v0.22.0/go.mod h1:bjVbsncaeh8jVdhttWYZuBGj21FcYw6Ia/XfHcNO7lQ=
|
||||
github.com/hashicorp/terraform-json v0.24.0 h1:rUiyF+x1kYawXeRth6fKFm/MdfBS6+lW4NbeATsYz8Q=
|
||||
github.com/hashicorp/terraform-json v0.24.0/go.mod h1:Nfj5ubo9xbu9uiAoZVBsNOjvNKB66Oyrvtit74kC7ow=
|
||||
github.com/hashicorp/terraform-plugin-docs v0.21.0 h1:yoyA/Y719z9WdFJAhpUkI1jRbKP/nteVNBaI3hW7iQ8=
|
||||
github.com/hashicorp/terraform-plugin-docs v0.21.0/go.mod h1:J4Wott1J2XBKZPp/NkQv7LMShJYOcrqhQ2myXBcu64s=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.11.0 h1:M7+9zBArexHFXDx/pKTxjE6n/2UCXY6b8FIq9ZYhwfE=
|
||||
github.com/hashicorp/terraform-plugin-framework v1.11.0/go.mod h1:qBXLDn69kM97NNVi/MQ9qgd1uWWsVftGSnygYG1tImM=
|
||||
github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/125t+AAurhqphJ2Co=
|
||||
|
@ -524,8 +521,6 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
|
|||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
|
||||
github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
|
@ -556,11 +551,10 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69
|
|||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
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.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
|
@ -595,8 +589,8 @@ github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1
|
|||
github.com/mitchellh/reflectwalk v1.0.0/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/moby/buildkit v0.13.1 h1:L8afOFhPq2RPJJSr/VyzbufwID7jquZVB7oFHbPRcPE=
|
||||
github.com/moby/buildkit v0.13.1/go.mod h1:aNmNQKLBFYAOFuzQjR3VA27/FijlvtBD1pjNwTSN37k=
|
||||
github.com/moby/buildkit v0.16.0 h1:wOVBj1o5YNVad/txPQNXUXdelm7Hs/i0PUFjzbK0VKE=
|
||||
github.com/moby/buildkit v0.16.0/go.mod h1:Xqx/5GlrqE1yIRORk0NSCVDFpQAU1WjlT6KHYZdisIQ=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
|
@ -608,16 +602,18 @@ github.com/moby/swarmkit/v2 v2.0.0-20230315203717-e28e8ba9bc83/go.mod h1:GvjR7mC
|
|||
github.com/moby/sys/mount v0.3.3 h1:fX1SVkXFJ47XWDoeFW4Sq7PdQJnV2QIDZAqjNqgEjUs=
|
||||
github.com/moby/sys/mount v0.3.3/go.mod h1:PBaEorSNTLG5t/+4EgukEQVlAvVEc6ZjTySwKdqp5K0=
|
||||
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
|
||||
github.com/moby/sys/mountinfo v0.7.1 h1:/tTvQaSJRr2FshkhXiIpux6fQ2Zvc4j7tAhMTStAG2g=
|
||||
github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
|
||||
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
|
||||
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
|
||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
|
||||
github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
|
||||
github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0=
|
||||
github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8=
|
||||
github.com/moby/sys/symlink v0.2.0 h1:tk1rOM+Ljp0nFmfOIBtlV3rTDlWOwFRhjEeAhZB0nZc=
|
||||
github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs=
|
||||
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
|
||||
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
|
||||
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
|
||||
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
@ -634,10 +630,12 @@ github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKt
|
|||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A=
|
||||
github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM=
|
||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
|
@ -711,8 +709,8 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
|||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
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/secure-systems-lab/go-securesystemslib v0.7.0 h1:OwvJ5jQf9LnIAS83waAjPbcMsODrTQUpJ02eNLUoxBg=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.7.0/go.mod h1:/2gYnlnHVQ6xeGtfIqFy7Do03K4cdCY0A/GlJLDKLHI=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
|
@ -752,8 +750,8 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
|
|||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
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/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ=
|
||||
github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU=
|
||||
github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d h1:K3j02b5j2Iw1xoggN9B2DIEkhWGheqFOeDkdJdBrJI8=
|
||||
|
@ -772,8 +770,12 @@ github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ0
|
|||
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
|
||||
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
|
||||
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 h1:7I5c2Ig/5FgqkYOh/N87NzoyI9U15qUPXhDD8uCupv8=
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/twpayne/go-vfs/v5 v5.0.4 h1:/ne3h+rW7f5YOyOFguz+3ztfUwzOLR0Vts3y0mMAitg=
|
||||
github.com/twpayne/go-vfs/v5 v5.0.4/go.mod h1:zTPFJUbgsEMFNSWnWQlLq9wh4AN83edZzx3VXbxrS1w=
|
||||
github.com/u-root/uio v0.0.0-20240209044354-b3d14b93376a h1:BH1SOPEvehD2kVrndDnGJiUF0TrBpNs+iyYocu6h0og=
|
||||
github.com/u-root/uio v0.0.0-20240209044354-b3d14b93376a/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
|
@ -811,12 +813,12 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
|||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
|
||||
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark v1.7.7 h1:5m9rrB1sW3JUMToKFQfb+FGt1U7r57IHu5GrYrG2nqU=
|
||||
github.com/yuin/goldmark v1.7.7/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
|
||||
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
|
||||
github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ=
|
||||
github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||
github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70=
|
||||
github.com/zclconf/go-cty v1.16.2/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/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
|
@ -882,11 +884,11 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
|
|||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
|
@ -895,8 +897,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -916,11 +918,11 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
|||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
|
||||
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
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=
|
||||
|
@ -930,8 +932,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -963,23 +965,22 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
@ -989,8 +990,8 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
|||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
|
@ -1006,8 +1007,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
|
|||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||
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=
|
||||
|
|
106
internal/imgutil/imgutil.go
Normal file
106
internal/imgutil/imgutil.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package imgutil
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
eboptions "github.com/coder/envbuilder/options"
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
)
|
||||
|
||||
// GetRemoteImage fetches the image manifest of the image.
|
||||
func GetRemoteImage(imgRef string) (v1.Image, error) {
|
||||
ref, err := name.ParseReference(imgRef)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse reference: %w", err)
|
||||
}
|
||||
|
||||
img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("check remote image: %w", err)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// ExtractEnvbuilderFromImage reads the image located at imgRef and extracts
|
||||
// MagicBinaryLocation to destPath.
|
||||
func ExtractEnvbuilderFromImage(ctx context.Context, imgRef, destPath string) error {
|
||||
var o eboptions.Options
|
||||
o.SetDefaults()
|
||||
needle := strings.TrimPrefix(o.BinaryPath, "/")
|
||||
img, err := GetRemoteImage(imgRef)
|
||||
if err != nil {
|
||||
return fmt.Errorf("check remote image: %w", err)
|
||||
}
|
||||
|
||||
layers, err := img.Layers()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get image layers: %w", err)
|
||||
}
|
||||
|
||||
// Check the layers in reverse order. The last layers are more likely to
|
||||
// include the binary.
|
||||
for i := len(layers) - 1; i >= 0; i-- {
|
||||
ul, err := layers[i].Uncompressed()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get uncompressed layer: %w", err)
|
||||
}
|
||||
|
||||
tr := tar.NewReader(ul)
|
||||
for {
|
||||
th, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("read tar header: %w", err)
|
||||
}
|
||||
|
||||
name := filepath.Clean(th.Name)
|
||||
if th.Typeflag != tar.TypeReg {
|
||||
tflog.Debug(ctx, "skip non-regular file", map[string]any{"name": name, "layer_idx": i + 1})
|
||||
continue
|
||||
}
|
||||
|
||||
if name != needle {
|
||||
tflog.Debug(ctx, "skip file", map[string]any{"name": name, "layer_idx": i + 1})
|
||||
continue
|
||||
}
|
||||
|
||||
tflog.Debug(ctx, "found file", map[string]any{"name": name, "layer_idx": i + 1})
|
||||
if err := os.MkdirAll(filepath.Dir(destPath), 0o755); err != nil {
|
||||
return fmt.Errorf("create parent directories: %w", err)
|
||||
}
|
||||
destF, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create dest file for writing: %w", err)
|
||||
}
|
||||
defer destF.Close()
|
||||
_, err = io.Copy(destF, tr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copy dest file from image: %w", err)
|
||||
}
|
||||
if err := destF.Close(); err != nil {
|
||||
return fmt.Errorf("close dest file: %w", err)
|
||||
}
|
||||
|
||||
if err := os.Chmod(destPath, 0o755); err != nil {
|
||||
return fmt.Errorf("chmod file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("extract envbuilder binary from image %q: %w", imgRef, os.ErrNotExist)
|
||||
}
|
|
@ -1,31 +1,23 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
kconfig "github.com/GoogleContainerTools/kaniko/pkg/config"
|
||||
"github.com/coder/envbuilder"
|
||||
"github.com/coder/envbuilder/constants"
|
||||
eblog "github.com/coder/envbuilder/log"
|
||||
eboptions "github.com/coder/envbuilder/options"
|
||||
"github.com/coder/terraform-provider-envbuilder/internal/imgutil"
|
||||
"github.com/coder/terraform-provider-envbuilder/internal/tfutil"
|
||||
"github.com/go-git/go-billy/v5/osfs"
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
|
||||
|
@ -57,28 +49,30 @@ type CachedImageResourceModel struct {
|
|||
CacheRepo types.String `tfsdk:"cache_repo"`
|
||||
GitURL types.String `tfsdk:"git_url"`
|
||||
// Optional "inputs".
|
||||
BaseImageCacheDir types.String `tfsdk:"base_image_cache_dir"`
|
||||
BuildContextPath types.String `tfsdk:"build_context_path"`
|
||||
CacheTTLDays types.Int64 `tfsdk:"cache_ttl_days"`
|
||||
DevcontainerDir types.String `tfsdk:"devcontainer_dir"`
|
||||
DevcontainerJSONPath types.String `tfsdk:"devcontainer_json_path"`
|
||||
DockerfilePath types.String `tfsdk:"dockerfile_path"`
|
||||
DockerConfigBase64 types.String `tfsdk:"docker_config_base64"`
|
||||
ExitOnBuildFailure types.Bool `tfsdk:"exit_on_build_failure"`
|
||||
ExtraEnv types.Map `tfsdk:"extra_env"`
|
||||
FallbackImage types.String `tfsdk:"fallback_image"`
|
||||
GitCloneDepth types.Int64 `tfsdk:"git_clone_depth"`
|
||||
GitCloneSingleBranch types.Bool `tfsdk:"git_clone_single_branch"`
|
||||
GitHTTPProxyURL types.String `tfsdk:"git_http_proxy_url"`
|
||||
GitPassword types.String `tfsdk:"git_password"`
|
||||
GitSSHPrivateKeyPath types.String `tfsdk:"git_ssh_private_key_path"`
|
||||
GitUsername types.String `tfsdk:"git_username"`
|
||||
IgnorePaths types.List `tfsdk:"ignore_paths"`
|
||||
Insecure types.Bool `tfsdk:"insecure"`
|
||||
RemoteRepoBuildMode types.Bool `tfsdk:"remote_repo_build_mode"`
|
||||
SSLCertBase64 types.String `tfsdk:"ssl_cert_base64"`
|
||||
Verbose types.Bool `tfsdk:"verbose"`
|
||||
WorkspaceFolder types.String `tfsdk:"workspace_folder"`
|
||||
BaseImageCacheDir types.String `tfsdk:"base_image_cache_dir"`
|
||||
BuildContextPath types.String `tfsdk:"build_context_path"`
|
||||
BuildSecrets types.Map `tfsdk:"build_secrets"`
|
||||
CacheTTLDays types.Int64 `tfsdk:"cache_ttl_days"`
|
||||
DevcontainerDir types.String `tfsdk:"devcontainer_dir"`
|
||||
DevcontainerJSONPath types.String `tfsdk:"devcontainer_json_path"`
|
||||
DockerfilePath types.String `tfsdk:"dockerfile_path"`
|
||||
DockerConfigBase64 types.String `tfsdk:"docker_config_base64"`
|
||||
ExitOnBuildFailure types.Bool `tfsdk:"exit_on_build_failure"`
|
||||
ExtraEnv types.Map `tfsdk:"extra_env"`
|
||||
FallbackImage types.String `tfsdk:"fallback_image"`
|
||||
GitCloneDepth types.Int64 `tfsdk:"git_clone_depth"`
|
||||
GitCloneSingleBranch types.Bool `tfsdk:"git_clone_single_branch"`
|
||||
GitHTTPProxyURL types.String `tfsdk:"git_http_proxy_url"`
|
||||
GitPassword types.String `tfsdk:"git_password"`
|
||||
GitSSHPrivateKeyPath types.String `tfsdk:"git_ssh_private_key_path"`
|
||||
GitSSHPrivateKeyBase64 types.String `tfsdk:"git_ssh_private_key_base64"`
|
||||
GitUsername types.String `tfsdk:"git_username"`
|
||||
IgnorePaths types.List `tfsdk:"ignore_paths"`
|
||||
Insecure types.Bool `tfsdk:"insecure"`
|
||||
RemoteRepoBuildMode types.Bool `tfsdk:"remote_repo_build_mode"`
|
||||
SSLCertBase64 types.String `tfsdk:"ssl_cert_base64"`
|
||||
Verbose types.Bool `tfsdk:"verbose"`
|
||||
WorkspaceFolder types.String `tfsdk:"workspace_folder"`
|
||||
// Computed "outputs".
|
||||
Env types.List `tfsdk:"env"`
|
||||
EnvMap types.Map `tfsdk:"env_map"`
|
||||
|
@ -128,6 +122,11 @@ func (r *CachedImageResource) Schema(ctx context.Context, req resource.SchemaReq
|
|||
MarkdownDescription: "(Envbuilder option) Can be specified when a DockerfilePath is specified outside the base WorkspaceFolder. This path MUST be relative to the WorkspaceFolder path into which the repo is cloned.",
|
||||
Optional: true,
|
||||
},
|
||||
"build_secrets": schema.MapAttribute{
|
||||
MarkdownDescription: "The secrets to use for the build. This is a map of key-value pairs.",
|
||||
ElementType: types.StringType,
|
||||
Optional: true,
|
||||
},
|
||||
"cache_ttl_days": schema.Int64Attribute{
|
||||
MarkdownDescription: "(Envbuilder option) The number of days to use cached layers before expiring them. Defaults to 7 days.",
|
||||
Optional: true,
|
||||
|
@ -194,6 +193,11 @@ func (r *CachedImageResource) Schema(ctx context.Context, req resource.SchemaReq
|
|||
MarkdownDescription: "(Envbuilder option) Path to an SSH private key to be used for Git authentication.",
|
||||
Optional: true,
|
||||
},
|
||||
"git_ssh_private_key_base64": schema.StringAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) Base64 encoded SSH private key to be used for Git authentication.",
|
||||
Optional: true,
|
||||
Sensitive: true,
|
||||
},
|
||||
"git_username": schema.StringAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) The username to use for Git authentication. This is optional.",
|
||||
Optional: true,
|
||||
|
@ -295,129 +299,11 @@ func (r *CachedImageResource) Configure(ctx context.Context, req resource.Config
|
|||
|
||||
// setComputedEnv sets data.Env and data.EnvMap based on the values of the
|
||||
// other fields in the model.
|
||||
func (data *CachedImageResourceModel) setComputedEnv(ctx context.Context) diag.Diagnostics {
|
||||
env := make(map[string]string)
|
||||
|
||||
env["ENVBUILDER_CACHE_REPO"] = tfValueToString(data.CacheRepo)
|
||||
env["ENVBUILDER_GIT_URL"] = tfValueToString(data.GitURL)
|
||||
|
||||
if !data.BaseImageCacheDir.IsNull() {
|
||||
env["ENVBUILDER_BASE_IMAGE_CACHE_DIR"] = tfValueToString(data.BaseImageCacheDir)
|
||||
}
|
||||
|
||||
if !data.BuildContextPath.IsNull() {
|
||||
env["ENVBUILDER_BUILD_CONTEXT_PATH"] = tfValueToString(data.BuildContextPath)
|
||||
}
|
||||
|
||||
if !data.CacheTTLDays.IsNull() {
|
||||
env["ENVBUILDER_CACHE_TTL_DAYS"] = tfValueToString(data.CacheTTLDays)
|
||||
}
|
||||
|
||||
if !data.DevcontainerDir.IsNull() {
|
||||
env["ENVBUILDER_DEVCONTAINER_DIR"] = tfValueToString(data.DevcontainerDir)
|
||||
}
|
||||
|
||||
if !data.DevcontainerJSONPath.IsNull() {
|
||||
env["ENVBUILDER_DEVCONTAINER_JSON_PATH"] = tfValueToString(data.DevcontainerJSONPath)
|
||||
}
|
||||
|
||||
if !data.DockerfilePath.IsNull() {
|
||||
env["ENVBUILDER_DOCKERFILE_PATH"] = tfValueToString(data.DockerfilePath)
|
||||
}
|
||||
|
||||
if !data.DockerConfigBase64.IsNull() {
|
||||
env["ENVBUILDER_DOCKER_CONFIG_BASE64"] = tfValueToString(data.DockerConfigBase64)
|
||||
}
|
||||
|
||||
if !data.ExitOnBuildFailure.IsNull() {
|
||||
env["ENVBUILDER_EXIT_ON_BUILD_FAILURE"] = tfValueToString(data.ExitOnBuildFailure)
|
||||
}
|
||||
|
||||
if !data.FallbackImage.IsNull() {
|
||||
env["ENVBUILDER_FALLBACK_IMAGE"] = tfValueToString(data.FallbackImage)
|
||||
}
|
||||
|
||||
if !data.GitCloneDepth.IsNull() {
|
||||
env["ENVBUILDER_GIT_CLONE_DEPTH"] = tfValueToString(data.GitCloneDepth)
|
||||
}
|
||||
|
||||
if !data.GitCloneSingleBranch.IsNull() {
|
||||
env["ENVBUILDER_GIT_CLONE_SINGLE_BRANCH"] = tfValueToString(data.GitCloneSingleBranch)
|
||||
}
|
||||
|
||||
if !data.GitHTTPProxyURL.IsNull() {
|
||||
env["ENVBUILDER_GIT_HTTP_PROXY_URL"] = tfValueToString(data.GitHTTPProxyURL)
|
||||
}
|
||||
|
||||
if !data.GitSSHPrivateKeyPath.IsNull() {
|
||||
env["ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH"] = tfValueToString(data.GitSSHPrivateKeyPath)
|
||||
}
|
||||
|
||||
if !data.GitUsername.IsNull() {
|
||||
env["ENVBUILDER_GIT_USERNAME"] = tfValueToString(data.GitUsername)
|
||||
}
|
||||
|
||||
if !data.GitPassword.IsNull() {
|
||||
env["ENVBUILDER_GIT_PASSWORD"] = tfValueToString(data.GitPassword)
|
||||
}
|
||||
|
||||
if !data.IgnorePaths.IsNull() {
|
||||
env["ENVBUILDER_IGNORE_PATHS"] = strings.Join(tfListToStringSlice(data.IgnorePaths), ",")
|
||||
}
|
||||
|
||||
if !data.Insecure.IsNull() {
|
||||
env["ENVBUILDER_INSECURE"] = tfValueToString(data.Insecure)
|
||||
}
|
||||
|
||||
// Default to remote build mode.
|
||||
if data.RemoteRepoBuildMode.IsNull() {
|
||||
env["ENVBUILDER_REMOTE_REPO_BUILD_MODE"] = "true"
|
||||
} else {
|
||||
env["ENVBUILDER_REMOTE_REPO_BUILD_MODE"] = tfValueToString(data.RemoteRepoBuildMode)
|
||||
}
|
||||
|
||||
if !data.SSLCertBase64.IsNull() {
|
||||
env["ENVBUILDER_SSL_CERT_BASE64"] = tfValueToString(data.SSLCertBase64)
|
||||
}
|
||||
|
||||
if !data.Verbose.IsNull() {
|
||||
env["ENVBUILDER_VERBOSE"] = tfValueToString(data.Verbose)
|
||||
}
|
||||
|
||||
if !data.WorkspaceFolder.IsNull() {
|
||||
env["ENVBUILDER_WORKSPACE_FOLDER"] = tfValueToString(data.WorkspaceFolder)
|
||||
}
|
||||
|
||||
// Do ExtraEnv last so that it can override any other values.
|
||||
// With one exception: ENVBUILDER_CACHE_REPO and ENVBUILDER_GIT_URL are required and should not be overridden.
|
||||
// Other values set by the provider may be overridden, but will generate a warning.
|
||||
func (data *CachedImageResourceModel) setComputedEnv(ctx context.Context, env map[string]string) diag.Diagnostics {
|
||||
var diag, ds diag.Diagnostics
|
||||
if !data.ExtraEnv.IsNull() {
|
||||
for key, elem := range data.ExtraEnv.Elements() {
|
||||
switch key {
|
||||
// These are required and should not be overridden.
|
||||
case "ENVBUILDER_CACHE_REPO", "ENVBUILDER_GIT_URL":
|
||||
diag.AddAttributeWarning(path.Root("extra_env"),
|
||||
"Cannot override required environment variable",
|
||||
fmt.Sprintf("The key %q in extra_env cannot be overridden.", key),
|
||||
)
|
||||
default:
|
||||
if _, ok := env[key]; ok {
|
||||
// This is a warning because it's possible that the user wants to override
|
||||
// a value set by the provider.
|
||||
diag.AddAttributeWarning(path.Root("extra_env"),
|
||||
"Overriding provider environment variable",
|
||||
fmt.Sprintf("The key %q in extra_env overrides an environment variable set by the provider.", key),
|
||||
)
|
||||
}
|
||||
env[key] = tfValueToString(elem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.EnvMap, ds = basetypes.NewMapValueFrom(ctx, types.StringType, env)
|
||||
diag = append(diag, ds...)
|
||||
data.Env, ds = basetypes.NewListValueFrom(ctx, types.StringType, sortedKeyValues(env))
|
||||
data.Env, ds = basetypes.NewListValueFrom(ctx, types.StringType, tfutil.DockerEnv(env))
|
||||
diag = append(diag, ds...)
|
||||
return diag
|
||||
}
|
||||
|
@ -431,6 +317,16 @@ func (r *CachedImageResource) Read(ctx context.Context, req resource.ReadRequest
|
|||
return
|
||||
}
|
||||
|
||||
// Get the options from the data model.
|
||||
opts, diags := optionsFromDataModel(data)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
// Set the expected environment variables.
|
||||
computedEnv := computeEnvFromOptions(opts, tfutil.TFMapToStringMap(data.ExtraEnv))
|
||||
resp.Diagnostics.Append(data.setComputedEnv(ctx, computedEnv)...)
|
||||
|
||||
// If the previous state is that Image == BuilderImage, then we previously did
|
||||
// not find the image. We will need to run another cache probe.
|
||||
if data.Image.Equal(data.BuilderImage) {
|
||||
|
@ -444,7 +340,7 @@ func (r *CachedImageResource) Read(ctx context.Context, req resource.ReadRequest
|
|||
}
|
||||
|
||||
// Check the remote registry for the image we previously found.
|
||||
img, err := getRemoteImage(data.Image.ValueString())
|
||||
img, err := imgutil.GetRemoteImage(data.Image.ValueString())
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "MANIFEST_UNKNOWN") {
|
||||
// Explicitly not making this an error diag.
|
||||
|
@ -478,9 +374,6 @@ func (r *CachedImageResource) Read(ctx context.Context, req resource.ReadRequest
|
|||
data.Image = types.StringValue(fmt.Sprintf("%s@%s", data.CacheRepo.ValueString(), digest))
|
||||
data.Exists = types.BoolValue(true)
|
||||
|
||||
// Set the expected environment variables.
|
||||
resp.Diagnostics.Append(data.setComputedEnv(ctx)...)
|
||||
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
|
@ -494,7 +387,18 @@ func (r *CachedImageResource) Create(ctx context.Context, req resource.CreateReq
|
|||
return
|
||||
}
|
||||
|
||||
cachedImg, err := r.runCacheProbe(ctx, data)
|
||||
// Get the options from the data model.
|
||||
opts, diags := optionsFromDataModel(data)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
// Set the expected environment variables.
|
||||
computedEnv := computeEnvFromOptions(opts, tfutil.TFMapToStringMap(data.ExtraEnv))
|
||||
resp.Diagnostics.Append(data.setComputedEnv(ctx, computedEnv)...)
|
||||
|
||||
cachedImg, err := runCacheProbe(ctx, data.BuilderImage.ValueString(), opts)
|
||||
data.ID = types.StringValue(uuid.Nil.String())
|
||||
data.Exists = types.BoolValue(err == nil)
|
||||
if err != nil {
|
||||
|
@ -517,9 +421,6 @@ func (r *CachedImageResource) Create(ctx context.Context, req resource.CreateReq
|
|||
data.ID = types.StringValue(digest.String())
|
||||
}
|
||||
|
||||
// Set the expected environment variables.
|
||||
resp.Diagnostics.Append(data.setComputedEnv(ctx)...)
|
||||
|
||||
// Save data into Terraform state
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
@ -553,7 +454,7 @@ func (r *CachedImageResource) Delete(ctx context.Context, req resource.DeleteReq
|
|||
// runCacheProbe performs a 'fake build' of the requested image and ensures that
|
||||
// all of the resulting layers of the image are present in the configured cache
|
||||
// repo. Otherwise, returns an error.
|
||||
func (r *CachedImageResource) runCacheProbe(ctx context.Context, data CachedImageResourceModel) (v1.Image, error) {
|
||||
func runCacheProbe(ctx context.Context, builderImage string, opts eboptions.Options) (v1.Image, error) {
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "envbuilder-provider-cached-image-data-source")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create temp directory: %s", err.Error())
|
||||
|
@ -565,7 +466,7 @@ func (r *CachedImageResource) runCacheProbe(ctx context.Context, data CachedImag
|
|||
}()
|
||||
|
||||
oldKanikoDir := kconfig.KanikoDir
|
||||
tmpKanikoDir := filepath.Join(tmpDir, constants.MagicDir)
|
||||
tmpKanikoDir := filepath.Join(tmpDir, ".envbuilder")
|
||||
// Normally you would set the KANIKO_DIR environment variable, but we are importing kaniko directly.
|
||||
kconfig.KanikoDir = tmpKanikoDir
|
||||
tflog.Info(ctx, "set kaniko dir to "+tmpKanikoDir)
|
||||
|
@ -577,225 +478,52 @@ func (r *CachedImageResource) runCacheProbe(ctx context.Context, data CachedImag
|
|||
if err := os.MkdirAll(tmpKanikoDir, 0o755); err != nil {
|
||||
return nil, fmt.Errorf("failed to create kaniko dir: %w", err)
|
||||
}
|
||||
// Use the temporary directory as our 'magic dir'.
|
||||
opts.WorkingDirBase = tmpKanikoDir
|
||||
|
||||
// In order to correctly reproduce the final layer of the cached image, we
|
||||
// need the envbuilder binary used to originally build the image!
|
||||
envbuilderPath := filepath.Join(tmpDir, "envbuilder")
|
||||
if err := extractEnvbuilderFromImage(ctx, data.BuilderImage.ValueString(), envbuilderPath); err != nil {
|
||||
if err := imgutil.ExtractEnvbuilderFromImage(ctx, builderImage, envbuilderPath); err != nil {
|
||||
tflog.Error(ctx, "failed to fetch envbuilder binary from builder image", map[string]any{"err": err})
|
||||
return nil, fmt.Errorf("failed to fetch the envbuilder binary from the builder image: %s", err.Error())
|
||||
}
|
||||
opts.BinaryPath = envbuilderPath
|
||||
|
||||
workspaceFolder := data.WorkspaceFolder.ValueString()
|
||||
if workspaceFolder == "" {
|
||||
workspaceFolder = filepath.Join(tmpDir, "workspace")
|
||||
tflog.Debug(ctx, "workspace_folder not specified, using temp dir", map[string]any{"workspace_folder": workspaceFolder})
|
||||
// We need a filesystem to work with.
|
||||
opts.Filesystem = osfs.New("/")
|
||||
// This should never be set to true, as this may be running outside of a container!
|
||||
opts.ForceSafe = false
|
||||
// We always want to get the cached image.
|
||||
opts.GetCachedImage = true
|
||||
// Log to the Terraform logger.
|
||||
opts.Logger = tfutil.TFLogFunc(ctx)
|
||||
|
||||
// We don't require users to set a workspace folder, but maybe there's a
|
||||
// reason someone may need to.
|
||||
if opts.WorkspaceFolder == "" {
|
||||
opts.WorkspaceFolder = filepath.Join(tmpDir, "workspace")
|
||||
if err := os.MkdirAll(opts.WorkspaceFolder, 0o755); err != nil {
|
||||
return nil, fmt.Errorf("failed to create workspace folder: %w", err)
|
||||
}
|
||||
tflog.Debug(ctx, "workspace_folder not specified, using temp dir", map[string]any{"workspace_folder": opts.WorkspaceFolder})
|
||||
}
|
||||
|
||||
opts := eboptions.Options{
|
||||
// These options are always required
|
||||
CacheRepo: data.CacheRepo.ValueString(),
|
||||
Filesystem: osfs.New("/"),
|
||||
ForceSafe: false, // This should never be set to true, as this may be running outside of a container!
|
||||
GetCachedImage: true, // always!
|
||||
Logger: tfLogFunc(ctx),
|
||||
Verbose: data.Verbose.ValueBool(),
|
||||
WorkspaceFolder: workspaceFolder,
|
||||
|
||||
// Options related to compiling the devcontainer
|
||||
BuildContextPath: data.BuildContextPath.ValueString(),
|
||||
DevcontainerDir: data.DevcontainerDir.ValueString(),
|
||||
DevcontainerJSONPath: data.DevcontainerJSONPath.ValueString(),
|
||||
DockerfilePath: data.DockerfilePath.ValueString(),
|
||||
DockerConfigBase64: data.DockerConfigBase64.ValueString(),
|
||||
FallbackImage: data.FallbackImage.ValueString(),
|
||||
|
||||
// These options are required for cloning the Git repo
|
||||
CacheTTLDays: data.CacheTTLDays.ValueInt64(),
|
||||
GitURL: data.GitURL.ValueString(),
|
||||
GitCloneDepth: data.GitCloneDepth.ValueInt64(),
|
||||
GitCloneSingleBranch: data.GitCloneSingleBranch.ValueBool(),
|
||||
GitUsername: data.GitUsername.ValueString(),
|
||||
GitPassword: data.GitPassword.ValueString(),
|
||||
GitSSHPrivateKeyPath: data.GitSSHPrivateKeyPath.ValueString(),
|
||||
GitHTTPProxyURL: data.GitHTTPProxyURL.ValueString(),
|
||||
RemoteRepoBuildMode: data.RemoteRepoBuildMode.ValueBool(),
|
||||
RemoteRepoDir: filepath.Join(tmpDir, "repo"),
|
||||
SSLCertBase64: data.SSLCertBase64.ValueString(),
|
||||
|
||||
// Other options
|
||||
BaseImageCacheDir: data.BaseImageCacheDir.ValueString(),
|
||||
BinaryPath: envbuilderPath, // needed to reproduce the final layer.
|
||||
ExitOnBuildFailure: data.ExitOnBuildFailure.ValueBool(), // may wish to do this instead of fallback image?
|
||||
Insecure: data.Insecure.ValueBool(), // might have internal CAs?
|
||||
IgnorePaths: tfListToStringSlice(data.IgnorePaths), // may need to be specified?
|
||||
// The below options are not relevant and are set to their zero value explicitly.
|
||||
// They must be set by extra_env.
|
||||
CoderAgentSubsystem: nil,
|
||||
CoderAgentToken: "",
|
||||
CoderAgentURL: "",
|
||||
ExportEnvFile: "",
|
||||
InitArgs: "",
|
||||
InitCommand: "",
|
||||
InitScript: "",
|
||||
LayerCacheDir: "",
|
||||
PostStartScriptPath: "",
|
||||
PushImage: false, // This is only relevant when building.
|
||||
SetupScript: "",
|
||||
SkipRebuild: false,
|
||||
}
|
||||
// The below options are not relevant and are set to their zero value
|
||||
// explicitly.
|
||||
// They must be set by extra_env to be used in the final builder image.
|
||||
opts.CoderAgentSubsystem = nil
|
||||
opts.CoderAgentToken = ""
|
||||
opts.CoderAgentURL = ""
|
||||
opts.ExportEnvFile = ""
|
||||
opts.InitArgs = ""
|
||||
opts.InitCommand = ""
|
||||
opts.InitScript = ""
|
||||
opts.LayerCacheDir = ""
|
||||
opts.PostStartScriptPath = ""
|
||||
opts.PushImage = false
|
||||
opts.SetupScript = ""
|
||||
opts.SkipRebuild = false
|
||||
|
||||
return envbuilder.RunCacheProbe(ctx, opts)
|
||||
}
|
||||
|
||||
// getRemoteImage fetches the image manifest of the image.
|
||||
func getRemoteImage(imgRef string) (v1.Image, error) {
|
||||
ref, err := name.ParseReference(imgRef)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse reference: %w", err)
|
||||
}
|
||||
|
||||
img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("check remote image: %w", err)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// extractEnvbuilderFromImage reads the image located at imgRef and extracts
|
||||
// MagicBinaryLocation to destPath.
|
||||
func extractEnvbuilderFromImage(ctx context.Context, imgRef, destPath string) error {
|
||||
needle := filepath.Clean(constants.MagicBinaryLocation)[1:] // skip leading '/'
|
||||
img, err := getRemoteImage(imgRef)
|
||||
if err != nil {
|
||||
return fmt.Errorf("check remote image: %w", err)
|
||||
}
|
||||
|
||||
layers, err := img.Layers()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get image layers: %w", err)
|
||||
}
|
||||
|
||||
// Check the layers in reverse order. The last layers are more likely to
|
||||
// include the binary.
|
||||
for i := len(layers) - 1; i >= 0; i-- {
|
||||
ul, err := layers[i].Uncompressed()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get uncompressed layer: %w", err)
|
||||
}
|
||||
|
||||
tr := tar.NewReader(ul)
|
||||
for {
|
||||
th, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("read tar header: %w", err)
|
||||
}
|
||||
|
||||
name := filepath.Clean(th.Name)
|
||||
if th.Typeflag != tar.TypeReg {
|
||||
tflog.Debug(ctx, "skip non-regular file", map[string]any{"name": name, "layer_idx": i + 1})
|
||||
continue
|
||||
}
|
||||
|
||||
if name != needle {
|
||||
tflog.Debug(ctx, "skip file", map[string]any{"name": name, "layer_idx": i + 1})
|
||||
continue
|
||||
}
|
||||
|
||||
tflog.Debug(ctx, "found file", map[string]any{"name": name, "layer_idx": i + 1})
|
||||
if err := os.MkdirAll(filepath.Dir(destPath), 0o755); err != nil {
|
||||
return fmt.Errorf("create parent directories: %w", err)
|
||||
}
|
||||
destF, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create dest file for writing: %w", err)
|
||||
}
|
||||
defer destF.Close()
|
||||
_, err = io.Copy(destF, tr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copy dest file from image: %w", err)
|
||||
}
|
||||
if err := destF.Close(); err != nil {
|
||||
return fmt.Errorf("close dest file: %w", err)
|
||||
}
|
||||
|
||||
if err := os.Chmod(destPath, 0o755); err != nil {
|
||||
return fmt.Errorf("chmod file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("extract envbuilder binary from image %q: %w", imgRef, os.ErrNotExist)
|
||||
}
|
||||
|
||||
// tfValueToString converts an attr.Value to its string representation
|
||||
// based on its Terraform type. This is needed because the String()
|
||||
// method on an attr.Value creates a 'human-readable' version of the type, which
|
||||
// leads to quotes, escaped characters, and other assorted sadness.
|
||||
func tfValueToString(val attr.Value) string {
|
||||
if val.IsUnknown() || val.IsNull() {
|
||||
return ""
|
||||
}
|
||||
if vs, ok := val.(interface{ ValueString() string }); ok {
|
||||
return vs.ValueString()
|
||||
}
|
||||
if vb, ok := val.(interface{ ValueBool() bool }); ok {
|
||||
return fmt.Sprintf("%t", vb.ValueBool())
|
||||
}
|
||||
if vi, ok := val.(interface{ ValueInt64() int64 }); ok {
|
||||
return fmt.Sprintf("%d", vi.ValueInt64())
|
||||
}
|
||||
panic(fmt.Errorf("tfValueToString: value %T is not a supported type", val))
|
||||
}
|
||||
|
||||
// tfListToStringSlice converts a types.List to a []string by calling
|
||||
// tfValueToString on each element.
|
||||
func tfListToStringSlice(l types.List) []string {
|
||||
var ss []string
|
||||
for _, el := range l.Elements() {
|
||||
ss = append(ss, tfValueToString(el))
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
// tfLogFunc is an adapter to envbuilder/log.Func.
|
||||
func tfLogFunc(ctx context.Context) eblog.Func {
|
||||
return func(level eblog.Level, format string, args ...any) {
|
||||
var logFn func(context.Context, string, ...map[string]interface{})
|
||||
switch level {
|
||||
case eblog.LevelTrace:
|
||||
logFn = tflog.Trace
|
||||
case eblog.LevelDebug:
|
||||
logFn = tflog.Debug
|
||||
case eblog.LevelWarn:
|
||||
logFn = tflog.Warn
|
||||
case eblog.LevelError:
|
||||
logFn = tflog.Error
|
||||
default:
|
||||
logFn = tflog.Info
|
||||
}
|
||||
logFn(ctx, fmt.Sprintf(format, args...))
|
||||
}
|
||||
}
|
||||
|
||||
// sortedKeyValues returns the keys and values of the map in the form "key=value"
|
||||
// sorted by key in lexicographical order.
|
||||
func sortedKeyValues(m map[string]string) []string {
|
||||
pairs := make([]string, 0, len(m))
|
||||
var sb strings.Builder
|
||||
for k := range m {
|
||||
_, _ = sb.WriteString(k)
|
||||
_, _ = sb.WriteRune('=')
|
||||
_, _ = sb.WriteString(m[k])
|
||||
pairs = append(pairs, sb.String())
|
||||
sb.Reset()
|
||||
}
|
||||
sort.Strings(pairs)
|
||||
return pairs
|
||||
}
|
||||
|
|
|
@ -20,8 +20,10 @@ func TestAccCachedImageResource(t *testing.T) {
|
|||
defer cancel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
files map[string]string
|
||||
name string
|
||||
files map[string]string
|
||||
extraEnv map[string]string
|
||||
assertEnv func(t *testing.T, deps testDependencies) resource.TestCheckFunc
|
||||
}{
|
||||
{
|
||||
// This test case is the simplest possible case: a devcontainer.json.
|
||||
|
@ -31,6 +33,28 @@ func TestAccCachedImageResource(t *testing.T) {
|
|||
files: map[string]string{
|
||||
".devcontainer/devcontainer.json": `{"image": "localhost:5000/test-ubuntu:latest"}`,
|
||||
},
|
||||
extraEnv: map[string]string{
|
||||
"CODER_AGENT_TOKEN": "some-token",
|
||||
"CODER_AGENT_URL": "https://coder.example.com",
|
||||
"ENVBUILDER_GIT_URL": "https://not.the.real.git/url",
|
||||
"ENVBUILDER_CACHE_REPO": "not-the-real-cache-repo",
|
||||
"FOO": testEnvValue,
|
||||
},
|
||||
assertEnv: func(t *testing.T, deps testDependencies) resource.TestCheckFunc {
|
||||
return resource.ComposeAggregateTestCheckFunc(
|
||||
assertEnv(t,
|
||||
"CODER_AGENT_TOKEN", "some-token",
|
||||
"CODER_AGENT_URL", "https://coder.example.com",
|
||||
"ENVBUILDER_CACHE_REPO", deps.CacheRepo,
|
||||
"ENVBUILDER_DOCKER_CONFIG_BASE64", deps.DockerConfigBase64,
|
||||
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", deps.Repo.Key,
|
||||
"ENVBUILDER_GIT_URL", deps.Repo.URL,
|
||||
"ENVBUILDER_REMOTE_REPO_BUILD_MODE", "true",
|
||||
"ENVBUILDER_VERBOSE", "true",
|
||||
"FOO", "bar\nbaz",
|
||||
),
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
// This test case includes a Dockerfile in addition to the devcontainer.json.
|
||||
|
@ -42,14 +66,143 @@ func TestAccCachedImageResource(t *testing.T) {
|
|||
".devcontainer/Dockerfile": `FROM localhost:5000/test-ubuntu:latest
|
||||
RUN date > /date.txt`,
|
||||
},
|
||||
extraEnv: map[string]string{
|
||||
"CODER_AGENT_TOKEN": "some-token",
|
||||
"CODER_AGENT_URL": "https://coder.example.com",
|
||||
"FOO": testEnvValue,
|
||||
"ENVBUILDER_GIT_URL": "https://not.the.real.git/url",
|
||||
"ENVBUILDER_CACHE_REPO": "not-the-real-cache-repo",
|
||||
},
|
||||
assertEnv: func(t *testing.T, deps testDependencies) resource.TestCheckFunc {
|
||||
return resource.ComposeAggregateTestCheckFunc(
|
||||
assertEnv(t,
|
||||
"CODER_AGENT_TOKEN", "some-token",
|
||||
"CODER_AGENT_URL", "https://coder.example.com",
|
||||
"ENVBUILDER_CACHE_REPO", deps.CacheRepo,
|
||||
"ENVBUILDER_DOCKER_CONFIG_BASE64", deps.DockerConfigBase64,
|
||||
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", deps.Repo.Key,
|
||||
"ENVBUILDER_GIT_URL", deps.Repo.URL,
|
||||
"ENVBUILDER_REMOTE_REPO_BUILD_MODE", "true",
|
||||
"ENVBUILDER_VERBOSE", "true",
|
||||
"FOO", "bar\nbaz",
|
||||
),
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
// This test case ensures that overriding the devcontainer directory works.
|
||||
name: "different_dir",
|
||||
files: map[string]string{
|
||||
"path/to/.devcontainer/devcontainer.json": `{"build": { "dockerfile": "Dockerfile" }}`,
|
||||
"path/to/.devcontainer/Dockerfile": `FROM localhost:5000/test-ubuntu:latest
|
||||
RUN date > /date.txt`,
|
||||
},
|
||||
extraEnv: map[string]string{
|
||||
"CODER_AGENT_TOKEN": "some-token",
|
||||
"CODER_AGENT_URL": "https://coder.example.com",
|
||||
"FOO": testEnvValue,
|
||||
"ENVBUILDER_GIT_URL": "https://not.the.real.git/url",
|
||||
"ENVBUILDER_CACHE_REPO": "not-the-real-cache-repo",
|
||||
"ENVBUILDER_DEVCONTAINER_DIR": "path/to/.devcontainer",
|
||||
"ENVBUILDER_DEVCONTAINER_JSON_PATH": "path/to/.devcontainer/devcontainer.json",
|
||||
"ENVBUILDER_DOCKERFILE_PATH": "path/to/.devcontainer/Dockerfile",
|
||||
},
|
||||
assertEnv: func(t *testing.T, deps testDependencies) resource.TestCheckFunc {
|
||||
return resource.ComposeAggregateTestCheckFunc(
|
||||
assertEnv(t,
|
||||
"CODER_AGENT_TOKEN", "some-token",
|
||||
"CODER_AGENT_URL", "https://coder.example.com",
|
||||
"ENVBUILDER_CACHE_REPO", deps.CacheRepo,
|
||||
"ENVBUILDER_DEVCONTAINER_DIR", "path/to/.devcontainer",
|
||||
"ENVBUILDER_DEVCONTAINER_JSON_PATH", "path/to/.devcontainer/devcontainer.json",
|
||||
"ENVBUILDER_DOCKERFILE_PATH", "path/to/.devcontainer/Dockerfile",
|
||||
"ENVBUILDER_DOCKER_CONFIG_BASE64", deps.DockerConfigBase64,
|
||||
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", deps.Repo.Key,
|
||||
"ENVBUILDER_GIT_URL", deps.Repo.URL,
|
||||
"ENVBUILDER_REMOTE_REPO_BUILD_MODE", "true",
|
||||
"ENVBUILDER_VERBOSE", "true",
|
||||
"FOO", "bar\nbaz",
|
||||
),
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
// This tests that a multi-stage build works correctly.
|
||||
name: "multistage_run_copy",
|
||||
files: map[string]string{
|
||||
"Dockerfile": `
|
||||
FROM localhost:5000/test-ubuntu:latest AS a
|
||||
RUN date > /date.txt
|
||||
FROM localhost:5000/test-ubuntu:latest
|
||||
COPY --from=a /date.txt /date.txt`,
|
||||
},
|
||||
extraEnv: map[string]string{
|
||||
"CODER_AGENT_TOKEN": "some-token",
|
||||
"CODER_AGENT_URL": "https://coder.example.com",
|
||||
"FOO": testEnvValue,
|
||||
"ENVBUILDER_GIT_URL": "https://not.the.real.git/url",
|
||||
"ENVBUILDER_CACHE_REPO": "not-the-real-cache-repo",
|
||||
"ENVBUILDER_DOCKERFILE_PATH": "Dockerfile",
|
||||
},
|
||||
assertEnv: func(t *testing.T, deps testDependencies) resource.TestCheckFunc {
|
||||
return resource.ComposeAggregateTestCheckFunc(
|
||||
assertEnv(t,
|
||||
"CODER_AGENT_TOKEN", "some-token",
|
||||
"CODER_AGENT_URL", "https://coder.example.com",
|
||||
"ENVBUILDER_CACHE_REPO", deps.CacheRepo,
|
||||
"ENVBUILDER_DOCKERFILE_PATH", "Dockerfile",
|
||||
"ENVBUILDER_DOCKER_CONFIG_BASE64", deps.DockerConfigBase64,
|
||||
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", deps.Repo.Key,
|
||||
"ENVBUILDER_GIT_URL", deps.Repo.URL,
|
||||
"ENVBUILDER_REMOTE_REPO_BUILD_MODE", "true",
|
||||
"ENVBUILDER_VERBOSE", "true",
|
||||
"FOO", "bar\nbaz",
|
||||
),
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
// This tests correct handling of the difference in permissions between
|
||||
// the provider and the image when running a COPY instruction.
|
||||
// Added to verify fix for coder/terraform-provider-envbuilder#43
|
||||
name: "copy_perms",
|
||||
files: map[string]string{
|
||||
"Dockerfile": `
|
||||
FROM localhost:5000/test-ubuntu:latest AS a
|
||||
COPY date.txt /date.txt
|
||||
FROM localhost:5000/test-ubuntu:latest
|
||||
COPY --from=a /date.txt /date.txt`,
|
||||
"date.txt": fmt.Sprintf("%d", time.Now().Unix()),
|
||||
},
|
||||
extraEnv: map[string]string{
|
||||
"CODER_AGENT_TOKEN": "some-token",
|
||||
"CODER_AGENT_URL": "https://coder.example.com",
|
||||
"FOO": testEnvValue,
|
||||
"ENVBUILDER_GIT_URL": "https://not.the.real.git/url",
|
||||
"ENVBUILDER_CACHE_REPO": "not-the-real-cache-repo",
|
||||
"ENVBUILDER_DOCKERFILE_PATH": "Dockerfile",
|
||||
},
|
||||
assertEnv: func(t *testing.T, deps testDependencies) resource.TestCheckFunc {
|
||||
return resource.ComposeAggregateTestCheckFunc(
|
||||
assertEnv(t,
|
||||
"CODER_AGENT_TOKEN", "some-token",
|
||||
"CODER_AGENT_URL", "https://coder.example.com",
|
||||
"ENVBUILDER_CACHE_REPO", deps.CacheRepo,
|
||||
"ENVBUILDER_DOCKERFILE_PATH", "Dockerfile",
|
||||
"ENVBUILDER_DOCKER_CONFIG_BASE64", deps.DockerConfigBase64,
|
||||
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", deps.Repo.Key,
|
||||
"ENVBUILDER_GIT_URL", deps.Repo.URL,
|
||||
"ENVBUILDER_REMOTE_REPO_BUILD_MODE", "true",
|
||||
"ENVBUILDER_VERBOSE", "true",
|
||||
"FOO", "bar\nbaz",
|
||||
),
|
||||
)
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
//nolint: paralleltest
|
||||
deps := setup(ctx, t, tc.files)
|
||||
deps.ExtraEnv["FOO"] = testEnvValue
|
||||
deps.ExtraEnv["ENVBUILDER_GIT_URL"] = "https://not.the.real.git/url"
|
||||
deps.ExtraEnv["ENVBUILDER_CACHE_REPO"] = "not-the-real-cache-repo"
|
||||
deps := setup(ctx, t, tc.extraEnv, tc.files)
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
|
@ -71,14 +224,13 @@ RUN date > /date.txt`,
|
|||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "image", deps.BuilderImage),
|
||||
// Inputs should still be present.
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "cache_repo", deps.CacheRepo),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "extra_env.FOO", "bar\nbaz"),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "git_url", deps.Repo.URL),
|
||||
// Should be empty
|
||||
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_username"),
|
||||
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_password"),
|
||||
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "cache_ttl_days"),
|
||||
// Environment variables
|
||||
assertEnv(t, deps),
|
||||
tc.assertEnv(t, deps),
|
||||
),
|
||||
ExpectNonEmptyPlan: true, // TODO: check the plan.
|
||||
},
|
||||
|
@ -93,14 +245,13 @@ RUN date > /date.txt`,
|
|||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "image", deps.BuilderImage),
|
||||
// Inputs should still be present.
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "cache_repo", deps.CacheRepo),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "extra_env.FOO", "bar\nbaz"),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "git_url", deps.Repo.URL),
|
||||
// Should be empty
|
||||
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_username"),
|
||||
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_password"),
|
||||
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "cache_ttl_days"),
|
||||
// Environment variables
|
||||
assertEnv(t, deps),
|
||||
tc.assertEnv(t, deps),
|
||||
),
|
||||
ExpectNonEmptyPlan: true, // TODO: check the plan.
|
||||
},
|
||||
|
@ -113,7 +264,6 @@ RUN date > /date.txt`,
|
|||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Inputs should still be present.
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "cache_repo", deps.CacheRepo),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "extra_env.FOO", "bar\nbaz"),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "git_url", deps.Repo.URL),
|
||||
// Should be empty
|
||||
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "git_username"),
|
||||
|
@ -125,7 +275,7 @@ RUN date > /date.txt`,
|
|||
resource.TestCheckResourceAttrSet("envbuilder_cached_image.test", "image"),
|
||||
resource.TestCheckResourceAttrWith("envbuilder_cached_image.test", "image", quotedPrefix(deps.CacheRepo)),
|
||||
// Environment variables
|
||||
assertEnv(t, deps),
|
||||
tc.assertEnv(t, deps),
|
||||
),
|
||||
},
|
||||
// 5) Should produce an empty plan after apply
|
||||
|
@ -144,28 +294,31 @@ RUN date > /date.txt`,
|
|||
}
|
||||
}
|
||||
|
||||
// assertEnv is a test helper that checks the environment variables set on the
|
||||
// cached image resource based on the provided test dependencies.
|
||||
func assertEnv(t *testing.T, deps testDependencies) resource.TestCheckFunc {
|
||||
// assertEnv is a test helper that checks the environment variables, in order,
|
||||
// on both the env and env_map attributes of the cached image resource.
|
||||
func assertEnv(t *testing.T, kvs ...string) resource.TestCheckFunc {
|
||||
t.Helper()
|
||||
return resource.ComposeAggregateTestCheckFunc(
|
||||
// Check that the environment variables are set correctly.
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.0", fmt.Sprintf("ENVBUILDER_CACHE_REPO=%s", deps.CacheRepo)),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.1", fmt.Sprintf("ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH=%s", deps.Repo.Key)),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.2", fmt.Sprintf("ENVBUILDER_GIT_URL=%s", deps.Repo.URL)),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.3", "ENVBUILDER_REMOTE_REPO_BUILD_MODE=true"),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.4", "ENVBUILDER_VERBOSE=true"),
|
||||
// Check that the extra environment variables are set correctly.
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env.5", "FOO=bar\nbaz"),
|
||||
// We should not have any other environment variables set.
|
||||
resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", "env.6"),
|
||||
if len(kvs)%2 != 0 {
|
||||
t.Fatalf("assertEnv: expected an even number of key-value pairs, got %d", len(kvs))
|
||||
}
|
||||
|
||||
// Check that the same values are set in env_map.
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env_map.ENVBUILDER_CACHE_REPO", deps.CacheRepo),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env_map.ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", deps.Repo.Key),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env_map.ENVBUILDER_GIT_URL", deps.Repo.URL),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env_map.ENVBUILDER_REMOTE_REPO_BUILD_MODE", "true"),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env_map.ENVBUILDER_VERBOSE", "true"),
|
||||
resource.TestCheckResourceAttr("envbuilder_cached_image.test", "env_map.FOO", "bar\nbaz"),
|
||||
)
|
||||
funcs := make([]resource.TestCheckFunc, 0)
|
||||
for i := 0; i < len(kvs); i += 2 {
|
||||
resKey := fmt.Sprintf("env.%d", len(funcs))
|
||||
resVal := fmt.Sprintf("%s=%s", kvs[i], kvs[i+1])
|
||||
fn := resource.TestCheckResourceAttr("envbuilder_cached_image.test", resKey, resVal)
|
||||
funcs = append(funcs, fn)
|
||||
}
|
||||
|
||||
lastKey := fmt.Sprintf("env.%d", len(funcs))
|
||||
lastFn := resource.TestCheckNoResourceAttr("envbuilder_cached_image.test", lastKey)
|
||||
funcs = append(funcs, lastFn)
|
||||
|
||||
for i := 0; i < len(kvs); i += 2 {
|
||||
resKey := fmt.Sprintf("env_map.%s", kvs[i])
|
||||
fn := resource.TestCheckResourceAttr("envbuilder_cached_image.test", resKey, kvs[i+1])
|
||||
funcs = append(funcs, fn)
|
||||
}
|
||||
|
||||
return resource.ComposeAggregateTestCheckFunc(funcs...)
|
||||
}
|
||||
|
|
282
internal/provider/helpers.go
Normal file
282
internal/provider/helpers.go
Normal file
|
@ -0,0 +1,282 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
eboptions "github.com/coder/envbuilder/options"
|
||||
"github.com/coder/serpent"
|
||||
"github.com/coder/terraform-provider-envbuilder/internal/tfutil"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const (
|
||||
envbuilderOptionPrefix = "ENVBUILDER_"
|
||||
)
|
||||
|
||||
// nonOverrideOptions are options that cannot be overridden by extra_env.
|
||||
var nonOverrideOptions = map[string]bool{
|
||||
"ENVBUILDER_CACHE_REPO": true,
|
||||
"ENVBUILDER_GIT_URL": true,
|
||||
}
|
||||
|
||||
// optionsFromDataModel converts a CachedImageResourceModel into a corresponding set of
|
||||
// Envbuilder options. It returns the options and any diagnostics encountered.
|
||||
func optionsFromDataModel(data CachedImageResourceModel) (eboptions.Options, diag.Diagnostics) {
|
||||
var diags diag.Diagnostics
|
||||
var opts eboptions.Options
|
||||
|
||||
// Required options. Cannot be overridden by extra_env.
|
||||
opts.CacheRepo = data.CacheRepo.ValueString()
|
||||
opts.GitURL = data.GitURL.ValueString()
|
||||
|
||||
// Other options can be overridden by extra_env, with a warning.
|
||||
// Keep track of which options are set from the data model so we
|
||||
// can check if they are being overridden.
|
||||
providerOpts := make(map[string]bool)
|
||||
|
||||
if !data.BaseImageCacheDir.IsNull() {
|
||||
providerOpts["ENVBUILDER_BASE_IMAGE_CACHE_DIR"] = true
|
||||
opts.BaseImageCacheDir = data.BaseImageCacheDir.ValueString()
|
||||
}
|
||||
|
||||
if !data.BuildContextPath.IsNull() {
|
||||
providerOpts["ENVBUILDER_BUILD_CONTEXT_PATH"] = true
|
||||
opts.BuildContextPath = data.BuildContextPath.ValueString()
|
||||
}
|
||||
|
||||
if !data.BuildSecrets.IsNull() {
|
||||
providerOpts["ENVBUILDER_BUILD_SECRETS"] = true
|
||||
|
||||
// Depending on use case, users might want to provide build secrets as a map or a list of strings.
|
||||
// The string list option is supported by extra_env, so we support the map option here. Envbuilder
|
||||
// expects a list of strings, so we convert the map to a list of strings here.
|
||||
buildSecretMap := tfutil.TFMapToStringMap(data.BuildSecrets)
|
||||
buildSecretSlice := make([]string, 0, len(buildSecretMap))
|
||||
for k, v := range buildSecretMap {
|
||||
buildSecretSlice = append(buildSecretSlice, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
slices.Sort(buildSecretSlice)
|
||||
|
||||
opts.BuildSecrets = buildSecretSlice
|
||||
}
|
||||
|
||||
if !data.CacheTTLDays.IsNull() {
|
||||
providerOpts["ENVBUILDER_CACHE_TTL_DAYS"] = true
|
||||
opts.CacheTTLDays = data.CacheTTLDays.ValueInt64()
|
||||
}
|
||||
|
||||
if !data.DevcontainerDir.IsNull() {
|
||||
providerOpts["ENVBUILDER_DEVCONTAINER_DIR"] = true
|
||||
opts.DevcontainerDir = data.DevcontainerDir.ValueString()
|
||||
}
|
||||
|
||||
if !data.DevcontainerJSONPath.IsNull() {
|
||||
providerOpts["ENVBUILDER_DEVCONTAINER_JSON_PATH"] = true
|
||||
opts.DevcontainerJSONPath = data.DevcontainerJSONPath.ValueString()
|
||||
}
|
||||
|
||||
if !data.DockerfilePath.IsNull() {
|
||||
providerOpts["ENVBUILDER_DOCKERFILE_PATH"] = true
|
||||
opts.DockerfilePath = data.DockerfilePath.ValueString()
|
||||
}
|
||||
|
||||
if !data.DockerConfigBase64.IsNull() {
|
||||
providerOpts["ENVBUILDER_DOCKER_CONFIG_BASE64"] = true
|
||||
opts.DockerConfigBase64 = data.DockerConfigBase64.ValueString()
|
||||
}
|
||||
|
||||
if !data.ExitOnBuildFailure.IsNull() {
|
||||
providerOpts["ENVBUILDER_EXIT_ON_BUILD_FAILURE"] = true
|
||||
opts.ExitOnBuildFailure = data.ExitOnBuildFailure.ValueBool()
|
||||
}
|
||||
|
||||
if !data.FallbackImage.IsNull() {
|
||||
providerOpts["ENVBUILDER_FALLBACK_IMAGE"] = true
|
||||
opts.FallbackImage = data.FallbackImage.ValueString()
|
||||
}
|
||||
|
||||
if !data.GitCloneDepth.IsNull() {
|
||||
providerOpts["ENVBUILDER_GIT_CLONE_DEPTH"] = true
|
||||
opts.GitCloneDepth = data.GitCloneDepth.ValueInt64()
|
||||
}
|
||||
|
||||
if !data.GitCloneSingleBranch.IsNull() {
|
||||
providerOpts["ENVBUILDER_GIT_CLONE_SINGLE_BRANCH"] = true
|
||||
opts.GitCloneSingleBranch = data.GitCloneSingleBranch.ValueBool()
|
||||
}
|
||||
|
||||
if !data.GitHTTPProxyURL.IsNull() {
|
||||
providerOpts["ENVBUILDER_GIT_HTTP_PROXY_URL"] = true
|
||||
opts.GitHTTPProxyURL = data.GitHTTPProxyURL.ValueString()
|
||||
}
|
||||
|
||||
if !data.GitSSHPrivateKeyPath.IsNull() {
|
||||
providerOpts["ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH"] = true
|
||||
opts.GitSSHPrivateKeyPath = data.GitSSHPrivateKeyPath.ValueString()
|
||||
}
|
||||
|
||||
if !data.GitSSHPrivateKeyBase64.IsNull() {
|
||||
providerOpts["ENVBUILDER_GIT_SSH_PRIVATE_KEY_BASE64"] = true
|
||||
opts.GitSSHPrivateKeyBase64 = data.GitSSHPrivateKeyBase64.ValueString()
|
||||
}
|
||||
|
||||
if !data.GitUsername.IsNull() {
|
||||
providerOpts["ENVBUILDER_GIT_USERNAME"] = true
|
||||
opts.GitUsername = data.GitUsername.ValueString()
|
||||
}
|
||||
|
||||
if !data.GitPassword.IsNull() {
|
||||
providerOpts["ENVBUILDER_GIT_PASSWORD"] = true
|
||||
opts.GitPassword = data.GitPassword.ValueString()
|
||||
}
|
||||
|
||||
if !data.IgnorePaths.IsNull() {
|
||||
providerOpts["ENVBUILDER_IGNORE_PATHS"] = true
|
||||
opts.IgnorePaths = tfutil.TFListToStringSlice(data.IgnorePaths)
|
||||
}
|
||||
|
||||
if !data.Insecure.IsNull() {
|
||||
providerOpts["ENVBUILDER_INSECURE"] = true
|
||||
opts.Insecure = data.Insecure.ValueBool()
|
||||
}
|
||||
|
||||
if data.RemoteRepoBuildMode.IsNull() {
|
||||
opts.RemoteRepoBuildMode = true
|
||||
} else {
|
||||
providerOpts["ENVBUILDER_REMOTE_REPO_BUILD_MODE"] = true
|
||||
opts.RemoteRepoBuildMode = data.RemoteRepoBuildMode.ValueBool()
|
||||
}
|
||||
|
||||
if !data.SSLCertBase64.IsNull() {
|
||||
providerOpts["ENVBUILDER_SSL_CERT_BASE64"] = true
|
||||
opts.SSLCertBase64 = data.SSLCertBase64.ValueString()
|
||||
}
|
||||
|
||||
if !data.Verbose.IsNull() {
|
||||
providerOpts["ENVBUILDER_VERBOSE"] = true
|
||||
opts.Verbose = data.Verbose.ValueBool()
|
||||
}
|
||||
|
||||
if !data.WorkspaceFolder.IsNull() {
|
||||
providerOpts["ENVBUILDER_WORKSPACE_FOLDER"] = true
|
||||
opts.WorkspaceFolder = data.WorkspaceFolder.ValueString()
|
||||
}
|
||||
|
||||
// convert extraEnv to a map for ease of use.
|
||||
extraEnv := make(map[string]string)
|
||||
for k, v := range data.ExtraEnv.Elements() {
|
||||
extraEnv[k] = tfutil.TFValueToString(v)
|
||||
}
|
||||
diags = append(diags, overrideOptionsFromExtraEnv(&opts, extraEnv, providerOpts)...)
|
||||
|
||||
if opts.GitSSHPrivateKeyPath != "" && opts.GitSSHPrivateKeyBase64 != "" {
|
||||
diags.AddError("Cannot set more than one git ssh private key option",
|
||||
"Both ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH and ENVBUILDER_GIT_SSH_PRIVATE_KEY_BASE64 have been set.")
|
||||
}
|
||||
|
||||
return opts, diags
|
||||
}
|
||||
|
||||
// overrideOptionsFromExtraEnv overrides the options in opts with values from extraEnv.
|
||||
// It returns any diagnostics encountered.
|
||||
// It will not override certain options, such as ENVBUILDER_CACHE_REPO and ENVBUILDER_GIT_URL.
|
||||
func overrideOptionsFromExtraEnv(opts *eboptions.Options, extraEnv map[string]string, providerOpts map[string]bool) diag.Diagnostics {
|
||||
var diags diag.Diagnostics
|
||||
// Make a map of the options for easy lookup.
|
||||
optsMap := make(map[string]pflag.Value)
|
||||
for _, opt := range opts.CLI() {
|
||||
optsMap[opt.Env] = opt.Value
|
||||
}
|
||||
for key, val := range extraEnv {
|
||||
opt, found := optsMap[key]
|
||||
if !found {
|
||||
// ignore unknown keys
|
||||
continue
|
||||
}
|
||||
|
||||
if nonOverrideOptions[key] {
|
||||
diags.AddAttributeWarning(path.Root("extra_env"),
|
||||
"Cannot override required environment variable",
|
||||
fmt.Sprintf("The key %q in extra_env cannot be overridden.", key),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if the option was set on the provider data model and generate a warning if so.
|
||||
if providerOpts[key] {
|
||||
diags.AddAttributeWarning(path.Root("extra_env"),
|
||||
"Overriding provider environment variable",
|
||||
fmt.Sprintf("The key %q in extra_env overrides an option set on the provider.", key),
|
||||
)
|
||||
}
|
||||
|
||||
// XXX: workaround for serpent behaviour where calling Set() on a
|
||||
// string slice will append instead of replace: set to empty first.
|
||||
if _, ok := optsMap[key].(*serpent.StringArray); ok {
|
||||
_ = optsMap[key].Set("")
|
||||
}
|
||||
|
||||
if err := opt.Set(val); err != nil {
|
||||
diags.AddAttributeError(path.Root("extra_env"),
|
||||
"Invalid value for environment variable",
|
||||
fmt.Sprintf("The key %q in extra_env has an invalid value: %s", key, err),
|
||||
)
|
||||
}
|
||||
}
|
||||
return diags
|
||||
}
|
||||
|
||||
// computeEnvFromOptions computes the environment variables to set based on the
|
||||
// options in opts and the extra environment variables in extraEnv.
|
||||
// It returns the computed environment variables as a map.
|
||||
// It will not set certain options, such as ENVBUILDER_CACHE_REPO and ENVBUILDER_GIT_URL.
|
||||
// It will also not handle legacy Envbuilder options (i.e. those not prefixed with ENVBUILDER_).
|
||||
func computeEnvFromOptions(opts eboptions.Options, extraEnv map[string]string) map[string]string {
|
||||
for _, opt := range opts.CLI() {
|
||||
if opt.Env == "" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
computed := make(map[string]string)
|
||||
for _, opt := range opts.CLI() {
|
||||
if opt.Env == "" {
|
||||
continue
|
||||
}
|
||||
// TODO: remove this check once support for legacy options is removed.
|
||||
// Only set the environment variables from opts that are not legacy options.
|
||||
// Legacy options are those that are not prefixed with ENVBUILDER_.
|
||||
// While we can detect when a legacy option is set, overriding it becomes
|
||||
// problematic. Erring on the side of caution, we will not override legacy options.
|
||||
if !strings.HasPrefix(opt.Env, envbuilderOptionPrefix) {
|
||||
continue
|
||||
}
|
||||
var val string
|
||||
if sa, ok := opt.Value.(*serpent.StringArray); ok {
|
||||
val = strings.Join(sa.GetSlice(), ",")
|
||||
} else {
|
||||
val = opt.Value.String()
|
||||
}
|
||||
|
||||
switch val {
|
||||
case "", "false", "0":
|
||||
// Skip zero values.
|
||||
continue
|
||||
}
|
||||
computed[opt.Env] = val
|
||||
}
|
||||
|
||||
// Merge in extraEnv, which may override values from opts.
|
||||
// Skip any keys that are envbuilder options.
|
||||
for key, val := range extraEnv {
|
||||
if strings.HasPrefix(key, envbuilderOptionPrefix) {
|
||||
continue
|
||||
}
|
||||
computed[key] = val
|
||||
}
|
||||
return computed
|
||||
}
|
|
@ -36,6 +36,9 @@ func (p *EnvbuilderProvider) Metadata(ctx context.Context, req provider.Metadata
|
|||
func (p *EnvbuilderProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
Attributes: map[string]schema.Attribute{},
|
||||
MarkdownDescription: `
|
||||
The Envbuilder provider can be used to check for the presence of a container image previously built by [Envbuilder](https://github.com/coder/envbuilder).
|
||||
This allows re-using a previously built image pushed to a container registry without having to rebuild it.`,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
421
internal/provider/provider_internal_test.go
Normal file
421
internal/provider/provider_internal_test.go
Normal file
|
@ -0,0 +1,421 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
eboptions "github.com/coder/envbuilder/options"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_optionsFromDataModel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
data CachedImageResourceModel
|
||||
expectOpts eboptions.Options
|
||||
expectNumErrorDiags int
|
||||
expectNumWarningDiags int
|
||||
}{
|
||||
{
|
||||
name: "required only",
|
||||
data: CachedImageResourceModel{
|
||||
BuilderImage: basetypes.NewStringValue("envbuilder:latest"),
|
||||
CacheRepo: basetypes.NewStringValue("localhost:5000/cache"),
|
||||
GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"),
|
||||
},
|
||||
expectOpts: eboptions.Options{
|
||||
CacheRepo: "localhost:5000/cache",
|
||||
GitURL: "git@git.local/devcontainer.git",
|
||||
RemoteRepoBuildMode: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all options without extra_env",
|
||||
data: CachedImageResourceModel{
|
||||
BuilderImage: basetypes.NewStringValue("envbuilder:latest"),
|
||||
CacheRepo: basetypes.NewStringValue("localhost:5000/cache"),
|
||||
GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"),
|
||||
BaseImageCacheDir: basetypes.NewStringValue("/tmp/cache"),
|
||||
BuildContextPath: basetypes.NewStringValue("."),
|
||||
BuildSecrets: basetypes.NewMapValueMust(basetypes.StringType{}, map[string]attr.Value{
|
||||
"FOO": basetypes.NewStringValue("bar"),
|
||||
"BAZ": basetypes.NewStringValue("qux"),
|
||||
}),
|
||||
CacheTTLDays: basetypes.NewInt64Value(7),
|
||||
DevcontainerDir: basetypes.NewStringValue(".devcontainer"),
|
||||
DevcontainerJSONPath: basetypes.NewStringValue(".devcontainer/devcontainer.json"),
|
||||
DockerfilePath: basetypes.NewStringValue("Dockerfile"),
|
||||
DockerConfigBase64: basetypes.NewStringValue("some base64"),
|
||||
ExitOnBuildFailure: basetypes.NewBoolValue(true),
|
||||
// ExtraEnv: map[string]basetypes.Value{},
|
||||
FallbackImage: basetypes.NewStringValue("fallback"),
|
||||
GitCloneDepth: basetypes.NewInt64Value(1),
|
||||
GitCloneSingleBranch: basetypes.NewBoolValue(true),
|
||||
GitHTTPProxyURL: basetypes.NewStringValue("http://proxy"),
|
||||
GitPassword: basetypes.NewStringValue("password"),
|
||||
GitSSHPrivateKeyPath: basetypes.NewStringValue("/tmp/id_rsa"),
|
||||
GitUsername: basetypes.NewStringValue("user"),
|
||||
IgnorePaths: listValue("ignore", "paths"),
|
||||
Insecure: basetypes.NewBoolValue(true),
|
||||
RemoteRepoBuildMode: basetypes.NewBoolValue(false),
|
||||
SSLCertBase64: basetypes.NewStringValue("cert"),
|
||||
Verbose: basetypes.NewBoolValue(true),
|
||||
WorkspaceFolder: basetypes.NewStringValue("workspace"),
|
||||
},
|
||||
expectOpts: eboptions.Options{
|
||||
CacheRepo: "localhost:5000/cache",
|
||||
GitURL: "git@git.local/devcontainer.git",
|
||||
BaseImageCacheDir: "/tmp/cache",
|
||||
BuildContextPath: ".",
|
||||
BuildSecrets: []string{"BAZ=qux", "FOO=bar"}, // Sorted
|
||||
CacheTTLDays: 7,
|
||||
DevcontainerDir: ".devcontainer",
|
||||
DevcontainerJSONPath: ".devcontainer/devcontainer.json",
|
||||
DockerfilePath: "Dockerfile",
|
||||
DockerConfigBase64: "some base64",
|
||||
ExitOnBuildFailure: true,
|
||||
FallbackImage: "fallback",
|
||||
GitCloneDepth: 1,
|
||||
GitCloneSingleBranch: true,
|
||||
GitHTTPProxyURL: "http://proxy",
|
||||
GitPassword: "password",
|
||||
GitSSHPrivateKeyPath: "/tmp/id_rsa",
|
||||
GitUsername: "user",
|
||||
IgnorePaths: []string{"ignore", "paths"},
|
||||
Insecure: true,
|
||||
RemoteRepoBuildMode: false,
|
||||
SSLCertBase64: "cert",
|
||||
Verbose: true,
|
||||
WorkspaceFolder: "workspace",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "extra env override",
|
||||
data: CachedImageResourceModel{
|
||||
BuilderImage: basetypes.NewStringValue("envbuilder:latest"),
|
||||
CacheRepo: basetypes.NewStringValue("localhost:5000/cache"),
|
||||
GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"),
|
||||
ExtraEnv: extraEnvMap(t,
|
||||
"CODER_AGENT_TOKEN", "token",
|
||||
"CODER_AGENT_URL", "http://coder",
|
||||
"FOO", "bar",
|
||||
"ENVBUILDER_BUILD_SECRETS", "FOO=bar,BAZ=qux",
|
||||
),
|
||||
},
|
||||
expectOpts: eboptions.Options{
|
||||
BuildSecrets: []string{"FOO=bar", "BAZ=qux"},
|
||||
CacheRepo: "localhost:5000/cache",
|
||||
GitURL: "git@git.local/devcontainer.git",
|
||||
RemoteRepoBuildMode: true,
|
||||
CoderAgentToken: "token",
|
||||
CoderAgentURL: "http://coder",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "extra_env override warnings",
|
||||
data: CachedImageResourceModel{
|
||||
BuilderImage: basetypes.NewStringValue("envbuilder:latest"),
|
||||
CacheRepo: basetypes.NewStringValue("localhost:5000/cache"),
|
||||
GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"),
|
||||
BaseImageCacheDir: basetypes.NewStringValue("/tmp/cache"),
|
||||
BuildContextPath: basetypes.NewStringValue("."),
|
||||
BuildSecrets: basetypes.NewMapValueMust(basetypes.StringType{}, map[string]attr.Value{
|
||||
"FOO": basetypes.NewStringValue("bar"),
|
||||
}),
|
||||
CacheTTLDays: basetypes.NewInt64Value(7),
|
||||
DevcontainerDir: basetypes.NewStringValue(".devcontainer"),
|
||||
DevcontainerJSONPath: basetypes.NewStringValue(".devcontainer/devcontainer.json"),
|
||||
DockerfilePath: basetypes.NewStringValue("Dockerfile"),
|
||||
DockerConfigBase64: basetypes.NewStringValue("some base64"),
|
||||
ExitOnBuildFailure: basetypes.NewBoolValue(true),
|
||||
// ExtraEnv: map[string]basetypes.Value{},
|
||||
FallbackImage: basetypes.NewStringValue("fallback"),
|
||||
GitCloneDepth: basetypes.NewInt64Value(1),
|
||||
GitCloneSingleBranch: basetypes.NewBoolValue(true),
|
||||
GitHTTPProxyURL: basetypes.NewStringValue("http://proxy"),
|
||||
GitPassword: basetypes.NewStringValue("password"),
|
||||
GitSSHPrivateKeyPath: basetypes.NewStringValue("/tmp/id_rsa"),
|
||||
GitUsername: basetypes.NewStringValue("user"),
|
||||
IgnorePaths: listValue("ignore", "paths"),
|
||||
Insecure: basetypes.NewBoolValue(true),
|
||||
RemoteRepoBuildMode: basetypes.NewBoolValue(false),
|
||||
SSLCertBase64: basetypes.NewStringValue("cert"),
|
||||
Verbose: basetypes.NewBoolValue(true),
|
||||
WorkspaceFolder: basetypes.NewStringValue("workspace"),
|
||||
ExtraEnv: extraEnvMap(t,
|
||||
"ENVBUILDER_BUILD_SECRETS", "FOO=bar,BAZ=qux",
|
||||
"ENVBUILDER_CACHE_REPO", "override",
|
||||
"ENVBUILDER_GIT_URL", "override",
|
||||
"ENVBUILDER_BASE_IMAGE_CACHE_DIR", "override",
|
||||
"ENVBUILDER_BUILD_CONTEXT_PATH", "override",
|
||||
"ENVBUILDER_CACHE_TTL_DAYS", "8",
|
||||
"ENVBUILDER_DEVCONTAINER_DIR", "override",
|
||||
"ENVBUILDER_DEVCONTAINER_JSON_PATH", "override",
|
||||
"ENVBUILDER_DOCKERFILE_PATH", "override",
|
||||
"ENVBUILDER_DOCKER_CONFIG_BASE64", "override",
|
||||
"ENVBUILDER_EXIT_ON_BUILD_FAILURE", "false",
|
||||
"ENVBUILDER_FALLBACK_IMAGE", "override",
|
||||
"ENVBUILDER_GIT_CLONE_DEPTH", "2",
|
||||
"ENVBUILDER_GIT_CLONE_SINGLE_BRANCH", "false",
|
||||
"ENVBUILDER_GIT_HTTP_PROXY_URL", "override",
|
||||
"ENVBUILDER_GIT_PASSWORD", "override",
|
||||
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", "override",
|
||||
"ENVBUILDER_GIT_USERNAME", "override",
|
||||
"ENVBUILDER_IGNORE_PATHS", "override",
|
||||
"ENVBUILDER_INSECURE", "false",
|
||||
"ENVBUILDER_REMOTE_REPO_BUILD_MODE", "true",
|
||||
"ENVBUILDER_SSL_CERT_BASE64", "override",
|
||||
"ENVBUILDER_VERBOSE", "false",
|
||||
"ENVBUILDER_WORKSPACE_FOLDER", "override",
|
||||
"FOO", "bar",
|
||||
),
|
||||
},
|
||||
expectOpts: eboptions.Options{
|
||||
// not overridden
|
||||
CacheRepo: "localhost:5000/cache",
|
||||
GitURL: "git@git.local/devcontainer.git",
|
||||
// overridden
|
||||
BaseImageCacheDir: "override",
|
||||
BuildContextPath: "override",
|
||||
BuildSecrets: []string{"FOO=bar", "BAZ=qux"},
|
||||
CacheTTLDays: 8,
|
||||
DevcontainerDir: "override",
|
||||
DevcontainerJSONPath: "override",
|
||||
DockerfilePath: "override",
|
||||
DockerConfigBase64: "override",
|
||||
ExitOnBuildFailure: false,
|
||||
FallbackImage: "override",
|
||||
GitCloneDepth: 2,
|
||||
GitCloneSingleBranch: false,
|
||||
GitHTTPProxyURL: "override",
|
||||
GitPassword: "override",
|
||||
GitSSHPrivateKeyPath: "override",
|
||||
GitUsername: "override",
|
||||
IgnorePaths: []string{"override"},
|
||||
Insecure: false,
|
||||
RemoteRepoBuildMode: true,
|
||||
SSLCertBase64: "override",
|
||||
Verbose: false,
|
||||
WorkspaceFolder: "override",
|
||||
},
|
||||
expectNumWarningDiags: 24,
|
||||
},
|
||||
{
|
||||
name: "extra_env override errors",
|
||||
data: CachedImageResourceModel{
|
||||
BuilderImage: basetypes.NewStringValue("envbuilder:latest"),
|
||||
CacheRepo: basetypes.NewStringValue("localhost:5000/cache"),
|
||||
GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"),
|
||||
ExtraEnv: extraEnvMap(t,
|
||||
"ENVBUILDER_CACHE_TTL_DAYS", "not a number",
|
||||
"ENVBUILDER_VERBOSE", "not a bool",
|
||||
"FOO", "bar",
|
||||
),
|
||||
},
|
||||
expectOpts: eboptions.Options{
|
||||
// not overridden
|
||||
CacheRepo: "localhost:5000/cache",
|
||||
GitURL: "git@git.local/devcontainer.git",
|
||||
RemoteRepoBuildMode: true,
|
||||
},
|
||||
expectNumErrorDiags: 2,
|
||||
},
|
||||
{
|
||||
name: "errors when git ssh private key path and base64 are set",
|
||||
data: CachedImageResourceModel{
|
||||
BuilderImage: basetypes.NewStringValue("envbuilder:latest"),
|
||||
CacheRepo: basetypes.NewStringValue("localhost:5000/cache"),
|
||||
GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"),
|
||||
GitSSHPrivateKeyPath: basetypes.NewStringValue("/tmp/id_rsa"),
|
||||
GitSSHPrivateKeyBase64: basetypes.NewStringValue("cHJpdmF0ZUtleQo="),
|
||||
},
|
||||
expectOpts: eboptions.Options{
|
||||
CacheRepo: "localhost:5000/cache",
|
||||
GitURL: "git@git.local/devcontainer.git",
|
||||
RemoteRepoBuildMode: true,
|
||||
GitSSHPrivateKeyPath: "/tmp/id_rsa",
|
||||
GitSSHPrivateKeyBase64: "cHJpdmF0ZUtleQo=",
|
||||
},
|
||||
expectNumErrorDiags: 1,
|
||||
},
|
||||
{
|
||||
name: "extra_env override errors when git ssh private key path and base64 are set",
|
||||
data: CachedImageResourceModel{
|
||||
BuilderImage: basetypes.NewStringValue("envbuilder:latest"),
|
||||
CacheRepo: basetypes.NewStringValue("localhost:5000/cache"),
|
||||
GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"),
|
||||
GitSSHPrivateKeyBase64: basetypes.NewStringValue("cHJpdmF0ZUtleQo="),
|
||||
ExtraEnv: extraEnvMap(t,
|
||||
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH", "/tmp/id_rsa",
|
||||
),
|
||||
},
|
||||
expectOpts: eboptions.Options{
|
||||
CacheRepo: "localhost:5000/cache",
|
||||
GitURL: "git@git.local/devcontainer.git",
|
||||
RemoteRepoBuildMode: true,
|
||||
GitSSHPrivateKeyPath: "/tmp/id_rsa",
|
||||
GitSSHPrivateKeyBase64: "cHJpdmF0ZUtleQo=",
|
||||
},
|
||||
expectNumErrorDiags: 1,
|
||||
},
|
||||
{
|
||||
name: "required only with base64 ssh key",
|
||||
data: CachedImageResourceModel{
|
||||
BuilderImage: basetypes.NewStringValue("envbuilder:latest"),
|
||||
CacheRepo: basetypes.NewStringValue("localhost:5000/cache"),
|
||||
GitURL: basetypes.NewStringValue("git@git.local/devcontainer.git"),
|
||||
GitSSHPrivateKeyBase64: basetypes.NewStringValue("cHJpdmF0ZUtleQo="),
|
||||
},
|
||||
expectOpts: eboptions.Options{
|
||||
CacheRepo: "localhost:5000/cache",
|
||||
GitURL: "git@git.local/devcontainer.git",
|
||||
RemoteRepoBuildMode: true,
|
||||
GitSSHPrivateKeyBase64: "cHJpdmF0ZUtleQo=",
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
actual, diags := optionsFromDataModel(tc.data)
|
||||
assert.Equal(t, tc.expectNumErrorDiags, diags.ErrorsCount())
|
||||
assert.Equal(t, tc.expectNumWarningDiags, diags.WarningsCount())
|
||||
assert.EqualValues(t, tc.expectOpts, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_computeEnvFromOptions(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
opts eboptions.Options
|
||||
extraEnv map[string]string
|
||||
expectEnv map[string]string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
opts: eboptions.Options{},
|
||||
expectEnv: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "all options",
|
||||
opts: eboptions.Options{
|
||||
BaseImageCacheDir: "string",
|
||||
BinaryPath: "string",
|
||||
BuildContextPath: "string",
|
||||
BuildSecrets: []string{"FOO=bar", "BAZ=qux"},
|
||||
CacheRepo: "string",
|
||||
CacheTTLDays: 1,
|
||||
CoderAgentSubsystem: []string{"one", "two"},
|
||||
CoderAgentToken: "string",
|
||||
CoderAgentURL: "string",
|
||||
DevcontainerDir: "string",
|
||||
DevcontainerJSONPath: "string",
|
||||
DockerConfigBase64: "string",
|
||||
DockerfilePath: "string",
|
||||
ExitOnBuildFailure: true,
|
||||
ExportEnvFile: "string",
|
||||
FallbackImage: "string",
|
||||
ForceSafe: true,
|
||||
GetCachedImage: true,
|
||||
GitCloneDepth: 1,
|
||||
GitCloneSingleBranch: true,
|
||||
GitHTTPProxyURL: "string",
|
||||
GitPassword: "string",
|
||||
GitSSHPrivateKeyPath: "string",
|
||||
GitURL: "string",
|
||||
GitUsername: "string",
|
||||
IgnorePaths: []string{"one", "two"},
|
||||
InitArgs: "string",
|
||||
InitCommand: "string",
|
||||
InitScript: "string",
|
||||
Insecure: true,
|
||||
LayerCacheDir: "string",
|
||||
PostStartScriptPath: "string",
|
||||
PushImage: true,
|
||||
RemoteRepoBuildMode: true,
|
||||
SetupScript: "string",
|
||||
SkipRebuild: true,
|
||||
SSLCertBase64: "string",
|
||||
Verbose: true,
|
||||
WorkspaceFolder: "string",
|
||||
},
|
||||
extraEnv: map[string]string{
|
||||
"ENVBUILDER_SOMETHING": "string", // should be ignored
|
||||
"FOO": "bar", // should be included
|
||||
},
|
||||
expectEnv: map[string]string{
|
||||
"ENVBUILDER_BASE_IMAGE_CACHE_DIR": "string",
|
||||
"ENVBUILDER_BINARY_PATH": "string",
|
||||
"ENVBUILDER_BUILD_CONTEXT_PATH": "string",
|
||||
"ENVBUILDER_BUILD_SECRETS": "FOO=bar,BAZ=qux",
|
||||
"ENVBUILDER_CACHE_REPO": "string",
|
||||
"ENVBUILDER_CACHE_TTL_DAYS": "1",
|
||||
"ENVBUILDER_DEVCONTAINER_DIR": "string",
|
||||
"ENVBUILDER_DEVCONTAINER_JSON_PATH": "string",
|
||||
"ENVBUILDER_DOCKER_CONFIG_BASE64": "string",
|
||||
"ENVBUILDER_DOCKERFILE_PATH": "string",
|
||||
"ENVBUILDER_EXIT_ON_BUILD_FAILURE": "true",
|
||||
"ENVBUILDER_EXPORT_ENV_FILE": "string",
|
||||
"ENVBUILDER_FALLBACK_IMAGE": "string",
|
||||
"ENVBUILDER_FORCE_SAFE": "true",
|
||||
"ENVBUILDER_GET_CACHED_IMAGE": "true",
|
||||
"ENVBUILDER_GIT_CLONE_DEPTH": "1",
|
||||
"ENVBUILDER_GIT_CLONE_SINGLE_BRANCH": "true",
|
||||
"ENVBUILDER_GIT_HTTP_PROXY_URL": "string",
|
||||
"ENVBUILDER_GIT_PASSWORD": "string",
|
||||
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH": "string",
|
||||
"ENVBUILDER_GIT_URL": "string",
|
||||
"ENVBUILDER_GIT_USERNAME": "string",
|
||||
"ENVBUILDER_IGNORE_PATHS": "one,two",
|
||||
"ENVBUILDER_INIT_ARGS": "string",
|
||||
"ENVBUILDER_INIT_COMMAND": "string",
|
||||
"ENVBUILDER_INIT_SCRIPT": "string",
|
||||
"ENVBUILDER_INSECURE": "true",
|
||||
"ENVBUILDER_LAYER_CACHE_DIR": "string",
|
||||
"ENVBUILDER_POST_START_SCRIPT_PATH": "string",
|
||||
"ENVBUILDER_PUSH_IMAGE": "true",
|
||||
"ENVBUILDER_REMOTE_REPO_BUILD_MODE": "true",
|
||||
"ENVBUILDER_SETUP_SCRIPT": "string",
|
||||
"ENVBUILDER_SKIP_REBUILD": "true",
|
||||
"ENVBUILDER_SSL_CERT_BASE64": "string",
|
||||
"ENVBUILDER_VERBOSE": "true",
|
||||
"ENVBUILDER_WORKSPACE_FOLDER": "string",
|
||||
"FOO": "bar",
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if tc.extraEnv == nil {
|
||||
tc.extraEnv = map[string]string{}
|
||||
}
|
||||
actual := computeEnvFromOptions(tc.opts, tc.extraEnv)
|
||||
assert.EqualValues(t, tc.expectEnv, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func listValue(vs ...string) basetypes.ListValue {
|
||||
vals := make([]attr.Value, len(vs))
|
||||
for i, s := range vs {
|
||||
vals[i] = basetypes.NewStringValue(s)
|
||||
}
|
||||
return basetypes.NewListValueMust(basetypes.StringType{}, vals)
|
||||
}
|
||||
|
||||
func extraEnvMap(t *testing.T, kvs ...string) basetypes.MapValue {
|
||||
t.Helper()
|
||||
if len(kvs)%2 != 0 {
|
||||
t.Fatalf("extraEnvMap: expected even number of key-value pairs, got %d", len(kvs))
|
||||
}
|
||||
vals := make(map[string]attr.Value)
|
||||
for i := 0; i < len(kvs); i += 2 {
|
||||
vals[kvs[i]] = basetypes.NewStringValue(kvs[i+1])
|
||||
}
|
||||
return basetypes.NewMapValueMust(basetypes.StringType{}, vals)
|
||||
}
|
|
@ -3,6 +3,7 @@ package provider
|
|||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -35,10 +36,11 @@ var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServe
|
|||
|
||||
// testDependencies contain information about stuff the test depends on.
|
||||
type testDependencies struct {
|
||||
BuilderImage string
|
||||
CacheRepo string
|
||||
ExtraEnv map[string]string
|
||||
Repo testGitRepoSSH
|
||||
BuilderImage string
|
||||
CacheRepo string
|
||||
DockerConfigBase64 string
|
||||
ExtraEnv map[string]string
|
||||
Repo testGitRepoSSH
|
||||
}
|
||||
|
||||
// Config generates a valid Terraform config file from the dependencies.
|
||||
|
@ -47,16 +49,17 @@ func (d *testDependencies) Config(t testing.TB) string {
|
|||
|
||||
tpl := `provider envbuilder {}
|
||||
resource "envbuilder_cached_image" "test" {
|
||||
builder_image = {{ quote .BuilderImage }}
|
||||
builder_image = {{ quote .BuilderImage }}
|
||||
cache_repo = {{ quote .CacheRepo }}
|
||||
docker_config_base64 = {{ quote .DockerConfigBase64 }}
|
||||
git_url = {{ quote .Repo.URL }}
|
||||
extra_env = {
|
||||
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH": {{ quote .Repo.Key }}
|
||||
"ENVBUILDER_VERBOSE": true
|
||||
{{ range $k, $v := .ExtraEnv }}
|
||||
{{ quote $k }}: {{ quote $v }}
|
||||
{{ end }}
|
||||
}
|
||||
git_url = {{ quote .Repo.URL }}
|
||||
git_ssh_private_key_path = {{ quote .Repo.Key }}
|
||||
verbose = true
|
||||
}`
|
||||
|
||||
fm := template.FuncMap{"quote": quote}
|
||||
|
@ -71,26 +74,36 @@ func quote(s string) string {
|
|||
return fmt.Sprintf("%q", s)
|
||||
}
|
||||
|
||||
func setup(ctx context.Context, t testing.TB, files map[string]string) testDependencies {
|
||||
func setup(ctx context.Context, t testing.TB, extraEnv, files map[string]string) testDependencies {
|
||||
t.Helper()
|
||||
|
||||
envbuilderImage := getEnvOrDefault("ENVBUILDER_IMAGE", "localhost:5000/envbuilder")
|
||||
envbuilderVersion := getEnvOrDefault("ENVBUILDER_VERSION", "latest")
|
||||
envbuilderImageRef := envbuilderImage + ":" + envbuilderVersion
|
||||
|
||||
// TODO: envbuilder creates /.envbuilder/bin/envbuilder owned by root:root which we are unable to clean up.
|
||||
// This causes tests to fail.
|
||||
testUsername := "testuser"
|
||||
testPassword := "testpassword"
|
||||
testAuthBase64 := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", testUsername, testPassword)))
|
||||
regDir := t.TempDir()
|
||||
reg := registrytest.New(t, regDir)
|
||||
reg := registrytest.New(t, regDir, registrytest.BasicAuthMW(t, testUsername, testPassword))
|
||||
|
||||
repoDir := setupGitRepo(t, files)
|
||||
gitRepo := serveGitRepoSSH(ctx, t, repoDir)
|
||||
dockerConfigJSON := fmt.Sprintf(`{
|
||||
"auths": {
|
||||
"%s": {
|
||||
"auth": "%s",
|
||||
}
|
||||
}
|
||||
}`, reg, testAuthBase64)
|
||||
dockerConfigJSONBase64 := base64.StdEncoding.EncodeToString([]byte(dockerConfigJSON))
|
||||
|
||||
return testDependencies{
|
||||
BuilderImage: envbuilderImageRef,
|
||||
CacheRepo: reg + "/test",
|
||||
ExtraEnv: make(map[string]string),
|
||||
Repo: gitRepo,
|
||||
BuilderImage: envbuilderImageRef,
|
||||
CacheRepo: reg + "/test",
|
||||
ExtraEnv: extraEnv,
|
||||
Repo: gitRepo,
|
||||
DockerConfigBase64: dockerConfigJSONBase64,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,18 +119,39 @@ func seedCache(ctx context.Context, t testing.TB, deps testDependencies) {
|
|||
|
||||
ensureImage(ctx, t, cli, deps.BuilderImage)
|
||||
|
||||
// Set up env for envbuilder
|
||||
seedEnv := map[string]string{
|
||||
"ENVBUILDER_CACHE_REPO": deps.CacheRepo,
|
||||
"ENVBUILDER_EXIT_ON_BUILD_FAILURE": "true",
|
||||
"ENVBUILDER_INIT_SCRIPT": "exit",
|
||||
"ENVBUILDER_PUSH_IMAGE": "true",
|
||||
"ENVBUILDER_VERBOSE": "true",
|
||||
"ENVBUILDER_GIT_URL": deps.Repo.URL,
|
||||
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH": "/id_ed25519",
|
||||
"ENVBUILDER_DOCKER_CONFIG_BASE64": deps.DockerConfigBase64,
|
||||
}
|
||||
|
||||
for k, v := range deps.ExtraEnv {
|
||||
if !strings.HasPrefix(k, envbuilderOptionPrefix) {
|
||||
continue
|
||||
}
|
||||
if _, ok := seedEnv[k]; ok {
|
||||
continue
|
||||
}
|
||||
seedEnv[k] = v
|
||||
}
|
||||
|
||||
seedDockerEnv := make([]string, 0)
|
||||
for k, v := range seedEnv {
|
||||
seedDockerEnv = append(seedDockerEnv, k+"="+v)
|
||||
}
|
||||
|
||||
t.Logf("running envbuilder to seed cache with args: %v", seedDockerEnv)
|
||||
|
||||
// Run envbuilder using this dir as a local layer cache
|
||||
ctr, err := cli.ContainerCreate(ctx, &container.Config{
|
||||
Image: deps.BuilderImage,
|
||||
Env: []string{
|
||||
"ENVBUILDER_CACHE_REPO=" + deps.CacheRepo,
|
||||
"ENVBUILDER_EXIT_ON_BUILD_FAILURE=true",
|
||||
"ENVBUILDER_INIT_SCRIPT=exit",
|
||||
"ENVBUILDER_PUSH_IMAGE=true",
|
||||
"ENVBUILDER_VERBOSE=true",
|
||||
"ENVBUILDER_GIT_URL=" + deps.Repo.URL,
|
||||
"ENVBUILDER_GIT_SSH_PRIVATE_KEY_PATH=/id_ed25519",
|
||||
},
|
||||
Env: seedDockerEnv,
|
||||
Labels: map[string]string{
|
||||
testContainerLabel: "true",
|
||||
},
|
||||
|
@ -160,7 +194,7 @@ SCANLOGS:
|
|||
}
|
||||
log := scanner.Text()
|
||||
t.Logf("envbuilder: %s", log)
|
||||
if strings.Contains(log, "=== Running the init command") {
|
||||
if strings.Contains(log, "=== Running init command") {
|
||||
break SCANLOGS
|
||||
}
|
||||
}
|
||||
|
|
92
internal/tfutil/tfutil.go
Normal file
92
internal/tfutil/tfutil.go
Normal file
|
@ -0,0 +1,92 @@
|
|||
package tfutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/coder/envbuilder/log"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
)
|
||||
|
||||
// TFValueToString converts an attr.Value to its string representation
|
||||
// based on its Terraform type. This is needed because the String()
|
||||
// method on an attr.Value creates a 'human-readable' version of the type, which
|
||||
// leads to quotes, escaped characters, and other assorted sadness.
|
||||
func TFValueToString(val attr.Value) string {
|
||||
if val.IsUnknown() || val.IsNull() {
|
||||
return ""
|
||||
}
|
||||
if vs, ok := val.(interface{ ValueString() string }); ok {
|
||||
return vs.ValueString()
|
||||
}
|
||||
if vb, ok := val.(interface{ ValueBool() bool }); ok {
|
||||
return fmt.Sprintf("%t", vb.ValueBool())
|
||||
}
|
||||
if vi, ok := val.(interface{ ValueInt64() int64 }); ok {
|
||||
return fmt.Sprintf("%d", vi.ValueInt64())
|
||||
}
|
||||
panic(fmt.Errorf("tfValueToString: value %T is not a supported type", val))
|
||||
}
|
||||
|
||||
// TFListToStringSlice converts a types.List to a []string by calling
|
||||
// tfValueToString on each element.
|
||||
func TFListToStringSlice(l types.List) []string {
|
||||
els := l.Elements()
|
||||
ss := make([]string, len(els))
|
||||
for idx, el := range els {
|
||||
ss[idx] = TFValueToString(el)
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
// TFMapToStringMap converts a types.Map to a map[string]string by calling
|
||||
// tfValueToString on each element.
|
||||
func TFMapToStringMap(m types.Map) map[string]string {
|
||||
els := m.Elements()
|
||||
res := make(map[string]string, len(els))
|
||||
for k, v := range els {
|
||||
res[k] = TFValueToString(v)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// TFLogFunc is an adapter to envbuilder/log.Func.
|
||||
func TFLogFunc(ctx context.Context) log.Func {
|
||||
return func(level log.Level, format string, args ...any) {
|
||||
var logFn func(context.Context, string, ...map[string]interface{})
|
||||
switch level {
|
||||
case log.LevelTrace:
|
||||
logFn = tflog.Trace
|
||||
case log.LevelDebug:
|
||||
logFn = tflog.Debug
|
||||
case log.LevelWarn:
|
||||
logFn = tflog.Warn
|
||||
case log.LevelError:
|
||||
logFn = tflog.Error
|
||||
default:
|
||||
logFn = tflog.Info
|
||||
}
|
||||
logFn(ctx, fmt.Sprintf(format, args...))
|
||||
}
|
||||
}
|
||||
|
||||
// DockerEnv returns the keys and values of the map in the form "key=value"
|
||||
// sorted by key in lexicographical order. This is the format expected by
|
||||
// Docker and some other tools that consume environment variables.
|
||||
func DockerEnv(m map[string]string) []string {
|
||||
pairs := make([]string, 0, len(m))
|
||||
var sb strings.Builder
|
||||
for k := range m {
|
||||
_, _ = sb.WriteString(k)
|
||||
_, _ = sb.WriteRune('=')
|
||||
_, _ = sb.WriteString(m[k])
|
||||
pairs = append(pairs, sb.String())
|
||||
sb.Reset()
|
||||
}
|
||||
sort.Strings(pairs)
|
||||
return pairs
|
||||
}
|
|
@ -2,6 +2,7 @@ package registrytest
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
@ -13,12 +14,31 @@ import (
|
|||
// New starts a new Docker registry listening on localhost.
|
||||
// It will automatically shut down when the test finishes.
|
||||
// It will store data in dir.
|
||||
func New(t testing.TB, dir string) string {
|
||||
func New(t testing.TB, dir string, mws ...func(http.Handler) http.Handler) string {
|
||||
t.Helper()
|
||||
regHandler := registry.New(registry.WithBlobHandler(registry.NewDiskBlobHandler(dir)))
|
||||
for _, mw := range mws {
|
||||
regHandler = mw(regHandler)
|
||||
}
|
||||
regSrv := httptest.NewServer(regHandler)
|
||||
t.Cleanup(func() { regSrv.Close() })
|
||||
regSrvURL, err := url.Parse(regSrv.URL)
|
||||
require.NoError(t, err)
|
||||
return fmt.Sprintf("localhost:%s", regSrvURL.Port())
|
||||
}
|
||||
|
||||
func BasicAuthMW(t testing.TB, username, password string) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if username != "" || password != "" {
|
||||
authUser, authPass, ok := r.BasicAuth()
|
||||
if !ok || username != authUser || password != authPass {
|
||||
t.Logf("basic auth failed: got user %q, pass %q", authUser, authPass)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue