mirror of
https://github.com/coder/terraform-provider-envbuilder.git
synced 2025-06-28 07:36:42 +00:00
implement first pass at cached image data source (#3)
implements envbuilder_cached_image data source
This commit is contained in:
parent
1a49822fc9
commit
c9e7cb8178
10 changed files with 1698 additions and 147 deletions
.github/workflows
.gitignoreGNUmakefiledocs/data-sources
go.modgo.suminternal/provider
testutil/registrytest
12
.github/workflows/test.yml
vendored
12
.github/workflows/test.yml
vendored
|
@ -65,11 +65,12 @@ jobs:
|
|||
matrix:
|
||||
# list whatever Terraform versions here you would like to support
|
||||
terraform:
|
||||
- '1.0.*'
|
||||
- '1.1.*'
|
||||
- '1.2.*'
|
||||
- '1.3.*'
|
||||
- '1.4.*'
|
||||
# TODO: test with more terraform versions. For now, testing with latest.
|
||||
# - "1.5.*"
|
||||
# - "1.6.*"
|
||||
# - "1.7.*"
|
||||
# - "1.8.*"
|
||||
- "1.9.*"
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
|
@ -81,6 +82,7 @@ jobs:
|
|||
terraform_version: ${{ matrix.terraform }}
|
||||
terraform_wrapper: false
|
||||
- run: go mod download
|
||||
- run: make test-registry
|
||||
- env:
|
||||
TF_ACC: "1"
|
||||
run: go test -v -cover ./internal/provider/
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -34,4 +34,5 @@ website/vendor
|
|||
# Keep windows files with windows line endings
|
||||
*.winfile eol=crlf
|
||||
|
||||
terraform-provider-envbuilder
|
||||
terraform-provider-envbuilder
|
||||
.registry-cache/
|
||||
|
|
31
GNUmakefile
31
GNUmakefile
|
@ -17,3 +17,34 @@ build: terraform-provider-envbuilder
|
|||
|
||||
terraform-provider-envbuilder: internal/provider/*.go main.go
|
||||
CGO_ENABLED=0 go build .
|
||||
|
||||
.PHONY: update-envbuilder-version
|
||||
update-envbuilder-version:
|
||||
go get github.com/coder/envbuilder@main
|
||||
go mod tidy
|
||||
|
||||
# Starts a local Docker registry on port 5000 with a local disk cache.
|
||||
.PHONY: test-registry
|
||||
test-registry: test-registry-container test-images-pull test-images-push
|
||||
|
||||
.PHONY: test-registry-container
|
||||
test-registry-container: .registry-cache
|
||||
if ! curl -fsSL http://localhost:5000/v2/_catalog > /dev/null 2>&1; then \
|
||||
docker rm -f tfprov-envbuilder-registry && \
|
||||
docker run -d -p 5000:5000 --name envbuilder-registry --volume $(PWD)/.registry-cache:/var/lib/registry registry:2; \
|
||||
fi
|
||||
|
||||
# Pulls images referenced in integration tests and pushes them to the local cache.
|
||||
.PHONY: test-images-push
|
||||
test-images-push: .registry-cache/docker/registry/v2/repositories/test-ubuntu
|
||||
|
||||
.PHONY: test-images-pull
|
||||
test-images-pull:
|
||||
docker pull ubuntu:latest
|
||||
docker tag ubuntu:latest localhost:5000/test-ubuntu:latest
|
||||
|
||||
.registry-cache:
|
||||
mkdir -p .registry-cache && chmod -R ag+w .registry-cache
|
||||
|
||||
.registry-cache/docker/registry/v2/repositories/test-ubuntu:
|
||||
docker push localhost:5000/test-ubuntu:latest
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
page_title: "envbuilder_cached_image Data Source - envbuilder"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
The cached image data source can be used to retrieve a cached image produced by envbuilder.
|
||||
The cached image data source can be used to retrieve a cached image produced by envbuilder. Reading from this data source will clone the specified Git repository, read a Devcontainer specification or Dockerfile, and check for its presence in the provided cache repo.
|
||||
---
|
||||
|
||||
# envbuilder_cached_image (Data Source)
|
||||
|
||||
The cached image data source can be used to retrieve a cached image produced by envbuilder.
|
||||
The cached image data source can be used to retrieve a cached image produced by envbuilder. Reading from this data source will clone the specified Git repository, read a Devcontainer specification or Dockerfile, and check for its presence in the provided cache repo.
|
||||
|
||||
## Example Usage
|
||||
|
||||
|
@ -33,20 +33,36 @@ resource "docker_container" "container" {
|
|||
|
||||
### Required
|
||||
|
||||
- `builder_image` (String) The builder image URL to use if the cache does not exist.
|
||||
- `cache_repo` (String) The name of the container registry to fetch the cache image from.
|
||||
- `git_url` (String) The URL of a Git repository containing a Devcontainer or Docker image to clone.
|
||||
- `builder_image` (String) The envbuilder image to use if the cached version is not found.
|
||||
- `cache_repo` (String) (Envbuilder option) The name of the container registry to fetch the cache image from.
|
||||
- `git_url` (String) (Envbuilder option) The URL of a Git repository containing a Devcontainer or Docker image to clone.
|
||||
|
||||
### Optional
|
||||
|
||||
- `cache_ttl_days` (Number) The number of days to use cached layers before expiring them. Defaults to 7 days.
|
||||
- `extra_env` (Map of String) Extra environment variables to set for the container. This may include evbuilder options.
|
||||
- `git_password` (String, Sensitive) The password to use for Git authentication. This is optional.
|
||||
- `git_username` (String) The username to use for Git authentication. This is optional.
|
||||
- `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.
|
||||
- `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.
|
||||
- `docker_config_base64` (String) (Envbuilder option) The base64 encoded Docker config file that will be used to pull images from private container registries.
|
||||
- `dockerfile_path` (String) (Envbuilder option) The relative path to the Dockerfile that will be used to build the workspace. This is an alternative to using a devcontainer that some might find simpler.
|
||||
- `exit_on_build_failure` (Boolean) (Envbuilder option) Terminates upon a build failure. This is handy when preferring the FALLBACK_IMAGE in cases where no devcontainer.json or image is provided. However, it ensures that the container stops if the build process encounters an error.
|
||||
- `extra_env` (Map of String) Extra environment variables to set for the container. This may include envbuilder options.
|
||||
- `fallback_image` (String) (Envbuilder option) Specifies an alternative image to use when neither an image is declared in the devcontainer.json file nor a Dockerfile is present. If there's a build failure (from a faulty Dockerfile) or a misconfiguration, this image will be the substitute. Set ExitOnBuildFailure to true to halt the container if the build faces an issue.
|
||||
- `git_clone_depth` (Number) (Envbuilder option) The depth to use when cloning the Git repository.
|
||||
- `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_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.
|
||||
- `insecure` (Boolean) (Envbuilder option) Bypass TLS verification when cloning and pulling from container registries.
|
||||
- `ssl_cert_base64` (String) (Envbuilder option) The content of an SSL cert file. This is useful for self-signed certificates.
|
||||
- `verbose` (Boolean) (Envbuilder option) Enable verbose output.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `env` (List of String) Computed envbuilder configuration to be set for the container.
|
||||
- `exists` (Boolean) Whether the cached image was exists or not for the given config.
|
||||
- `id` (String) Cached image identifier
|
||||
- `image` (String) Outputs the cached image URL if it exists, otherwise the builder image URL is output instead.
|
||||
- `id` (String) Cached image identifier. This will generally be the image's SHA256 digest.
|
||||
- `image` (String) Outputs the cached image repo@digest if it exists, and builder image otherwise.
|
||||
|
|
273
go.mod
273
go.mod
|
@ -1,42 +1,166 @@
|
|||
module github.com/mafredri/terraform-provider-envbuilder
|
||||
|
||||
go 1.22
|
||||
go 1.22.4
|
||||
|
||||
// We use our own Kaniko fork.
|
||||
replace github.com/GoogleContainerTools/kaniko => github.com/coder/kaniko v0.0.0-20240717115058-0ba2908ca4d3
|
||||
|
||||
// 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.0.0.20240731115920-cacbcb8fef6c
|
||||
github.com/docker/docker v26.1.0+incompatible
|
||||
github.com/go-git/go-billy/v5 v5.5.0
|
||||
github.com/google/go-containerregistry v0.19.1
|
||||
github.com/hashicorp/terraform-plugin-docs v0.19.4
|
||||
github.com/hashicorp/terraform-plugin-framework v1.10.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.9.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
)
|
||||
|
||||
require (
|
||||
cdr.dev/slog v1.6.2-0.20240126064726-20367d4aede6 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||
github.com/DataDog/appsec-internal-go v1.5.0 // indirect
|
||||
github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 // indirect
|
||||
github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1 // indirect
|
||||
github.com/DataDog/datadog-go/v5 v5.3.0 // indirect
|
||||
github.com/DataDog/go-libddwaf/v2 v2.4.2 // indirect
|
||||
github.com/DataDog/go-tuf v1.0.2-0.5.2 // indirect
|
||||
github.com/DataDog/gostackparse v0.7.0 // indirect
|
||||
github.com/DataDog/sketches-go v1.4.2 // indirect
|
||||
github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||
github.com/Microsoft/hcsshim v0.11.4 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect
|
||||
github.com/agext/levenshtein v1.2.2 // 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
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ecr v1.27.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.23.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.49.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect
|
||||
github.com/aws/smithy-go v1.20.2 // indirect
|
||||
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240419161514-af205d85bb44 // indirect
|
||||
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/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/fatih/color v1.16.0 // 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.15 // indirect
|
||||
github.com/containerd/continuity v0.4.3 // indirect
|
||||
github.com/containerd/fifo v1.1.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
|
||||
github.com/containerd/ttrpc v1.2.3 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.1.1 // 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/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 v26.1.0+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.2+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.1 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/ePirat/docker-credential-gitlabci v1.0.0 // indirect
|
||||
github.com/ebitengine/purego v0.6.0-alpha.5 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
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-git/go-git/v5 v5.12.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.1 // 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/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/uuid v1.6.0 // 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/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-memdb v1.3.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.0 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
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.7.0 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.21.0 // indirect
|
||||
github.com/hashicorp/logutils v1.0.0 // indirect
|
||||
|
@ -46,39 +170,150 @@ require (
|
|||
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
|
||||
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/huandu/xstrings v1.3.3 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/illarion/gonotify v1.0.1 // indirect
|
||||
github.com/imdario/mergo v0.3.15 // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
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-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mdlayher/genetlink v1.3.2 // indirect
|
||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||
github.com/mdlayher/sdnotify v1.0.0 // indirect
|
||||
github.com/mdlayher/socket v0.5.0 // indirect
|
||||
github.com/miekg/dns v1.1.55 // indirect
|
||||
github.com/minio/highwayhash v1.0.2 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
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/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/sequential v0.5.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.0 // indirect
|
||||
github.com/moby/sys/symlink v0.2.0 // indirect
|
||||
github.com/moby/sys/user 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
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/oklog/run v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.1.0 // indirect
|
||||
github.com/opencontainers/selinux v1.11.0 // indirect
|
||||
github.com/otiai10/copy v1.14.0 // indirect
|
||||
github.com/outcaste-io/ristretto v0.2.3 // indirect
|
||||
github.com/philhofer/fwd v1.1.2 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.18 // indirect
|
||||
github.com/pion/transport/v2 v2.0.0 // indirect
|
||||
github.com/pion/udp v0.1.4 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/posener/complete v1.2.3 // indirect
|
||||
github.com/prometheus/client_golang v1.19.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.0 // indirect
|
||||
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect
|
||||
github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 // indirect
|
||||
github.com/redis/go-redis/v9 v9.1.0 // indirect
|
||||
github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.2.2 // indirect
|
||||
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
|
||||
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
|
||||
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 // indirect
|
||||
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/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
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
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-meta v1.1.0 // indirect
|
||||
github.com/zclconf/go-cty v1.14.4 // 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
|
||||
go.nhat.io/otelsql v0.13.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||
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.25.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/oauth2 v0.20.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/term v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.22.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
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/grpc v1.63.2 // indirect
|
||||
google.golang.org/protobuf v1.34.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.3.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e // indirect
|
||||
google.golang.org/grpc v1.64.1 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.64.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20240509041132-65b30f7869dc // indirect
|
||||
inet.af/peercred v0.0.0-20210906144145-0893ea02156a // indirect
|
||||
nhooyr.io/websocket v1.8.7 // indirect
|
||||
storj.io/drpc v0.0.33 // indirect
|
||||
tailscale.com v1.46.1 // indirect
|
||||
)
|
||||
|
|
|
@ -7,6 +7,15 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
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/go-git/go-billy/v5/osfs"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
|
@ -28,17 +37,36 @@ type CachedImageDataSource struct {
|
|||
|
||||
// CachedImageDataSourceModel describes the data source data model.
|
||||
type CachedImageDataSourceModel struct {
|
||||
// Required "inputs".
|
||||
BuilderImage types.String `tfsdk:"builder_image"`
|
||||
CacheRepo types.String `tfsdk:"cache_repo"`
|
||||
CacheTTLDays types.Number `tfsdk:"cache_ttl_days"`
|
||||
Env types.List `tfsdk:"env"`
|
||||
Exists types.Bool `tfsdk:"exists"`
|
||||
ExtraEnv types.Map `tfsdk:"extra_env"`
|
||||
GitPassword types.String `tfsdk:"git_password"`
|
||||
GitURL types.String `tfsdk:"git_url"`
|
||||
GitUsername types.String `tfsdk:"git_username"`
|
||||
ID types.String `tfsdk:"id"`
|
||||
Image types.String `tfsdk:"image"`
|
||||
// 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"`
|
||||
SSLCertBase64 types.String `tfsdk:"ssl_cert_base64"`
|
||||
Verbose types.Bool `tfsdk:"verbose"`
|
||||
// Computed "outputs".
|
||||
Env types.List `tfsdk:"env"`
|
||||
Exists types.Bool `tfsdk:"exists"`
|
||||
ID types.String `tfsdk:"id"`
|
||||
Image types.String `tfsdk:"image"`
|
||||
}
|
||||
|
||||
func (d *CachedImageDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
|
@ -48,56 +76,127 @@ func (d *CachedImageDataSource) Metadata(ctx context.Context, req datasource.Met
|
|||
func (d *CachedImageDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
// This description is used by the documentation generator and the language server.
|
||||
MarkdownDescription: "The cached image data source can be used to retrieve a cached image produced by envbuilder.",
|
||||
MarkdownDescription: "The cached image data source can be used to retrieve a cached image produced by envbuilder. Reading from this data source will clone the specified Git repository, read a Devcontainer specification or Dockerfile, and check for its presence in the provided cache repo.",
|
||||
|
||||
Attributes: map[string]schema.Attribute{
|
||||
// Required "inputs".
|
||||
"builder_image": schema.StringAttribute{
|
||||
MarkdownDescription: "The builder image URL to use if the cache does not exist.",
|
||||
MarkdownDescription: "The envbuilder image to use if the cached version is not found.",
|
||||
Required: true,
|
||||
},
|
||||
"cache_repo": schema.StringAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) The name of the container registry to fetch the cache image from.",
|
||||
Required: true,
|
||||
},
|
||||
"git_url": schema.StringAttribute{
|
||||
MarkdownDescription: "The URL of a Git repository containing a Devcontainer or Docker image to clone.",
|
||||
MarkdownDescription: "(Envbuilder option) The URL of a Git repository containing a Devcontainer or Docker image to clone.",
|
||||
Required: true,
|
||||
},
|
||||
"git_username": schema.StringAttribute{
|
||||
MarkdownDescription: "The username to use for Git authentication. This is optional.",
|
||||
// Optional "inputs".
|
||||
"base_image_cache_dir": schema.StringAttribute{
|
||||
MarkdownDescription: "(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.",
|
||||
Optional: true,
|
||||
},
|
||||
"git_password": schema.StringAttribute{
|
||||
MarkdownDescription: "The password to use for Git authentication. This is optional.",
|
||||
Sensitive: true,
|
||||
"build_context_path": schema.StringAttribute{
|
||||
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,
|
||||
},
|
||||
"cache_repo": schema.StringAttribute{
|
||||
MarkdownDescription: "The name of the container registry to fetch the cache image from.",
|
||||
Required: 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,
|
||||
},
|
||||
"cache_ttl_days": schema.NumberAttribute{
|
||||
MarkdownDescription: "The number of days to use cached layers before expiring them. Defaults to 7 days.",
|
||||
"devcontainer_dir": schema.StringAttribute{
|
||||
MarkdownDescription: "(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`.",
|
||||
Optional: true,
|
||||
},
|
||||
"devcontainer_json_path": schema.StringAttribute{
|
||||
MarkdownDescription: "(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.",
|
||||
Optional: true,
|
||||
},
|
||||
"dockerfile_path": schema.StringAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) The relative path to the Dockerfile that will be used to build the workspace. This is an alternative to using a devcontainer that some might find simpler.",
|
||||
Optional: true,
|
||||
},
|
||||
"docker_config_base64": schema.StringAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) The base64 encoded Docker config file that will be used to pull images from private container registries.",
|
||||
Optional: true,
|
||||
},
|
||||
"exit_on_build_failure": schema.BoolAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) Terminates upon a build failure. This is handy when preferring the FALLBACK_IMAGE in cases where no devcontainer.json or image is provided. However, it ensures that the container stops if the build process encounters an error.",
|
||||
Optional: true,
|
||||
},
|
||||
// TODO(mafredri): Map vs List? Support both?
|
||||
"extra_env": schema.MapAttribute{
|
||||
MarkdownDescription: "Extra environment variables to set for the container. This may include evbuilder options.",
|
||||
MarkdownDescription: "Extra environment variables to set for the container. This may include envbuilder options.",
|
||||
ElementType: types.StringType,
|
||||
Optional: true,
|
||||
},
|
||||
"id": schema.StringAttribute{
|
||||
MarkdownDescription: "Cached image identifier",
|
||||
"fallback_image": schema.StringAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) Specifies an alternative image to use when neither an image is declared in the devcontainer.json file nor a Dockerfile is present. If there's a build failure (from a faulty Dockerfile) or a misconfiguration, this image will be the substitute. Set ExitOnBuildFailure to true to halt the container if the build faces an issue.",
|
||||
Optional: true,
|
||||
},
|
||||
"git_clone_depth": schema.Int64Attribute{
|
||||
MarkdownDescription: "(Envbuilder option) The depth to use when cloning the Git repository.",
|
||||
Optional: true,
|
||||
},
|
||||
"git_clone_single_branch": schema.BoolAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) Clone only a single branch of the Git repository.",
|
||||
Optional: true,
|
||||
},
|
||||
"git_http_proxy_url": schema.StringAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) The URL for the HTTP proxy. This is optional.",
|
||||
Optional: true,
|
||||
},
|
||||
"git_password": schema.StringAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) The password to use for Git authentication. This is optional.",
|
||||
Sensitive: true,
|
||||
Optional: true,
|
||||
},
|
||||
"git_ssh_private_key_path": schema.StringAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) Path to an SSH private key to be used for Git authentication.",
|
||||
Optional: true,
|
||||
},
|
||||
"git_username": schema.StringAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) The username to use for Git authentication. This is optional.",
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"ignore_paths": schema.ListAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) The comma separated list of paths to ignore when building the workspace.",
|
||||
ElementType: types.StringType,
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
"insecure": schema.BoolAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) Bypass TLS verification when cloning and pulling from container registries.",
|
||||
Optional: true,
|
||||
},
|
||||
"ssl_cert_base64": schema.StringAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) The content of an SSL cert file. This is useful for self-signed certificates.",
|
||||
Optional: true,
|
||||
},
|
||||
"verbose": schema.BoolAttribute{
|
||||
MarkdownDescription: "(Envbuilder option) Enable verbose output.",
|
||||
Optional: true,
|
||||
},
|
||||
|
||||
// Computed "outputs".
|
||||
// TODO(mafredri): Map vs List? Support both?
|
||||
"env": schema.ListAttribute{
|
||||
MarkdownDescription: "Computed envbuilder configuration to be set for the container.",
|
||||
ElementType: types.StringType,
|
||||
Computed: true,
|
||||
},
|
||||
"exists": schema.BoolAttribute{
|
||||
MarkdownDescription: "Whether the cached image was exists or not for the given config.",
|
||||
Computed: true,
|
||||
},
|
||||
"image": schema.StringAttribute{
|
||||
MarkdownDescription: "Outputs the cached image URL if it exists, otherwise the builder image URL is output instead.",
|
||||
"id": schema.StringAttribute{
|
||||
MarkdownDescription: "Cached image identifier. This will generally be the image's SHA256 digest.",
|
||||
Computed: true,
|
||||
},
|
||||
// TODO(mafredri): Map vs List? Support both?
|
||||
"env": schema.ListAttribute{
|
||||
MarkdownDescription: "Computed envbuilder configuration to be set for the container.",
|
||||
ElementType: types.StringType,
|
||||
"image": schema.StringAttribute{
|
||||
MarkdownDescription: "Outputs the cached image repo@digest if it exists, and builder image otherwise.",
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
|
@ -142,10 +241,96 @@ func (d *CachedImageDataSource) Read(ctx context.Context, req datasource.ReadReq
|
|||
// return
|
||||
// }
|
||||
|
||||
// TODO(mafredri): Implement the actual data source read logic.
|
||||
data.ID = types.StringValue("cached-image-id")
|
||||
data.Exists = types.BoolValue(false)
|
||||
data.Image = data.BuilderImage
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "envbuilder-provider-cached-image-data-source")
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create temp directory: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := os.RemoveAll(tmpDir); err != nil {
|
||||
tflog.Error(ctx, "failed to clean up tmpDir", map[string]any{"tmpDir": tmpDir, "err": err.Error()})
|
||||
}
|
||||
}()
|
||||
oldKanikoDir := kconfig.KanikoDir
|
||||
tmpKanikoDir := filepath.Join(tmpDir, constants.MagicDir)
|
||||
// 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)
|
||||
defer func() {
|
||||
kconfig.KanikoDir = oldKanikoDir
|
||||
tflog.Info(ctx, "restored kaniko dir to "+oldKanikoDir)
|
||||
}()
|
||||
if err := os.MkdirAll(tmpKanikoDir, 0o755); err != nil {
|
||||
tflog.Error(ctx, "failed to create kaniko dir: "+err.Error())
|
||||
}
|
||||
|
||||
// TODO: check if this is a "plan" or "apply", and only run envbuilder on "apply".
|
||||
// This may require changing this to be a resource instead of a data source.
|
||||
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: tmpDir,
|
||||
|
||||
// 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(),
|
||||
SSLCertBase64: data.SSLCertBase64.ValueString(),
|
||||
|
||||
// Other options
|
||||
BaseImageCacheDir: data.BaseImageCacheDir.ValueString(),
|
||||
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.
|
||||
CoderAgentSubsystem: nil,
|
||||
CoderAgentToken: "",
|
||||
CoderAgentURL: "",
|
||||
ExportEnvFile: "",
|
||||
InitArgs: "",
|
||||
InitCommand: "",
|
||||
InitScript: "",
|
||||
LayerCacheDir: "",
|
||||
PostStartScriptPath: "",
|
||||
PushImage: false,
|
||||
SetupScript: "",
|
||||
SkipRebuild: false,
|
||||
}
|
||||
|
||||
image, err := envbuilder.RunCacheProbe(ctx, opts)
|
||||
data.Exists = types.BoolValue(err == nil)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddWarning("Cached image not found", err.Error())
|
||||
// TODO: Get the repo digest of the envbuilder image and use that as the ID
|
||||
data.Image = data.BuilderImage
|
||||
} else {
|
||||
digest, err := image.Digest()
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Failed to get cached image digest", err.Error())
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, fmt.Sprintf("found image: %s@%s", opts.CacheRepo, digest))
|
||||
data.ID = types.StringValue(digest.String())
|
||||
data.Image = types.StringValue(fmt.Sprintf("%s@%s", opts.CacheRepo, digest))
|
||||
}
|
||||
|
||||
// Compute the env attribute from the config map.
|
||||
// TODO(mafredri): Convert any other relevant attributes given via schema.
|
||||
|
@ -167,6 +352,28 @@ func (d *CachedImageDataSource) Read(ctx context.Context, req datasource.ReadReq
|
|||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||
}
|
||||
|
||||
// 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...))
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: the String() method of Terraform values will evalue to `<null>` if unknown.
|
||||
// Check IsUnknown() first before calling String().
|
||||
type stringable interface {
|
||||
IsUnknown() bool
|
||||
String() string
|
||||
|
@ -180,3 +387,17 @@ func appendKnownEnvToList(list types.List, key string, value stringable) types.L
|
|||
list, _ = types.ListValue(types.StringType, append(list.Elements(), elem))
|
||||
return list
|
||||
}
|
||||
|
||||
func tfListToStringSlice(l types.List) []string {
|
||||
var ss []string
|
||||
for _, el := range l.Elements() {
|
||||
if sv, ok := el.(stringable); !ok {
|
||||
panic(fmt.Sprintf("developer error: element %+v must be stringable", el))
|
||||
} else if sv.IsUnknown() {
|
||||
ss = append(ss, "")
|
||||
} else {
|
||||
ss = append(ss, sv.String())
|
||||
}
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
|
|
@ -4,45 +4,118 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccExampleDataSource(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
// Read testing
|
||||
{
|
||||
Config: testAccCachedImageDataSourceConfig,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Input
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "extra_env.ENVBUILDER_VERBOSE", "true"),
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "git_url", "https://github.com/coder/envbuilder-starter-devcontainer"),
|
||||
resource.TestCheckNoResourceAttr("data.envbuilder_cached_image.test", "git_username"),
|
||||
resource.TestCheckNoResourceAttr("data.envbuilder_cached_image.test", "git_password"),
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "cache_repo", "localhost:5000/local/test-cache"),
|
||||
resource.TestCheckNoResourceAttr("data.envbuilder_cached_image.test", "cache_ttl_days"),
|
||||
// Computed
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "id", "cached-image-id"),
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "exists", "false"),
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "image", "ghcr.io/coder/envbuilder:latest"),
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "env.0", "ENVBUILDER_VERBOSE=\"true\""),
|
||||
),
|
||||
// TODO: change this to only test for a non-existent image.
|
||||
// Move the heavy lifting to integration.
|
||||
func TestAccCachedImageDataSource(t *testing.T) {
|
||||
t.Run("Found", func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
t.Cleanup(cancel)
|
||||
files := map[string]string{
|
||||
"devcontainer.json": `{"build": { "dockerfile": "Dockerfile" }}`,
|
||||
"Dockerfile": `FROM localhost:5000/test-ubuntu:latest
|
||||
RUN apt-get update && apt-get install -y cowsay`,
|
||||
}
|
||||
deps := setup(t, files)
|
||||
seedCache(ctx, t, deps)
|
||||
tfCfg := fmt.Sprintf(`data "envbuilder_cached_image" "test" {
|
||||
builder_image = %q
|
||||
devcontainer_dir = %q
|
||||
git_url = %q
|
||||
extra_env = {
|
||||
"FOO" : "bar"
|
||||
}
|
||||
cache_repo = %q
|
||||
}`, deps.BuilderImage, deps.RepoDir, deps.RepoDir, deps.CacheRepo)
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: tfCfg,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Inputs should still be present.
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "cache_repo", deps.CacheRepo),
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "extra_env.FOO", "bar"),
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "git_url", deps.RepoDir),
|
||||
// Should be empty
|
||||
resource.TestCheckNoResourceAttr("data.envbuilder_cached_image.test", "git_username"),
|
||||
resource.TestCheckNoResourceAttr("data.envbuilder_cached_image.test", "git_password"),
|
||||
resource.TestCheckNoResourceAttr("data.envbuilder_cached_image.test", "cache_ttl_days"),
|
||||
// Computed
|
||||
resource.TestCheckResourceAttrWith("data.envbuilder_cached_image.test", "id", func(value string) error {
|
||||
// value is enclosed in quotes
|
||||
value = strings.Trim(value, `"`)
|
||||
if !strings.HasPrefix(value, "sha256:") {
|
||||
return fmt.Errorf("expected image %q to have prefix %q", value, deps.CacheRepo)
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "exists", "true"),
|
||||
resource.TestCheckResourceAttrSet("data.envbuilder_cached_image.test", "image"),
|
||||
resource.TestCheckResourceAttrWith("data.envbuilder_cached_image.test", "image", func(value string) error {
|
||||
// value is enclosed in quotes
|
||||
value = strings.Trim(value, `"`)
|
||||
if !strings.HasPrefix(value, deps.CacheRepo) {
|
||||
return fmt.Errorf("expected image %q to have prefix %q", value, deps.CacheRepo)
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "env.0", "FOO=\"bar\""),
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("NotFound", func(t *testing.T) {
|
||||
files := map[string]string{
|
||||
"devcontainer.json": `{"build": { "dockerfile": "Dockerfile" }}`,
|
||||
"Dockerfile": `FROM localhost:5000/test-ubuntu:latest
|
||||
RUN apt-get update && apt-get install -y cowsay`,
|
||||
}
|
||||
deps := setup(t, files)
|
||||
// We do not seed the cache.
|
||||
tfCfg := fmt.Sprintf(`data "envbuilder_cached_image" "test" {
|
||||
builder_image = %q
|
||||
devcontainer_dir = %q
|
||||
git_url = %q
|
||||
extra_env = {
|
||||
"FOO" : "bar"
|
||||
}
|
||||
cache_repo = %q
|
||||
}`, deps.BuilderImage, deps.RepoDir, deps.RepoDir, deps.CacheRepo)
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: tfCfg,
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Inputs should still be present.
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "cache_repo", deps.CacheRepo),
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "extra_env.FOO", "bar"),
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "git_url", deps.RepoDir),
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "exists", "false"),
|
||||
resource.TestCheckResourceAttr("data.envbuilder_cached_image.test", "image", deps.BuilderImage),
|
||||
// Should be empty
|
||||
resource.TestCheckNoResourceAttr("data.envbuilder_cached_image.test", "git_username"),
|
||||
resource.TestCheckNoResourceAttr("data.envbuilder_cached_image.test", "git_password"),
|
||||
resource.TestCheckNoResourceAttr("data.envbuilder_cached_image.test", "cache_ttl_days"),
|
||||
// Computed values should be empty.
|
||||
resource.TestCheckNoResourceAttr("data.envbuilder_cached_image.test", "id"),
|
||||
resource.TestCheckResourceAttrSet("data.envbuilder_cached_image.test", "env.0"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const testAccCachedImageDataSourceConfig = `
|
||||
data "envbuilder_cached_image" "test" {
|
||||
builder_image = "ghcr.io/coder/envbuilder:latest"
|
||||
git_url = "https://github.com/coder/envbuilder-starter-devcontainer"
|
||||
cache_repo = "localhost:5000/local/test-cache"
|
||||
extra_env = {
|
||||
"ENVBUILDER_VERBOSE" : "true"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
|
@ -4,10 +4,27 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/providerserver"
|
||||
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
|
||||
"github.com/mafredri/terraform-provider-envbuilder/testutil/registrytest"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
testContainerLabel = "terraform-provider-envbuilder-test"
|
||||
)
|
||||
|
||||
// testAccProtoV6ProviderFactories are used to instantiate a provider during
|
||||
|
@ -23,3 +40,128 @@ func testAccPreCheck(t *testing.T) {
|
|||
// about the appropriate environment variables being set are common to see in a pre-check
|
||||
// function.
|
||||
}
|
||||
|
||||
type testDependencies struct {
|
||||
BuilderImage string
|
||||
RepoDir string
|
||||
CacheRepo string
|
||||
}
|
||||
|
||||
func setup(t testing.TB, files map[string]string) testDependencies {
|
||||
t.Helper()
|
||||
|
||||
envbuilderImage := getEnvOrDefault("ENVBUILDER_IMAGE", "ghcr.io/coder/envbuilder-preview")
|
||||
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.
|
||||
repoDir := t.TempDir()
|
||||
regDir := t.TempDir()
|
||||
reg := registrytest.New(t, regDir)
|
||||
writeFiles(t, files, repoDir)
|
||||
return testDependencies{
|
||||
BuilderImage: envbuilderImageRef,
|
||||
CacheRepo: reg + "/test",
|
||||
RepoDir: repoDir,
|
||||
}
|
||||
}
|
||||
|
||||
func seedCache(ctx context.Context, t testing.TB, deps testDependencies) {
|
||||
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||
require.NoError(t, err, "init docker client")
|
||||
t.Cleanup(func() { _ = cli.Close() })
|
||||
ensureImage(ctx, t, cli, deps.BuilderImage)
|
||||
// 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_DEVCONTAINER_DIR=" + deps.RepoDir,
|
||||
"ENVBUILDER_EXIT_ON_BUILD_FAILURE=true",
|
||||
"ENVBUILDER_INIT_SCRIPT=exit",
|
||||
// FIXME: Enabling this options causes envbuilder to add its binary to the image under the path
|
||||
// /.envbuilder/bin/envbuilder. This file will have ownership root:root and permissions 0o755.
|
||||
// Because of this, t.Cleanup() will be unable to delete the temp dir, causing the test to fail.
|
||||
// "ENVBUILDER_PUSH_IMAGE=true",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
testContainerLabel: "true",
|
||||
}}, &container.HostConfig{
|
||||
NetworkMode: container.NetworkMode("host"),
|
||||
Binds: []string{deps.RepoDir + ":" + deps.RepoDir},
|
||||
}, nil, nil, "")
|
||||
require.NoError(t, err, "failed to run envbuilder to seed cache")
|
||||
t.Cleanup(func() {
|
||||
_ = cli.ContainerRemove(ctx, ctr.ID, container.RemoveOptions{
|
||||
RemoveVolumes: true,
|
||||
Force: true,
|
||||
})
|
||||
})
|
||||
err = cli.ContainerStart(ctx, ctr.ID, container.StartOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
rawLogs, err := cli.ContainerLogs(ctx, ctr.ID, container.LogsOptions{
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
Follow: true,
|
||||
Timestamps: false,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer rawLogs.Close()
|
||||
scanner := bufio.NewScanner(rawLogs)
|
||||
SCANLOGS:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
require.Fail(t, "envbuilder did not finish running in time")
|
||||
default:
|
||||
if !scanner.Scan() {
|
||||
require.Fail(t, "envbuilder did not run successfully")
|
||||
}
|
||||
log := scanner.Text()
|
||||
t.Logf("envbuilder: %s", log)
|
||||
if strings.Contains(log, "=== Running the init command") {
|
||||
break SCANLOGS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getEnvOrDefault(env, defVal string) string {
|
||||
if val := os.Getenv(env); val != "" {
|
||||
return val
|
||||
}
|
||||
return defVal
|
||||
}
|
||||
|
||||
func writeFiles(t testing.TB, files map[string]string, destPath string) {
|
||||
for relPath, content := range files {
|
||||
absPath := filepath.Join(destPath, relPath)
|
||||
d := filepath.Dir(absPath)
|
||||
bs := []byte(content)
|
||||
require.NoError(t, os.MkdirAll(d, 0o755))
|
||||
require.NoError(t, os.WriteFile(absPath, bs, 0o644))
|
||||
t.Logf("wrote %d bytes to %s", len(bs), absPath)
|
||||
}
|
||||
}
|
||||
|
||||
func ensureImage(ctx context.Context, t testing.TB, cli *client.Client, ref string) {
|
||||
t.Helper()
|
||||
|
||||
t.Logf("ensuring image %q", ref)
|
||||
images, err := cli.ImageList(ctx, image.ListOptions{})
|
||||
require.NoError(t, err, "list images")
|
||||
for _, img := range images {
|
||||
if slices.Contains(img.RepoTags, ref) {
|
||||
t.Logf("image %q found locally, not pulling", ref)
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Logf("image %s not found locally, attempting to pull", ref)
|
||||
resp, err := cli.ImagePull(ctx, ref, image.PullOptions{})
|
||||
require.NoError(t, err)
|
||||
_, err = io.ReadAll(resp)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
24
testutil/registrytest/registrytest.go
Normal file
24
testutil/registrytest/registrytest.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package registrytest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/registry"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
t.Helper()
|
||||
regHandler := registry.New(registry.WithBlobHandler(registry.NewDiskBlobHandler(dir)))
|
||||
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())
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue