diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 147d86d..709caf3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -101,6 +101,8 @@ jobs: env: TF_ACC: "1" CODER_ENTERPRISE_LICENSE: ${{ secrets.CODER_ENTERPRISE_LICENSE }} + # Reaper pulls from Docker Hub (flaky); tests use t.Cleanup and CI is ephemeral. + TESTCONTAINERS_RYUK_DISABLED: "true" timeout-minutes: 10 lint: diff --git a/go.mod b/go.mod index 6e127ca..14dd35e 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,6 @@ require ( github.com/coder/retry v1.5.1 github.com/coder/serpent v0.15.0 github.com/coder/websocket v1.8.15 - github.com/docker/docker v28.5.2+incompatible - github.com/docker/go-connections v0.7.0 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 github.com/hashicorp/terraform-plugin-docs v0.25.0 @@ -21,9 +19,12 @@ require ( github.com/hashicorp/terraform-plugin-testing v1.16.0 github.com/otiai10/copy v1.14.1 github.com/stretchr/testify v1.11.1 + github.com/testcontainers/testcontainers-go v0.43.0 ) require ( + dario.cat/mergo v1.0.2 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect github.com/DataDog/appsec-internal-go v1.11.2 // indirect github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.64.2 // indirect @@ -62,6 +63,7 @@ require ( github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect github.com/brianvoe/gofakeit/v7 v7.15.0 // indirect github.com/buger/jsonparser v1.1.2 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect @@ -71,9 +73,13 @@ require ( github.com/coder/terraform-provider-coder/v2 v2.18.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect github.com/coreos/go-oidc/v3 v3.19.0 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect + github.com/docker/go-connections v0.7.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 // indirect @@ -115,8 +121,10 @@ require ( github.com/imdario/mergo v0.3.16 // indirect github.com/invopop/jsonschema v0.14.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.18.6 // indirect github.com/lucasb-eyer/go-colorful v1.4.0 // indirect github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect + github.com/magiconair/properties v1.8.10 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.22 // indirect github.com/mattn/go-runewidth v0.0.19 // indirect @@ -127,7 +135,14 @@ require ( github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/sys/atomicwriter v0.1.0 // indirect + github.com/moby/go-archive v0.2.0 // indirect + github.com/moby/moby/api v1.54.2 // indirect + github.com/moby/moby/client v0.4.0 // indirect + github.com/moby/patternmatcher v0.6.1 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/muesli/termenv v0.16.0 // indirect @@ -155,8 +170,9 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/secure-systems-lab/go-securesystemslib v0.10.0 // indirect - github.com/shirou/gopsutil/v4 v4.26.1 // indirect + github.com/shirou/gopsutil/v4 v4.26.5 // indirect github.com/shopspring/decimal v1.4.0 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect diff --git a/go.sum b/go.sum index 1f34318..c58e691 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ cloud.google.com/go/longrunning v1.0.0 h1:lwzWEYD8+NkYV7dhexOz6kmlvajZA70+bW/xMh cloud.google.com/go/longrunning v1.0.0/go.mod h1:8nqFBPOO1U/XkhWl0I19AMZEphrHi73VNABIpKYaTwM= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= @@ -98,6 +100,8 @@ github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/ github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk= github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -139,8 +143,14 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/coreos/go-oidc/v3 v3.19.0 h1:F/xyOi3x1UnG1U27YVnM1N6bHiL1K2upi6U/0qr8r+I= github.com/coreos/go-oidc/v3 v3.19.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -152,8 +162,6 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WA github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 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/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= -github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -318,6 +326,8 @@ github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= @@ -346,10 +356,20 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 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/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= -github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= +github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= +github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= +github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw= +github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g= +github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= +github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.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.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -358,8 +378,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -431,8 +449,8 @@ github.com/secure-systems-lab/go-securesystemslib v0.10.0 h1:l+H5ErcW0PAehBNrBxo github.com/secure-systems-lab/go-securesystemslib v0.10.0/go.mod h1:MRKONWmRoFzPNQ9USRF9i1mc7MvAVvF1LlW8X5VWDvk= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= -github.com/shirou/gopsutil/v4 v4.26.1 h1:TOkEyriIXk2HX9d4isZJtbjXbEjf5qyKPAzbzY0JWSo= -github.com/shirou/gopsutil/v4 v4.26.1/go.mod h1:medLI9/UNAb0dOI9Q3/7yWSqKkj00u+1tgY8nvv41pc= +github.com/shirou/gopsutil/v4 v4.26.5 h1:RPcBXkpz7kOj9PqGFQOlBPZHsyaPvPVQc098y9RmCNM= +github.com/shirou/gopsutil/v4 v4.26.5/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= @@ -453,8 +471,9 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -469,6 +488,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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/testcontainers/testcontainers-go v0.43.0 h1:oEQx5MW2DGd9z3AeEQfB2lPM0eLs7ztyaGRu75bFo5A= +github.com/testcontainers/testcontainers-go v0.43.0/go.mod h1:+VxkT2NQnKOZPKi6praMuMKYHYyOGXr0XSBSlSMCzFo= github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po= github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= @@ -561,8 +582,6 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUY go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 h1:qazEJlUOQzhCpzQpFETGby7EdqjI1wsd0W+6Gg1SCTU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0/go.mod h1:fOD2Yefuxixkx3ahVNf0O/PERb6r4OlbxfATVnYvzCo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.37.0 h1:6VjV6Et+1Hd2iLZEPtdV7vie80Yyqf7oikJLjQ/myi0= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.37.0/go.mod h1:u8hcp8ji5gaM/RfcOo8z9NMnf1pVLfVY7lBY2VOGuUU= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0 h1:SNhVp/9q4Go/XHBkQ1/d5u9P/U+L1yaGPoi0x+mStaI= @@ -645,6 +664,7 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -737,6 +757,8 @@ gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987 h1:TU8z2Lh3Bbq77w0t1eG8yRlL gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU= k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= +pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= storj.io/drpc v0.0.34 h1:q9zlQKfJ5A7x8NQNFk8x7eKUF78FMhmAbZLnFK+og7I= storj.io/drpc v0.0.34/go.mod h1:Y9LZaa8esL1PW2IDMqJE7CFSNq7d5bQ3RI7mGPtmKMg= tailscale.com v1.80.3 h1:uGLWZdl61YbhvhoU6qdnHPF7zuuqGGRaTfbECur035Y= diff --git a/integration/integration.go b/integration/integration.go index 8130979..ffd383f 100644 --- a/integration/integration.go +++ b/integration/integration.go @@ -1,25 +1,23 @@ package integration import ( - "bytes" "context" - "fmt" "io" - "net" "net/url" "os" "testing" "time" "github.com/coder/coder/v2/codersdk" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/image" - "github.com/docker/docker/api/types/network" - "github.com/docker/docker/client" - "github.com/docker/docker/pkg/stdcopy" - "github.com/docker/go-connections/nat" - "github.com/stretchr/testify/require" + "github.com/testcontainers/testcontainers-go" + tcnetwork "github.com/testcontainers/testcontainers-go/network" + "github.com/testcontainers/testcontainers-go/wait" +) + +const ( + coderHTTPPort = "3000/tcp" + coderStartupTimeout = 90 * time.Second ) // User-configurable options for coder backend. @@ -59,10 +57,8 @@ func EnableRateLimits(opts *coderOptions) { func StartCoder(ctx context.Context, t *testing.T, name string, options ...func(*coderOptions)) *codersdk.Client { // Start with the defaults. opts := coderOptions{ - useLicense: false, - image: "ghcr.io/coder/coder", - version: "latest", - experiments: "", + image: "ghcr.io/coder/coder", + version: "latest", } // Apply user-selected options. @@ -86,82 +82,53 @@ func StartCoder(ctx context.Context, t *testing.T, name string, options ...func( } ref := opts.image + ":" + opts.version - t.Logf("using coder image %s:%s", opts.image, opts.version) - - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - require.NoError(t, err, "init docker client") - - p := randomPort(t) - t.Logf("random port is %d", p) + t.Logf("[%s] using coder image %s", name, ref) // Give Coder an external PostgreSQL on a per-test network instead of its // embedded one, which downloads a binary from Maven at startup (a flaky, // rate-limited fetch that reds CI lanes). - netName := "terraform-provider-coderd-" + name + "-net" - net, err := cli.NetworkCreate(ctx, netName, network.CreateOptions{}) + nw, err := tcnetwork.New(ctx) + testcontainers.CleanupNetwork(t, nw) require.NoError(t, err, "create test network") - t.Cleanup(func() { - // t.Context() is canceled before t.Cleanup callbacks run, so use a - // fresh context or the removal is a no-op and the network leaks. - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - if err := cli.NetworkRemove(ctx, net.ID); err != nil { - t.Logf("error removing network %s: %v", net.ID, err) - } - }) - startPostgres(ctx, t, cli, netName) + startPostgres(ctx, t, nw.Name) - // Stand up a temporary Coder instance. - pullImage(ctx, t, cli, ref) - - env := []string{ - "CODER_HTTP_ADDRESS=0.0.0.0:3000", // Listen on all interfaces inside the container. - "CODER_ACCESS_URL=http://localhost:3000", // Avoid creating try.coder.app URLs. - "CODER_TELEMETRY_ENABLE=false", // Avoid creating noise. - "CODER_PG_CONNECTION_URL=postgres://postgres:postgres@postgres:5432/postgres?sslmode=disable", + env := map[string]string{ + "CODER_HTTP_ADDRESS": "0.0.0.0:3000", // Listen on all interfaces inside the container. + "CODER_ACCESS_URL": "http://localhost:3000", // Avoid creating try.coder.app URLs. + "CODER_TELEMETRY_ENABLE": "false", // Avoid creating noise. + "CODER_PG_CONNECTION_URL": "postgres://postgres:postgres@postgres:5432/postgres?sslmode=disable", } if !opts.enableRateLimits { - env = append(env, "CODER_DANGEROUS_DISABLE_RATE_LIMITS=true") + env["CODER_DANGEROUS_DISABLE_RATE_LIMITS"] = "true" } if opts.experiments != "" { - env = append(env, "CODER_EXPERIMENTS="+opts.experiments) + env["CODER_EXPERIMENTS"] = opts.experiments } - ctr, err := cli.ContainerCreate(ctx, &container.Config{ - Image: ref, - Env: env, - Labels: map[string]string{}, - ExposedPorts: map[nat.Port]struct{}{nat.Port("3000/tcp"): {}}, - }, &container.HostConfig{ - PortBindings: map[nat.Port][]nat.PortBinding{ - nat.Port("3000/tcp"): {{HostIP: "127.0.0.1", HostPort: fmt.Sprintf("%d", p)}}, + ctr, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: ref, + AlwaysPullImage: true, + Env: env, + ExposedPorts: []string{coderHTTPPort}, + Networks: []string{nw.Name}, + WaitingFor: wait.ForHTTP("/api/v2/buildinfo").WithPort(coderHTTPPort).WithStartupTimeout(coderStartupTimeout), }, - }, &network.NetworkingConfig{ - EndpointsConfig: map[string]*network.EndpointSettings{netName: {}}, - }, nil, "terraform-provider-coderd-"+name) - require.NoError(t, err, "create test deployment") - - t.Logf("created container %s\n", ctr.ID) - t.Cleanup(func() { // Make sure we clean up after ourselves. - // TODO: also have this execute if you Ctrl+C! - t.Logf("stopping container %s\n", ctr.ID) - // t.Context() is canceled before t.Cleanup callbacks run, so use a - // fresh context or the removal is a no-op and the container leaks. - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - _ = cli.ContainerRemove(ctx, ctr.ID, container.RemoveOptions{ - Force: true, - }) + Started: true, }) + testcontainers.CleanupContainer(t, ctr) t.Cleanup(func() { - if t.Failed() { - dumpContainerLogs(t, cli, ctr.ID) + if t.Failed() && ctr != nil { + dumpContainerLogs(t, ctr) } }) + require.NoError(t, err, "start coder container") - err = cli.ContainerStart(ctx, ctr.ID, container.StartOptions{}) - require.NoError(t, err, "start container") - t.Logf("started container %s\n", ctr.ID) + endpoint, err := ctr.PortEndpoint(ctx, coderHTTPPort, "http") + require.NoError(t, err, "coder endpoint") + coderURL, err := url.Parse(endpoint) + require.NoError(t, err, "parse coder URL") + client := codersdk.New(coderURL) // nolint:gosec // For testing only. var ( @@ -170,18 +137,8 @@ func StartCoder(ctx context.Context, t *testing.T, name string, options ...func( testUsername = "admin" ) - // Perform first time setup - coderURL, err := url.Parse(fmt.Sprintf("http://localhost:%d", p)) - require.NoError(t, err, "parse coder URL") - client := codersdk.New(coderURL) - // Wait for the container to come up. - require.Eventually(t, func() bool { - _, err := client.BuildInfo(ctx) - if err != nil { - t.Logf("not ready yet: %s", err.Error()) - } - return err == nil - }, 90*time.Second, time.Second, "coder failed to become ready in time") + // Perform first time setup. The wait strategy already blocked on + // /api/v2/buildinfo returning 200, so the server is ready. _, err = client.CreateFirstUser(ctx, codersdk.CreateFirstUserRequest{ Email: testEmail, Username: testUsername, @@ -203,55 +160,37 @@ func StartCoder(ctx context.Context, t *testing.T, name string, options ...func( return client } -// pullImage pulls a Docker image, streaming progress to stderr. -func pullImage(ctx context.Context, t *testing.T, cli *client.Client, ref string) { - t.Helper() - reader, err := cli.ImagePull(ctx, ref, image.PullOptions{}) - require.NoError(t, err, "pull image %s", ref) - defer func() { - if err := reader.Close(); err != nil { - t.Logf("error closing image puller: %v", err) - } - }() - _, err = io.Copy(os.Stderr, reader) - require.NoError(t, err, "read image pull output for %s", ref) -} - // startPostgres runs a throwaway PostgreSQL that Coder connects to instead of -// starting its embedded one. It's reachable at hostname "postgres" on netName. -// Coder retries its database connection on startup, so we don't wait for it. -func startPostgres(ctx context.Context, t *testing.T, cli *client.Client, netName string) { +// starting its embedded one. It's reachable at hostname "postgres" on the given +// network. Coder retries its database connection on startup, so we don't wait +// for it. +func startPostgres(ctx context.Context, t *testing.T, networkName string) { t.Helper() const ref = "us-docker.pkg.dev/coder-v2-images-public/public/postgres:17" - pullImage(ctx, t, cli, ref) - ctr, err := cli.ContainerCreate(ctx, &container.Config{ - Image: ref, - Env: []string{"POSTGRES_PASSWORD=postgres"}, - }, &container.HostConfig{}, &network.NetworkingConfig{ - EndpointsConfig: map[string]*network.EndpointSettings{ - netName: {Aliases: []string{"postgres"}}, + ctr, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + Image: ref, + AlwaysPullImage: true, + Env: map[string]string{"POSTGRES_PASSWORD": "postgres"}, + Networks: []string{networkName}, + NetworkAliases: map[string][]string{networkName: {"postgres"}}, }, - }, nil, "") - require.NoError(t, err, "create postgres container") - t.Cleanup(func() { - // t.Context() is canceled before t.Cleanup callbacks run, so use a - // fresh context or the removal is a no-op and the container leaks. - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - _ = cli.ContainerRemove(ctx, ctr.ID, container.RemoveOptions{Force: true}) + Started: true, }) - require.NoError(t, cli.ContainerStart(ctx, ctr.ID, container.StartOptions{}), "start postgres container") + testcontainers.CleanupContainer(t, ctr) + require.NoError(t, err, "start postgres container") } -func dumpContainerLogs(t *testing.T, cli *client.Client, containerID string) { +func dumpContainerLogs(t *testing.T, ctr testcontainers.Container) { + t.Helper() + if ctr == nil { + return + } ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - logs, err := cli.ContainerLogs(ctx, containerID, container.LogsOptions{ - ShowStdout: true, - ShowStderr: true, - }) + logs, err := ctr.Logs(ctx) if err != nil { - t.Logf("failed to fetch logs for container %s: %v", containerID, err) + t.Logf("failed to fetch coder logs: %v", err) return } defer func() { @@ -259,22 +198,12 @@ func dumpContainerLogs(t *testing.T, cli *client.Client, containerID string) { t.Logf("error closing container log reader: %v", err) } }() - var buf bytes.Buffer - if _, err := stdcopy.StdCopy(&buf, &buf, logs); err != nil { - t.Logf("failed to read logs for container %s: %v", containerID, err) + // testcontainers already demultiplexes the stream for non-TTY containers, + // so read the plain log output directly. + out, err := io.ReadAll(logs) + if err != nil { + t.Logf("failed to read coder logs: %v", err) return } - t.Logf("=== coder container %s logs ===\n%s=== end coder container logs ===", containerID, buf.String()) -} - -// randomPort is a helper function to find a free random port. -// Note that the OS may reallocate the port very quickly, so -// this is not _guaranteed_. -func randomPort(t *testing.T) int { - random, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err, "failed to listen on localhost") - _ = random.Close() - tcpAddr, valid := random.Addr().(*net.TCPAddr) - require.True(t, valid, "random port address is not a *net.TCPAddr?!") - return tcpAddr.Port + t.Logf("=== coder container logs ===\n%s=== end coder container logs ===", out) }