build: upgrade dependencies and build with go 1.21
This commit is contained in:
3
vendor/github.com/testcontainers/testcontainers-go/.gitignore
generated
vendored
3
vendor/github.com/testcontainers/testcontainers-go/.gitignore
generated
vendored
@@ -8,3 +8,6 @@ site/
|
||||
src/mkdocs-codeinclude-plugin
|
||||
src/pip-delete-this-directory.txt
|
||||
.idea/
|
||||
.DS_Store
|
||||
|
||||
TEST-*.xml
|
||||
|
16
vendor/github.com/testcontainers/testcontainers-go/.golangci.yml
generated
vendored
Normal file
16
vendor/github.com/testcontainers/testcontainers-go/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
linters:
|
||||
enable:
|
||||
- gci
|
||||
- gofmt
|
||||
- misspell
|
||||
|
||||
|
||||
linters-settings:
|
||||
gci:
|
||||
sections:
|
||||
- standard
|
||||
- default
|
||||
- prefix(github.com/testcontainters)
|
||||
|
||||
run:
|
||||
timeout: 5m
|
25
vendor/github.com/testcontainers/testcontainers-go/.travis.yml
generated
vendored
25
vendor/github.com/testcontainers/testcontainers-go/.travis.yml
generated
vendored
@@ -1,25 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- stable
|
||||
- 1.x
|
||||
- 1.14.x
|
||||
- 1.15.x
|
||||
|
||||
install: true
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
before_script:
|
||||
- go get gotest.tools/gotestsum
|
||||
|
||||
script:
|
||||
- go mod verify
|
||||
- go mod tidy
|
||||
- scripts/checks.sh
|
||||
- go vet ./...
|
||||
- gotestsum --format short-verbose ./...
|
||||
|
13
vendor/github.com/testcontainers/testcontainers-go/CONTRIBUTING.md
generated
vendored
Normal file
13
vendor/github.com/testcontainers/testcontainers-go/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Contributing
|
||||
|
||||
Please see the [main contributing guidelines](./docs/contributing.md).
|
||||
|
||||
There are additional docs describing [contributing documentation changes](./docs/contributing_docs.md).
|
||||
|
||||
### GitHub Sponsorship
|
||||
|
||||
Testcontainers is [in the GitHub Sponsors program](https://github.com/sponsors/testcontainers)!
|
||||
|
||||
This repository is supported by our sponsors, meaning that issues are eligible to have a 'bounty' attached to them by sponsors.
|
||||
|
||||
Please see [the bounty policy page](https://golang.testcontainers.org/bounty) if you are interested, either as a sponsor or as a contributor.
|
9
vendor/github.com/testcontainers/testcontainers-go/Makefile
generated
vendored
Normal file
9
vendor/github.com/testcontainers/testcontainers-go/Makefile
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
include ./commons-test.mk
|
||||
|
||||
.PHONY: test-all
|
||||
test-all: tools test-unit
|
||||
|
||||
.PHONY: test-examples
|
||||
test-examples:
|
||||
@echo "Running example tests..."
|
||||
make -C examples test
|
1
vendor/github.com/testcontainers/testcontainers-go/Pipfile
generated
vendored
1
vendor/github.com/testcontainers/testcontainers-go/Pipfile
generated
vendored
@@ -7,6 +7,7 @@ verify_ssl = true
|
||||
|
||||
[packages]
|
||||
mkdocs = "*"
|
||||
mkdocs-codeinclude-plugin = "*"
|
||||
mkdocs-material = "*"
|
||||
mkdocs-markdownextradata-plugin = "*"
|
||||
|
||||
|
327
vendor/github.com/testcontainers/testcontainers-go/Pipfile.lock
generated
vendored
327
vendor/github.com/testcontainers/testcontainers-go/Pipfile.lock
generated
vendored
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "39d6d886946223291b5b9c8ee4769e8c9eec313f1e82a02a21dacc890d507fe3"
|
||||
"sha256": "d3830267ea93391a077a2ba5b93aa8218c0a38002694b404b56df11498da3e56"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -18,159 +18,288 @@
|
||||
"default": {
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
|
||||
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
|
||||
"sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd",
|
||||
"sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"
|
||||
],
|
||||
"version": "==7.1.2"
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==8.1.6"
|
||||
},
|
||||
"ghp-import": {
|
||||
"hashes": [
|
||||
"sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619",
|
||||
"sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"
|
||||
],
|
||||
"version": "==2.1.0"
|
||||
},
|
||||
"importlib-metadata": {
|
||||
"hashes": [
|
||||
"sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f",
|
||||
"sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"
|
||||
"sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4",
|
||||
"sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"
|
||||
],
|
||||
"markers": "python_version < '3.8'",
|
||||
"version": "==1.6.0"
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==6.7.0"
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
|
||||
"sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"
|
||||
"sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8",
|
||||
"sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"
|
||||
],
|
||||
"version": "==2.11.2"
|
||||
},
|
||||
"livereload": {
|
||||
"hashes": [
|
||||
"sha256:78d55f2c268a8823ba499305dcac64e28ddeb9a92571e12d543cd304faf5817b",
|
||||
"sha256:89254f78d7529d7ea0a3417d224c34287ebfe266b05e67e51facaf82c27f0f66"
|
||||
],
|
||||
"version": "==2.6.1"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.0.3"
|
||||
},
|
||||
"markdown": {
|
||||
"hashes": [
|
||||
"sha256:1fafe3f1ecabfb514a5285fca634a53c1b32a81cb0feb154264d55bf2ff22c17",
|
||||
"sha256:c467cd6233885534bf0fe96e62e3cf46cfc1605112356c4f9981512b8174de59"
|
||||
"sha256:225c6123522495d4119a90b3a3ba31a1e87a70369e03f14799ea9c0d7183a3d6",
|
||||
"sha256:a4c1b65c0957b4bd9e7d86ddc7b3c9868fb9670660f6f99f6d1bca8954d5a941"
|
||||
],
|
||||
"version": "==3.2.2"
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.4.4"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
|
||||
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
|
||||
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
|
||||
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
|
||||
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
|
||||
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
|
||||
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
|
||||
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
|
||||
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
|
||||
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
|
||||
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
|
||||
"sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b",
|
||||
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
|
||||
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
|
||||
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
|
||||
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
|
||||
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
|
||||
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
|
||||
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
|
||||
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
|
||||
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
|
||||
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
|
||||
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
|
||||
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
|
||||
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
|
||||
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
|
||||
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
|
||||
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
|
||||
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
|
||||
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
|
||||
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
|
||||
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
|
||||
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"
|
||||
"sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e",
|
||||
"sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e",
|
||||
"sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431",
|
||||
"sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686",
|
||||
"sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559",
|
||||
"sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc",
|
||||
"sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c",
|
||||
"sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0",
|
||||
"sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4",
|
||||
"sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9",
|
||||
"sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575",
|
||||
"sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba",
|
||||
"sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d",
|
||||
"sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3",
|
||||
"sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00",
|
||||
"sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155",
|
||||
"sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac",
|
||||
"sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52",
|
||||
"sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f",
|
||||
"sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8",
|
||||
"sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b",
|
||||
"sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24",
|
||||
"sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea",
|
||||
"sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198",
|
||||
"sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0",
|
||||
"sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee",
|
||||
"sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be",
|
||||
"sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2",
|
||||
"sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707",
|
||||
"sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6",
|
||||
"sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58",
|
||||
"sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779",
|
||||
"sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636",
|
||||
"sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c",
|
||||
"sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad",
|
||||
"sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee",
|
||||
"sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc",
|
||||
"sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2",
|
||||
"sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48",
|
||||
"sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7",
|
||||
"sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e",
|
||||
"sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b",
|
||||
"sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa",
|
||||
"sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5",
|
||||
"sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e",
|
||||
"sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb",
|
||||
"sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9",
|
||||
"sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57",
|
||||
"sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc",
|
||||
"sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"
|
||||
],
|
||||
"version": "==1.1.1"
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.1.3"
|
||||
},
|
||||
"mergedeep": {
|
||||
"hashes": [
|
||||
"sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8",
|
||||
"sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==1.3.4"
|
||||
},
|
||||
"mkdocs": {
|
||||
"hashes": [
|
||||
"sha256:17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939",
|
||||
"sha256:8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"
|
||||
"sha256:89f5a094764381cda656af4298727c9f53dc3e602983087e1fe96ea1df24f4c1",
|
||||
"sha256:a1fa8c2d0c1305d7fc2b9d9f607c71778572a8b110fb26642aa00296c9e6d072"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.4"
|
||||
"version": "==1.2.3"
|
||||
},
|
||||
"mkdocs-codeinclude-plugin": {
|
||||
"hashes": [
|
||||
"sha256:172a917c9b257fa62850b669336151f85d3cd40312b2b52520cbcceab557ea6c",
|
||||
"sha256:305387f67a885f0e36ec1cf977324fe1fe50d31301147194b63631d0864601b1"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.2.1"
|
||||
},
|
||||
"mkdocs-markdownextradata-plugin": {
|
||||
"hashes": [
|
||||
"sha256:64d1c966b288d653f51f7531c03204eb988d0d77e56055c9d703d99105259a36"
|
||||
"sha256:9c562e8fe375647d5692d11dfe369a7bdd50302174d35995fce2aeca58036ec6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.0.5"
|
||||
"version": "==0.2.5"
|
||||
},
|
||||
"mkdocs-material": {
|
||||
"hashes": [
|
||||
"sha256:524debb6ee8ee89cee08886f2a67c3c3875c0ee9579c598d7448cbd2607cd3b7",
|
||||
"sha256:62ae84082fa9f077c86b7db63e7bedf392005041b451defc850f8d0887a11e91"
|
||||
"sha256:20c13aa0a54841e1f1c080edb0e3573407884e4abea51ee25573061189bec83e",
|
||||
"sha256:3314d94ccc11481b1a3aa4f7babb4fb2bc47daa2fa8ace2463665952116f409b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.2.0"
|
||||
"version": "==8.2.7"
|
||||
},
|
||||
"mkdocs-material-extensions": {
|
||||
"hashes": [
|
||||
"sha256:9c003da71e2cc2493d910237448c672e00cefc800d3d6ae93d2fc69979e3bd93",
|
||||
"sha256:e41d9f38e4798b6617ad98ca8f7f1157b1e4385ac1459ca1e4ea219b556df945"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.1.1"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61",
|
||||
"sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==23.1"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44",
|
||||
"sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"
|
||||
"sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692",
|
||||
"sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"
|
||||
],
|
||||
"version": "==2.6.1"
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.16.1"
|
||||
},
|
||||
"pymdown-extensions": {
|
||||
"hashes": [
|
||||
"sha256:5bf93d1ccd8281948cd7c559eb363e59b179b5373478e8a7195cf4b78e3c11b6",
|
||||
"sha256:8f415b21ee86d80bb2c3676f4478b274d0a8ccb13af672a4c86b9ffd22bd005c"
|
||||
"sha256:508009b211373058debb8247e168de4cbcb91b1bff7b5e961b2c3e864e00b195",
|
||||
"sha256:ef25dbbae530e8f67575d222b75ff0649b1e841e22c2ae9a20bad9472c2207dc"
|
||||
],
|
||||
"version": "==7.1"
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==10.1"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
|
||||
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.8.2"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c",
|
||||
"sha256:436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95",
|
||||
"sha256:460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2",
|
||||
"sha256:5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4",
|
||||
"sha256:7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad",
|
||||
"sha256:9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba",
|
||||
"sha256:a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1",
|
||||
"sha256:aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e",
|
||||
"sha256:c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673",
|
||||
"sha256:c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13",
|
||||
"sha256:e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19"
|
||||
"sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc",
|
||||
"sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741",
|
||||
"sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206",
|
||||
"sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27",
|
||||
"sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595",
|
||||
"sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62",
|
||||
"sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98",
|
||||
"sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696",
|
||||
"sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d",
|
||||
"sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867",
|
||||
"sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47",
|
||||
"sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486",
|
||||
"sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6",
|
||||
"sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3",
|
||||
"sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007",
|
||||
"sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938",
|
||||
"sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c",
|
||||
"sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735",
|
||||
"sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d",
|
||||
"sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba",
|
||||
"sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8",
|
||||
"sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5",
|
||||
"sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd",
|
||||
"sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3",
|
||||
"sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0",
|
||||
"sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515",
|
||||
"sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c",
|
||||
"sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c",
|
||||
"sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924",
|
||||
"sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34",
|
||||
"sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43",
|
||||
"sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859",
|
||||
"sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673",
|
||||
"sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a",
|
||||
"sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab",
|
||||
"sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa",
|
||||
"sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c",
|
||||
"sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585",
|
||||
"sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d",
|
||||
"sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.1"
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==6.0.1"
|
||||
},
|
||||
"pyyaml-env-tag": {
|
||||
"hashes": [
|
||||
"sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb",
|
||||
"sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==0.1"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
|
||||
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||
],
|
||||
"version": "==1.14.0"
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"tornado": {
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc",
|
||||
"sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52",
|
||||
"sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6",
|
||||
"sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d",
|
||||
"sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b",
|
||||
"sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673",
|
||||
"sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9",
|
||||
"sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a",
|
||||
"sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740"
|
||||
"sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36",
|
||||
"sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"
|
||||
],
|
||||
"version": "==6.0.4"
|
||||
"markers": "python_version < '3.8'",
|
||||
"version": "==4.7.1"
|
||||
},
|
||||
"watchdog": {
|
||||
"hashes": [
|
||||
"sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a",
|
||||
"sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100",
|
||||
"sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8",
|
||||
"sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc",
|
||||
"sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae",
|
||||
"sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41",
|
||||
"sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0",
|
||||
"sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f",
|
||||
"sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c",
|
||||
"sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9",
|
||||
"sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3",
|
||||
"sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709",
|
||||
"sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83",
|
||||
"sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759",
|
||||
"sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9",
|
||||
"sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3",
|
||||
"sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7",
|
||||
"sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f",
|
||||
"sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346",
|
||||
"sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674",
|
||||
"sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397",
|
||||
"sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96",
|
||||
"sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d",
|
||||
"sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a",
|
||||
"sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64",
|
||||
"sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44",
|
||||
"sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.0.0"
|
||||
},
|
||||
"zipp": {
|
||||
"hashes": [
|
||||
"sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b",
|
||||
"sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"
|
||||
"sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b",
|
||||
"sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"
|
||||
],
|
||||
"version": "==3.1.0"
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.15.0"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
|
77
vendor/github.com/testcontainers/testcontainers-go/README.md
generated
vendored
77
vendor/github.com/testcontainers/testcontainers-go/README.md
generated
vendored
@@ -1,70 +1,17 @@
|
||||
[](https://travis-ci.org/testcontainers/testcontainers-go)
|
||||
# Testcontainers
|
||||
|
||||
[](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=141451032&machine=standardLinux32gb&devcontainer_path=.devcontainer%2Fdevcontainer.json&location=EastUs)
|
||||
|
||||
[](https://github.com/testcontainers/testcontainers-go/actions/workflows/ci.yml)
|
||||
[](https://goreportcard.com/report/github.com/testcontainers/testcontainers-go)
|
||||
[](https://godoc.org/github.com/testcontainers/testcontainers-go)
|
||||
[](https://pkg.go.dev/github.com/testcontainers/testcontainers-go)
|
||||
|
||||
_Testcontainers for Go_ is a Go package that makes it simple to create and clean up container-based dependencies for
|
||||
automated integration/smoke tests. The clean, easy-to-use API enables developers to programmatically define containers
|
||||
that should be run as part of a test and clean up those resources when the test is done.
|
||||
|
||||
When I was working on a Zipkin PR I discovered a nice Java library called
|
||||
[Testcontainers](https://www.testcontainers.org/).
|
||||
You can find more information about _Testcontainers for Go_ at [golang.testcontainers.org](https://golang.testcontainers.org), which is rendered from the [./docs](./docs) directory.
|
||||
|
||||
It provides an easy and clean API over the go docker sdk to run, terminate and
|
||||
connect to containers in your tests.
|
||||
## Using _Testcontainers for Go_
|
||||
|
||||
I found myself comfortable programmatically writing the containers I need to run
|
||||
an integration/smoke tests. So I started porting this library in Go.
|
||||
|
||||
This is an example:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
)
|
||||
|
||||
func TestNginxLatestReturn(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: "nginx",
|
||||
ExposedPorts: []string{"80/tcp"},
|
||||
WaitingFor: wait.ForHTTP("/"),
|
||||
}
|
||||
nginxC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: req,
|
||||
Started: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer nginxC.Terminate(ctx)
|
||||
ip, err := nginxC.Host(ctx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
port, err := nginxC.MappedPort(ctx, "80")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
resp, err := http.Get(fmt.Sprintf("http://%s:%s", ip, port.Port()))
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Errorf("Expected status code %d. Got %d.", http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
}
|
||||
```
|
||||
This is a simple example, you can create one container in my case using the
|
||||
`nginx` image. You can get its IP `ip, err := nginxC.GetContainerIpAddress(ctx)` and you
|
||||
can use it to make a GET: `resp, err := http.Get(fmt.Sprintf("http://%s", ip))`
|
||||
|
||||
To clean your environment you can defer the container termination `defer
|
||||
nginxC.Terminate(ctx, t)`. `t` is `*testing.T` and it is used to notify is the
|
||||
`defer` failed marking the test as failed.
|
||||
|
||||
## Documentation
|
||||
|
||||
The documentation lives in [./docs](./docs) and it is rendered at
|
||||
[golang.testcontainers.org](https://golang.testcontainers.org).
|
||||
Please visit [the quickstart guide](https://golang.testcontainers.org/quickstart) to understand how to add the dependency to your Go project.
|
||||
|
203
vendor/github.com/testcontainers/testcontainers-go/RELEASING.md
generated
vendored
Normal file
203
vendor/github.com/testcontainers/testcontainers-go/RELEASING.md
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
# Releasing Testcontainers for Go
|
||||
|
||||
In order to create a release, we have added a shell script that performs all the tasks for you, allowing a dry-run mode for checking it before creating the release. We are going to explain how to use it in this document.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
First, it's really important that you first check that the [version.go](./internal/version.go) file is up-to-date, containing the right version you want to create. That file will be used by the automation to perform the release.
|
||||
Once the version file is correct in the repository:
|
||||
|
||||
Second, check that the git remote for the `origin` is pointing to `github.com/testcontainers/testcontainers-go`. You can check it by running:
|
||||
|
||||
```shell
|
||||
git remote -v
|
||||
```
|
||||
|
||||
## Prepare the release
|
||||
|
||||
Once the remote is properly set, please follow these steps:
|
||||
|
||||
- Run the [pre-release.sh](./scripts/pre-release.sh) shell script to run it in dry-run mode.
|
||||
- You can use the `DRY_RUN` variable to enable or disable the dry-run mode. By default, it's enabled.
|
||||
- To prepare for a release, updating the _Testcontainers for Go_ dependency for all the modules and examples, without performing any Git operation:
|
||||
|
||||
DRY_RUN="false" ./scripts/pre-release.sh
|
||||
|
||||
- The script will update the [mkdocs.yml](./mkdocks.yml) file, updating the `latest_version` field to the current version.
|
||||
- The script will update the `go.mod` files for each Go modules and example modules under the examples and modules directories, updating the version of the testcontainers-go dependency to the recently created tag.
|
||||
- The script will modify the docs for the each Go module **that was not released yet**, updating the version of _Testcontainers for Go_ where it was added to the recently created tag.
|
||||
|
||||
An example execution, with dry-run mode enabled:
|
||||
|
||||
```shell
|
||||
sed "s/latest_version: .*/latest_version: v0.20.1/g" /Users/mdelapenya/sourcecode/src/github.com/testcontainers/testcontainers-go/mkdocs.yml > /Users/mdelapenya/sourcecode/src/github.com/testcontainers/testcontainers-go/mkdocs.yml.tmp
|
||||
mv /Users/mdelapenya/sourcecode/src/github.com/testcontainers/testcontainers-go/mkdocs.yml.tmp /Users/mdelapenya/sourcecode/src/github.com/testcontainers/testcontainers-go/mkdocs.yml
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" bigtable/go.mod > bigtable/go.mod.tmp
|
||||
mv bigtable/go.mod.tmp bigtable/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" cockroachdb/go.mod > cockroachdb/go.mod.tmp
|
||||
mv cockroachdb/go.mod.tmp cockroachdb/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" consul/go.mod > consul/go.mod.tmp
|
||||
mv consul/go.mod.tmp consul/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" datastore/go.mod > datastore/go.mod.tmp
|
||||
mv datastore/go.mod.tmp datastore/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" firestore/go.mod > firestore/go.mod.tmp
|
||||
mv firestore/go.mod.tmp firestore/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" mongodb/go.mod > mongodb/go.mod.tmp
|
||||
mv mongodb/go.mod.tmp mongodb/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" nginx/go.mod > nginx/go.mod.tmp
|
||||
mv nginx/go.mod.tmp nginx/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" pubsub/go.mod > pubsub/go.mod.tmp
|
||||
mv pubsub/go.mod.tmp pubsub/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" spanner/go.mod > spanner/go.mod.tmp
|
||||
mv spanner/go.mod.tmp spanner/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" toxiproxy/go.mod > toxiproxy/go.mod.tmp
|
||||
mv toxiproxy/go.mod.tmp toxiproxy/go.mod
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" compose/go.mod > compose/go.mod.tmp
|
||||
mv compose/go.mod.tmp compose/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" couchbase/go.mod > couchbase/go.mod.tmp
|
||||
mv couchbase/go.mod.tmp couchbase/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" localstack/go.mod > localstack/go.mod.tmp
|
||||
mv localstack/go.mod.tmp localstack/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" mysql/go.mod > mysql/go.mod.tmp
|
||||
mv mysql/go.mod.tmp mysql/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" neo4j/go.mod > neo4j/go.mod.tmp
|
||||
mv neo4j/go.mod.tmp neo4j/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" postgres/go.mod > postgres/go.mod.tmp
|
||||
mv postgres/go.mod.tmp postgres/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" pulsar/go.mod > pulsar/go.mod.tmp
|
||||
mv pulsar/go.mod.tmp pulsar/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" redis/go.mod > redis/go.mod.tmp
|
||||
mv redis/go.mod.tmp redis/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" redpanda/go.mod > redpanda/go.mod.tmp
|
||||
mv redpanda/go.mod.tmp redpanda/go.mod
|
||||
sed "s/testcontainers-go v.*/testcontainers-go v0.20.1/g" vault/go.mod > vault/go.mod.tmp
|
||||
mv vault/go.mod.tmp vault/go.mod
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
go mod tidy
|
||||
sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" couchbase.md > couchbase.md.tmp
|
||||
mv couchbase.md.tmp couchbase.md
|
||||
sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" localstack.md > localstack.md.tmp
|
||||
mv localstack.md.tmp localstack.md
|
||||
sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" mysql.md > mysql.md.tmp
|
||||
mv mysql.md.tmp mysql.md
|
||||
sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" neo4j.md > neo4j.md.tmp
|
||||
mv neo4j.md.tmp neo4j.md
|
||||
sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" postgres.md > postgres.md.tmp
|
||||
mv postgres.md.tmp postgres.md
|
||||
sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" pulsar.md > pulsar.md.tmp
|
||||
mv pulsar.md.tmp pulsar.md
|
||||
sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" redis.md > redis.md.tmp
|
||||
mv redis.md.tmp redis.md
|
||||
sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" redpanda.md > redpanda.md.tmp
|
||||
mv redpanda.md.tmp redpanda.md
|
||||
sed "s/Not available until the next release of testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\"><span class=\"tc-version\">:material-tag: main<\/span><\/a>/Since testcontainers-go <a href=\"https:\/\/github.com\/testcontainers\/testcontainers-go\/releases\/tag\/v0.20.1\"><span class=\"tc-version\">:material-tag: v0.20.1<\/span><\/a>/g" vault.md > vault.md.tmp
|
||||
mv vault.md.tmp vault.md
|
||||
```
|
||||
|
||||
## Performing a release
|
||||
|
||||
Once you are satisfied with the modified files in the git state:
|
||||
|
||||
- Run the [release.sh](./scripts/release.sh) shell script to create the release in dry-run mode.
|
||||
- You can use the `DRY_RUN` variable to enable or disable the dry-run mode. By default, it's enabled.
|
||||
|
||||
DRY_RUN="false" ./scripts/release.sh
|
||||
|
||||
- You can define the bump type, using the `BUMP_TYPE` environment variable. The default value is `minor`, but you can also use `major` or `patch` (the script will fail if the value is not one of these three):
|
||||
|
||||
BUMP_TYPE="major" ./scripts/release.sh
|
||||
|
||||
- The script will commit the current state of the git repository, if the `DRY_RUN` variable is set to `false`. The modified files are the ones modified by the `pre-release.sh` script.
|
||||
- The script will create a git tag with the current value of the [version.go](./internal/version.go) file, starting with `v`: e.g. `v0.18.0`, for the following Go modules:
|
||||
- the root module, representing the Testcontainers for Go library.
|
||||
- all the Go modules living in both the `examples` and `modules` directory. The git tag value for these Go modules will be created using this name convention:
|
||||
|
||||
"${directory}/${module_name}/${version}", e.g. "examples/mysql/v0.18.0", "modules/compose/v0.18.0"
|
||||
|
||||
- The script will update the [version.go](./internal/version.go) file, setting the next development version to the value defined in the `BUMP_TYPE` environment variable. For example, if the current version is `v0.18.0`, the script will update the [version.go](./internal/version.go) file with the next development version `v0.19.0`.
|
||||
- The script will create a commit in the **main** branch if the `DRY_RUN` variable is set to `false`.
|
||||
- The script will push the main branch including the tags to the upstream repository, https://github.com/testcontainers/testcontainers-go, if the `DRY_RUN` variable is set to `false`.
|
||||
- Finally, the script will trigger the Golang proxy to update the modules in https://proxy.golang.org/, if the `DRY_RUN` variable is set to `false`.
|
||||
|
||||
An example execution, with dry-run mode enabled:
|
||||
|
||||
```
|
||||
$ ./scripts/release.sh
|
||||
Current version: v0.20.1
|
||||
git add /Users/mdelapenya/sourcecode/src/github.com/testcontainers/testcontainers-go/internal/version.go
|
||||
git add /Users/mdelapenya/sourcecode/src/github.com/testcontainers/testcontainers-go/mkdocs.yml
|
||||
git add examples/**/go.*
|
||||
git add modules/**/go.*
|
||||
git commit -m chore: use new version (v0.20.1) in modules and examples
|
||||
git tag v0.20.1
|
||||
git tag examples/bigtable/v0.20.1
|
||||
git tag examples/cockroachdb/v0.20.1
|
||||
git tag examples/consul/v0.20.1
|
||||
git tag examples/datastore/v0.20.1
|
||||
git tag examples/firestore/v0.20.1
|
||||
git tag examples/mongodb/v0.20.1
|
||||
git tag examples/nginx/v0.20.1
|
||||
git tag examples/pubsub/v0.20.1
|
||||
git tag examples/spanner/v0.20.1
|
||||
git tag examples/toxiproxy/v0.20.1
|
||||
git tag modules/compose/v0.20.1
|
||||
git tag modules/couchbase/v0.20.1
|
||||
git tag modules/localstack/v0.20.1
|
||||
git tag modules/mysql/v0.20.1
|
||||
git tag modules/neo4j/v0.20.1
|
||||
git tag modules/postgres/v0.20.1
|
||||
git tag modules/pulsar/v0.20.1
|
||||
git tag modules/redis/v0.20.1
|
||||
git tag modules/redpanda/v0.20.1
|
||||
git tag modules/vault/v0.20.1
|
||||
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
|
||||
Producing a minor bump of the version, from 0.20.1 to 0.21.0
|
||||
sed "s/const Version = ".*"/const Version = "0.21.0"/g" /Users/mdelapenya/sourcecode/src/github.com/testcontainers/testcontainers-go/internal/version.go > /Users/mdelapenya/sourcecode/src/github.com/testcontainers/testcontainers-go/internal/version.go.tmp
|
||||
mv /Users/mdelapenya/sourcecode/src/github.com/testcontainers/testcontainers-go/internal/version.go.tmp /Users/mdelapenya/sourcecode/src/github.com/testcontainers/testcontainers-go/internal/version.go
|
||||
git add /Users/mdelapenya/sourcecode/src/github.com/testcontainers/testcontainers-go/internal/version.go
|
||||
git commit -m chore: prepare for next minor development cycle (0.21.0)
|
||||
git push origin main --tags
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/examples/bigtable/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/examples/cockroachdb/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/examples/consul/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/examples/datastore/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/examples/firestore/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/examples/mongodb/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/examples/nginx/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/examples/pubsub/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/examples/spanner/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/examples/toxiproxy/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/modules/compose/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/modules/couchbase/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/modules/localstack/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/modules/mysql/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/modules/neo4j/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/modules/postgres/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/modules/pulsar/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/modules/redis/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/modules/redpanda/@v/v0.20.1.info
|
||||
curl https://proxy.golang.org/github.com/testcontainers/testcontainers-go/modules/vault/@v/v0.20.1.info
|
||||
```
|
||||
|
||||
Right after that, you have to:
|
||||
- Verify that the commits are in the upstream repository, otherwise, update it with the current state of the main branch.
|
23
vendor/github.com/testcontainers/testcontainers-go/commons-test.mk
generated
vendored
Normal file
23
vendor/github.com/testcontainers/testcontainers-go/commons-test.mk
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
.PHONY: dependencies-scan
|
||||
dependencies-scan:
|
||||
@echo ">> Scanning dependencies in $(CURDIR)..."
|
||||
go list -json -m all | docker run --rm -i sonatypecommunity/nancy:latest sleuth --skip-update-check
|
||||
|
||||
.PHONY: test-%
|
||||
test-%:
|
||||
@echo "Running $* tests..."
|
||||
gotestsum \
|
||||
--format short-verbose \
|
||||
--rerun-fails=5 \
|
||||
--packages="./..." \
|
||||
--junitfile TEST-$*.xml \
|
||||
-- \
|
||||
-timeout=30m
|
||||
|
||||
.PHONY: tools
|
||||
tools:
|
||||
go mod download
|
||||
|
||||
.PHONY: tools-tidy
|
||||
tools-tidy:
|
||||
go mod tidy
|
269
vendor/github.com/testcontainers/testcontainers-go/compose.go
generated
vendored
269
vendor/github.com/testcontainers/testcontainers-go/compose.go
generated
vendored
@@ -1,269 +0,0 @@
|
||||
package testcontainers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
envProjectName = "COMPOSE_PROJECT_NAME"
|
||||
envComposeFile = "COMPOSE_FILE"
|
||||
)
|
||||
|
||||
// DockerCompose defines the contract for running Docker Compose
|
||||
type DockerCompose interface {
|
||||
Down() ExecError
|
||||
Invoke() ExecError
|
||||
WithCommand([]string) DockerCompose
|
||||
WithEnv(map[string]string) DockerCompose
|
||||
}
|
||||
|
||||
// LocalDockerCompose represents a Docker Compose execution using local binary
|
||||
// docker-compose or docker-compose.exe, depending on the underlying platform
|
||||
type LocalDockerCompose struct {
|
||||
Executable string
|
||||
ComposeFilePaths []string
|
||||
absComposeFilePaths []string
|
||||
Identifier string
|
||||
Cmd []string
|
||||
Env map[string]string
|
||||
Services map[string]interface{}
|
||||
}
|
||||
|
||||
// NewLocalDockerCompose returns an instance of the local Docker Compose, using an
|
||||
// array of Docker Compose file paths and an identifier for the Compose execution.
|
||||
//
|
||||
// It will iterate through the array adding '-f compose-file-path' flags to the local
|
||||
// Docker Compose execution. The identifier represents the name of the execution,
|
||||
// which will define the name of the underlying Docker network and the name of the
|
||||
// running Compose services.
|
||||
func NewLocalDockerCompose(filePaths []string, identifier string) *LocalDockerCompose {
|
||||
dc := &LocalDockerCompose{}
|
||||
|
||||
dc.Executable = "docker-compose"
|
||||
if runtime.GOOS == "windows" {
|
||||
dc.Executable = "docker-compose.exe"
|
||||
}
|
||||
|
||||
dc.ComposeFilePaths = filePaths
|
||||
|
||||
dc.absComposeFilePaths = make([]string, len(filePaths))
|
||||
for i, cfp := range dc.ComposeFilePaths {
|
||||
abs, _ := filepath.Abs(cfp)
|
||||
dc.absComposeFilePaths[i] = abs
|
||||
}
|
||||
|
||||
dc.validate()
|
||||
|
||||
dc.Identifier = strings.ToLower(identifier)
|
||||
|
||||
return dc
|
||||
}
|
||||
|
||||
// Down executes docker-compose down
|
||||
func (dc *LocalDockerCompose) Down() ExecError {
|
||||
return executeCompose(dc, []string{"down"})
|
||||
}
|
||||
|
||||
func (dc *LocalDockerCompose) getDockerComposeEnvironment() map[string]string {
|
||||
environment := map[string]string{}
|
||||
|
||||
composeFileEnvVariableValue := ""
|
||||
for _, abs := range dc.absComposeFilePaths {
|
||||
composeFileEnvVariableValue += abs + string(os.PathListSeparator)
|
||||
}
|
||||
|
||||
environment[envProjectName] = dc.Identifier
|
||||
environment[envComposeFile] = composeFileEnvVariableValue
|
||||
|
||||
return environment
|
||||
}
|
||||
|
||||
// Invoke invokes the docker compose
|
||||
func (dc *LocalDockerCompose) Invoke() ExecError {
|
||||
return executeCompose(dc, dc.Cmd)
|
||||
}
|
||||
|
||||
// WithCommand assigns the command
|
||||
func (dc *LocalDockerCompose) WithCommand(cmd []string) DockerCompose {
|
||||
dc.Cmd = cmd
|
||||
return dc
|
||||
}
|
||||
|
||||
// WithEnv assigns the environment
|
||||
func (dc *LocalDockerCompose) WithEnv(env map[string]string) DockerCompose {
|
||||
dc.Env = env
|
||||
return dc
|
||||
}
|
||||
|
||||
// validate checks if the files to be run in the compose are valid YAML files, setting up
|
||||
// references to all services in them
|
||||
func (dc *LocalDockerCompose) validate() error {
|
||||
type compose struct {
|
||||
Services map[string]interface{}
|
||||
}
|
||||
|
||||
for _, abs := range dc.absComposeFilePaths {
|
||||
c := compose{}
|
||||
|
||||
yamlFile, err := ioutil.ReadFile(abs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = yaml.Unmarshal(yamlFile, &c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dc.Services = c.Services
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecError is super struct that holds any information about an execution error, so the client code
|
||||
// can handle the result
|
||||
type ExecError struct {
|
||||
Command []string
|
||||
Error error
|
||||
Stdout error
|
||||
Stderr error
|
||||
}
|
||||
|
||||
// execute executes a program with arguments and environment variables inside a specific directory
|
||||
func execute(
|
||||
dirContext string, environment map[string]string, binary string, args []string) ExecError {
|
||||
|
||||
var errStdout, errStderr error
|
||||
|
||||
cmd := exec.Command(binary, args...)
|
||||
cmd.Dir = dirContext
|
||||
cmd.Env = os.Environ()
|
||||
|
||||
for key, value := range environment {
|
||||
cmd.Env = append(cmd.Env, key+"="+value)
|
||||
}
|
||||
|
||||
stdoutIn, _ := cmd.StdoutPipe()
|
||||
stderrIn, _ := cmd.StderrPipe()
|
||||
|
||||
stdout := newCapturingPassThroughWriter(os.Stdout)
|
||||
stderr := newCapturingPassThroughWriter(os.Stderr)
|
||||
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
execCmd := []string{"Starting command", dirContext, binary}
|
||||
execCmd = append(execCmd, args...)
|
||||
|
||||
return ExecError{
|
||||
// add information about the CMD and arguments used
|
||||
Command: execCmd,
|
||||
Error: err,
|
||||
Stderr: errStderr,
|
||||
Stdout: errStdout,
|
||||
}
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
_, errStdout = io.Copy(stdout, stdoutIn)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
_, errStderr = io.Copy(stderr, stderrIn)
|
||||
wg.Wait()
|
||||
|
||||
err = cmd.Wait()
|
||||
|
||||
execCmd := []string{"Reading std", dirContext, binary}
|
||||
execCmd = append(execCmd, args...)
|
||||
|
||||
return ExecError{
|
||||
Command: execCmd,
|
||||
Error: err,
|
||||
Stderr: errStderr,
|
||||
Stdout: errStdout,
|
||||
}
|
||||
}
|
||||
|
||||
func executeCompose(dc *LocalDockerCompose, args []string) ExecError {
|
||||
if which(dc.Executable) != nil {
|
||||
return ExecError{
|
||||
Command: []string{dc.Executable},
|
||||
Error: fmt.Errorf("Local Docker Compose not found. Is %s on the PATH?", dc.Executable),
|
||||
}
|
||||
}
|
||||
|
||||
environment := dc.getDockerComposeEnvironment()
|
||||
for k, v := range dc.Env {
|
||||
environment[k] = v
|
||||
}
|
||||
|
||||
cmds := []string{}
|
||||
pwd := "."
|
||||
if len(dc.absComposeFilePaths) > 0 {
|
||||
pwd, _ = filepath.Split(dc.absComposeFilePaths[0])
|
||||
|
||||
for _, abs := range dc.absComposeFilePaths {
|
||||
cmds = append(cmds, "-f", abs)
|
||||
}
|
||||
} else {
|
||||
cmds = append(cmds, "-f", "docker-compose.yml")
|
||||
}
|
||||
cmds = append(cmds, args...)
|
||||
|
||||
execErr := execute(pwd, environment, dc.Executable, cmds)
|
||||
err := execErr.Error
|
||||
if err != nil {
|
||||
args := strings.Join(dc.Cmd, " ")
|
||||
return ExecError{
|
||||
Command: []string{dc.Executable},
|
||||
Error: fmt.Errorf("Local Docker compose exited abnormally whilst running %s: [%v]. %s", dc.Executable, args, err.Error()),
|
||||
}
|
||||
}
|
||||
|
||||
return execErr
|
||||
}
|
||||
|
||||
// capturingPassThroughWriter is a writer that remembers
|
||||
// data written to it and passes it to w
|
||||
type capturingPassThroughWriter struct {
|
||||
buf bytes.Buffer
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
// newCapturingPassThroughWriter creates new capturingPassThroughWriter
|
||||
func newCapturingPassThroughWriter(w io.Writer) *capturingPassThroughWriter {
|
||||
return &capturingPassThroughWriter{
|
||||
w: w,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *capturingPassThroughWriter) Write(d []byte) (int, error) {
|
||||
w.buf.Write(d)
|
||||
return w.w.Write(d)
|
||||
}
|
||||
|
||||
// Bytes returns bytes written to the writer
|
||||
func (w *capturingPassThroughWriter) Bytes() []byte {
|
||||
return w.buf.Bytes()
|
||||
}
|
||||
|
||||
// Which checks if a binary is present in PATH
|
||||
func which(binary string) error {
|
||||
_, err := exec.LookPath(binary)
|
||||
|
||||
return err
|
||||
}
|
29
vendor/github.com/testcontainers/testcontainers-go/config.go
generated
vendored
Normal file
29
vendor/github.com/testcontainers/testcontainers-go/config.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package testcontainers
|
||||
|
||||
import (
|
||||
"github.com/testcontainers/testcontainers-go/internal/config"
|
||||
)
|
||||
|
||||
// TestcontainersConfig represents the configuration for Testcontainers
|
||||
type TestcontainersConfig struct {
|
||||
Host string `properties:"docker.host,default="` // Deprecated: use Config.Host instead
|
||||
TLSVerify int `properties:"docker.tls.verify,default=0"` // Deprecated: use Config.TLSVerify instead
|
||||
CertPath string `properties:"docker.cert.path,default="` // Deprecated: use Config.CertPath instead
|
||||
RyukDisabled bool `properties:"ryuk.disabled,default=false"` // Deprecated: use Config.RyukDisabled instead
|
||||
RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"` // Deprecated: use Config.RyukPrivileged instead
|
||||
Config config.Config
|
||||
}
|
||||
|
||||
// ReadConfig reads from testcontainers properties file, storing the result in a singleton instance
|
||||
// of the TestcontainersConfig struct
|
||||
func ReadConfig() TestcontainersConfig {
|
||||
cfg := config.Read()
|
||||
return TestcontainersConfig{
|
||||
Host: cfg.Host,
|
||||
TLSVerify: cfg.TLSVerify,
|
||||
CertPath: cfg.CertPath,
|
||||
RyukDisabled: cfg.RyukDisabled,
|
||||
RyukPrivileged: cfg.RyukPrivileged,
|
||||
Config: cfg,
|
||||
}
|
||||
}
|
209
vendor/github.com/testcontainers/testcontainers-go/container.go
generated
vendored
209
vendor/github.com/testcontainers/testcontainers-go/container.go
generated
vendored
@@ -2,14 +2,20 @@ package testcontainers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
tcexec "github.com/testcontainers/testcontainers-go/exec"
|
||||
"github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
)
|
||||
|
||||
@@ -22,13 +28,6 @@ type DeprecatedContainer interface {
|
||||
Terminate(ctx context.Context) error
|
||||
}
|
||||
|
||||
// ContainerProvider allows the creation of containers on an arbitrary system
|
||||
type ContainerProvider interface {
|
||||
CreateContainer(context.Context, ContainerRequest) (Container, error) // create a container without starting it
|
||||
RunContainer(context.Context, ContainerRequest) (Container, error) // create a container and start it
|
||||
Health(context.Context) error
|
||||
}
|
||||
|
||||
// Container allows getting info about and controlling a single container instance
|
||||
type Container interface {
|
||||
GetContainerID() string // get the container id from the provider
|
||||
@@ -38,89 +37,124 @@ type Container interface {
|
||||
MappedPort(context.Context, nat.Port) (nat.Port, error) // get externally mapped port for a container port
|
||||
Ports(context.Context) (nat.PortMap, error) // get all exposed ports
|
||||
SessionID() string // get session id
|
||||
Start(context.Context) error // start the container
|
||||
Terminate(context.Context) error // terminate the container
|
||||
Logs(context.Context) (io.ReadCloser, error) // Get logs of the container
|
||||
IsRunning() bool
|
||||
Start(context.Context) error // start the container
|
||||
Stop(context.Context, *time.Duration) error // stop the container
|
||||
Terminate(context.Context) error // terminate the container
|
||||
Logs(context.Context) (io.ReadCloser, error) // Get logs of the container
|
||||
FollowOutput(LogConsumer)
|
||||
StartLogProducer(context.Context) error
|
||||
StopLogProducer() error
|
||||
Name(context.Context) (string, error) // get container name
|
||||
State(context.Context) (*types.ContainerState, error) // returns container's running state
|
||||
Networks(context.Context) ([]string, error) // get container networks
|
||||
NetworkAliases(context.Context) (map[string][]string, error) // get container network aliases for a network
|
||||
Exec(ctx context.Context, cmd []string) (int, error)
|
||||
ContainerIP(context.Context) (string, error) // get container ip
|
||||
Exec(ctx context.Context, cmd []string, options ...tcexec.ProcessOption) (int, io.Reader, error)
|
||||
ContainerIP(context.Context) (string, error) // get container ip
|
||||
ContainerIPs(context.Context) ([]string, error) // get all container IPs
|
||||
CopyToContainer(ctx context.Context, fileContent []byte, containerFilePath string, fileMode int64) error
|
||||
CopyDirToContainer(ctx context.Context, hostDirPath string, containerParentPath string, fileMode int64) error
|
||||
CopyFileToContainer(ctx context.Context, hostFilePath string, containerFilePath string, fileMode int64) error
|
||||
CopyFileFromContainer(ctx context.Context, filePath string) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// ImageBuildInfo defines what is needed to build an image
|
||||
type ImageBuildInfo interface {
|
||||
GetContext() (io.Reader, error) // the path to the build context
|
||||
GetDockerfile() string // the relative path to the Dockerfile, including the fileitself
|
||||
ShouldBuildImage() bool // return true if the image needs to be built
|
||||
GetContext() (io.Reader, error) // the path to the build context
|
||||
GetDockerfile() string // the relative path to the Dockerfile, including the fileitself
|
||||
ShouldPrintBuildLog() bool // allow build log to be printed to stdout
|
||||
ShouldBuildImage() bool // return true if the image needs to be built
|
||||
GetBuildArgs() map[string]*string // return the environment args used to build the from Dockerfile
|
||||
GetAuthConfigs() map[string]registry.AuthConfig // return the auth configs to be able to pull from an authenticated docker registry
|
||||
}
|
||||
|
||||
// FromDockerfile represents the parameters needed to build an image from a Dockerfile
|
||||
// rather than using a pre-built one
|
||||
type FromDockerfile struct {
|
||||
Context string // the path to the context of of the docker build
|
||||
ContextArchive io.Reader // the tar archive file to send to docker that contains the build context
|
||||
Dockerfile string // the path from the context to the Dockerfile for the image, defaults to "Dockerfile"
|
||||
Context string // the path to the context of of the docker build
|
||||
ContextArchive io.Reader // the tar archive file to send to docker that contains the build context
|
||||
Dockerfile string // the path from the context to the Dockerfile for the image, defaults to "Dockerfile"
|
||||
BuildArgs map[string]*string // enable user to pass build args to docker daemon
|
||||
PrintBuildLog bool // enable user to print build log
|
||||
AuthConfigs map[string]registry.AuthConfig // Deprecated. Testcontainers will detect registry credentials automatically. Enable auth configs to be able to pull from an authenticated docker registry
|
||||
}
|
||||
|
||||
type ContainerFile struct {
|
||||
HostFilePath string
|
||||
ContainerFilePath string
|
||||
FileMode int64
|
||||
}
|
||||
|
||||
// ContainerRequest represents the parameters used to get a running container
|
||||
type ContainerRequest struct {
|
||||
FromDockerfile
|
||||
Image string
|
||||
Entrypoint []string
|
||||
Env map[string]string
|
||||
ExposedPorts []string // allow specifying protocol info
|
||||
Cmd []string
|
||||
Labels map[string]string
|
||||
BindMounts map[string]string
|
||||
VolumeMounts map[string]string
|
||||
Tmpfs map[string]string
|
||||
RegistryCred string
|
||||
WaitingFor wait.Strategy
|
||||
Name string // for specifying container name
|
||||
Hostname string
|
||||
Privileged bool // for starting privileged container
|
||||
Networks []string // for specifying network names
|
||||
NetworkAliases map[string][]string // for specifying network aliases
|
||||
SkipReaper bool // indicates whether we skip setting up a reaper for this
|
||||
ReaperImage string // alternative reaper image
|
||||
AutoRemove bool // if set to true, the container will be removed from the host when stopped
|
||||
NetworkMode container.NetworkMode
|
||||
AlwaysPullImage bool // Always pull image
|
||||
Image string
|
||||
Entrypoint []string
|
||||
Env map[string]string
|
||||
ExposedPorts []string // allow specifying protocol info
|
||||
Cmd []string
|
||||
Labels map[string]string
|
||||
Mounts ContainerMounts
|
||||
Tmpfs map[string]string
|
||||
RegistryCred string // Deprecated: Testcontainers will detect registry credentials automatically
|
||||
WaitingFor wait.Strategy
|
||||
Name string // for specifying container name
|
||||
Hostname string
|
||||
ExtraHosts []string // Deprecated: Use HostConfigModifier instead
|
||||
Privileged bool // For starting privileged container
|
||||
Networks []string // for specifying network names
|
||||
NetworkAliases map[string][]string // for specifying network aliases
|
||||
NetworkMode container.NetworkMode // Deprecated: Use HostConfigModifier instead
|
||||
Resources container.Resources // Deprecated: Use HostConfigModifier instead
|
||||
Files []ContainerFile // files which will be copied when container starts
|
||||
User string // for specifying uid:gid
|
||||
SkipReaper bool // Deprecated: The reaper is globally controlled by the .testcontainers.properties file or the TESTCONTAINERS_RYUK_DISABLED environment variable
|
||||
ReaperImage string // Deprecated: use WithImageName ContainerOption instead. Alternative reaper image
|
||||
ReaperOptions []ContainerOption // options for the reaper
|
||||
AutoRemove bool // Deprecated: Use HostConfigModifier instead. If set to true, the container will be removed from the host when stopped
|
||||
AlwaysPullImage bool // Always pull image
|
||||
ImagePlatform string // ImagePlatform describes the platform which the image runs on.
|
||||
Binds []string // Deprecated: Use HostConfigModifier instead
|
||||
ShmSize int64 // Amount of memory shared with the host (in bytes)
|
||||
CapAdd []string // Deprecated: Use HostConfigModifier instead. Add Linux capabilities
|
||||
CapDrop []string // Deprecated: Use HostConfigModifier instead. Drop Linux capabilities
|
||||
ConfigModifier func(*container.Config) // Modifier for the config before container creation
|
||||
HostConfigModifier func(*container.HostConfig) // Modifier for the host config before container creation
|
||||
EnpointSettingsModifier func(map[string]*network.EndpointSettings) // Modifier for the network settings before container creation
|
||||
LifecycleHooks []ContainerLifecycleHooks // define hooks to be executed during container lifecycle
|
||||
}
|
||||
|
||||
// ProviderType is an enum for the possible providers
|
||||
type ProviderType int
|
||||
// containerOptions functional options for a container
|
||||
type containerOptions struct {
|
||||
ImageName string
|
||||
RegistryCredentials string // Deprecated: Testcontainers will detect registry credentials automatically
|
||||
}
|
||||
|
||||
// possible provider types
|
||||
const (
|
||||
ProviderDocker ProviderType = iota // Docker is default = 0
|
||||
)
|
||||
// functional option for setting the reaper image
|
||||
type ContainerOption func(*containerOptions)
|
||||
|
||||
// GetProvider provides the provider implementation for a certain type
|
||||
func (t ProviderType) GetProvider() (GenericProvider, error) {
|
||||
switch t {
|
||||
case ProviderDocker:
|
||||
provider, err := NewDockerProvider()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create Docker provider")
|
||||
}
|
||||
return provider, nil
|
||||
// WithImageName sets the reaper image name
|
||||
func WithImageName(imageName string) ContainerOption {
|
||||
return func(o *containerOptions) {
|
||||
o.ImageName = imageName
|
||||
}
|
||||
return nil, errors.New("unknown provider")
|
||||
}
|
||||
|
||||
// Validate ensures that the ContainerRequest does not have invalid paramters configured to it
|
||||
// Deprecated: Testcontainers will detect registry credentials automatically
|
||||
// WithRegistryCredentials sets the reaper registry credentials
|
||||
func WithRegistryCredentials(registryCredentials string) ContainerOption {
|
||||
return func(o *containerOptions) {
|
||||
o.RegistryCredentials = registryCredentials
|
||||
}
|
||||
}
|
||||
|
||||
// Validate ensures that the ContainerRequest does not have invalid parameters configured to it
|
||||
// ex. make sure you are not specifying both an image as well as a context
|
||||
func (c *ContainerRequest) Validate() error {
|
||||
|
||||
validationMethods := []func() error{
|
||||
c.validateContextAndImage,
|
||||
c.validateContexOrImageIsSpecified,
|
||||
c.validateContextOrImageIsSpecified,
|
||||
c.validateMounts,
|
||||
}
|
||||
|
||||
var err error
|
||||
@@ -140,6 +174,13 @@ func (c *ContainerRequest) GetContext() (io.Reader, error) {
|
||||
return c.ContextArchive, nil
|
||||
}
|
||||
|
||||
// always pass context as absolute path
|
||||
abs, err := filepath.Abs(c.Context)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting absolute path: %w", err)
|
||||
}
|
||||
c.Context = abs
|
||||
|
||||
buildContext, err := archive.TarWithOptions(c.Context, &archive.TarOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -148,6 +189,11 @@ func (c *ContainerRequest) GetContext() (io.Reader, error) {
|
||||
return buildContext, nil
|
||||
}
|
||||
|
||||
// GetBuildArgs returns the env args to be used when creating from Dockerfile
|
||||
func (c *ContainerRequest) GetBuildArgs() map[string]*string {
|
||||
return c.FromDockerfile.BuildArgs
|
||||
}
|
||||
|
||||
// GetDockerfile returns the Dockerfile from the ContainerRequest, defaults to "Dockerfile"
|
||||
func (c *ContainerRequest) GetDockerfile() string {
|
||||
f := c.FromDockerfile.Dockerfile
|
||||
@@ -158,10 +204,34 @@ func (c *ContainerRequest) GetDockerfile() string {
|
||||
return f
|
||||
}
|
||||
|
||||
// GetAuthConfigs returns the auth configs to be able to pull from an authenticated docker registry
|
||||
func (c *ContainerRequest) GetAuthConfigs() map[string]registry.AuthConfig {
|
||||
images, err := testcontainersdocker.ExtractImagesFromDockerfile(filepath.Join(c.Context, c.GetDockerfile()), c.GetBuildArgs())
|
||||
if err != nil {
|
||||
return map[string]registry.AuthConfig{}
|
||||
}
|
||||
|
||||
authConfigs := map[string]registry.AuthConfig{}
|
||||
for _, image := range images {
|
||||
registry, authConfig, err := DockerImageAuth(context.Background(), image)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
authConfigs[registry] = authConfig
|
||||
}
|
||||
|
||||
return authConfigs
|
||||
}
|
||||
|
||||
func (c *ContainerRequest) ShouldBuildImage() bool {
|
||||
return c.FromDockerfile.Context != "" || c.FromDockerfile.ContextArchive != nil
|
||||
}
|
||||
|
||||
func (c *ContainerRequest) ShouldPrintBuildLog() bool {
|
||||
return c.FromDockerfile.PrintBuildLog
|
||||
}
|
||||
|
||||
func (c *ContainerRequest) validateContextAndImage() error {
|
||||
if c.FromDockerfile.Context != "" && c.Image != "" {
|
||||
return errors.New("you cannot specify both an Image and Context in a ContainerRequest")
|
||||
@@ -170,10 +240,25 @@ func (c *ContainerRequest) validateContextAndImage() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ContainerRequest) validateContexOrImageIsSpecified() error {
|
||||
func (c *ContainerRequest) validateContextOrImageIsSpecified() error {
|
||||
if c.FromDockerfile.Context == "" && c.FromDockerfile.ContextArchive == nil && c.Image == "" {
|
||||
return errors.New("you must specify either a build context or an image")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ContainerRequest) validateMounts() error {
|
||||
targets := make(map[string]bool, len(c.Mounts))
|
||||
|
||||
for idx := range c.Mounts {
|
||||
m := c.Mounts[idx]
|
||||
targetPath := m.Target.Target()
|
||||
if targets[targetPath] {
|
||||
return fmt.Errorf("%w: %s", ErrDuplicateMountTarget, targetPath)
|
||||
} else {
|
||||
targets[targetPath] = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
962
vendor/github.com/testcontainers/testcontainers-go/docker.go
generated
vendored
962
vendor/github.com/testcontainers/testcontainers-go/docker.go
generated
vendored
File diff suppressed because it is too large
Load Diff
118
vendor/github.com/testcontainers/testcontainers-go/docker_auth.go
generated
vendored
Normal file
118
vendor/github.com/testcontainers/testcontainers-go/docker_auth.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
package testcontainers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/cpuguy83/dockercfg"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
|
||||
)
|
||||
|
||||
// DockerImageAuth returns the auth config for the given Docker image, extracting first its Docker registry.
|
||||
// Finally, it will use the credential helpers to extract the information from the docker config file
|
||||
// for that registry, if it exists.
|
||||
func DockerImageAuth(ctx context.Context, image string) (string, registry.AuthConfig, error) {
|
||||
defaultRegistry := defaultRegistry(ctx)
|
||||
reg := testcontainersdocker.ExtractRegistry(image, defaultRegistry)
|
||||
|
||||
cfgs, err := getDockerAuthConfigs()
|
||||
if err != nil {
|
||||
return reg, registry.AuthConfig{}, err
|
||||
}
|
||||
|
||||
if cfg, ok := cfgs[reg]; ok {
|
||||
return reg, cfg, nil
|
||||
}
|
||||
|
||||
return reg, registry.AuthConfig{}, dockercfg.ErrCredentialsNotFound
|
||||
}
|
||||
|
||||
// defaultRegistry returns the default registry to use when pulling images
|
||||
// It will use the docker daemon to get the default registry, returning "https://index.docker.io/v1/" if
|
||||
// it fails to get the information from the daemon
|
||||
func defaultRegistry(ctx context.Context) string {
|
||||
client, err := testcontainersdocker.NewClient(ctx)
|
||||
if err != nil {
|
||||
return testcontainersdocker.IndexDockerIO
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
info, err := client.Info(ctx)
|
||||
if err != nil {
|
||||
return testcontainersdocker.IndexDockerIO
|
||||
}
|
||||
|
||||
return info.IndexServerAddress
|
||||
}
|
||||
|
||||
// getDockerAuthConfigs returns a map with the auth configs from the docker config file
|
||||
// using the registry as the key
|
||||
func getDockerAuthConfigs() (map[string]registry.AuthConfig, error) {
|
||||
cfg, err := getDockerConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfgs := map[string]registry.AuthConfig{}
|
||||
for k, v := range cfg.AuthConfigs {
|
||||
ac := registry.AuthConfig{
|
||||
Auth: v.Auth,
|
||||
Email: v.Email,
|
||||
IdentityToken: v.IdentityToken,
|
||||
Password: v.Password,
|
||||
RegistryToken: v.RegistryToken,
|
||||
ServerAddress: v.ServerAddress,
|
||||
Username: v.Username,
|
||||
}
|
||||
|
||||
if v.Username == "" && v.Password == "" {
|
||||
u, p, _ := dockercfg.GetRegistryCredentials(k)
|
||||
ac.Username = u
|
||||
ac.Password = p
|
||||
}
|
||||
|
||||
if v.Auth == "" {
|
||||
ac.Auth = base64.StdEncoding.EncodeToString([]byte(ac.Username + ":" + ac.Password))
|
||||
}
|
||||
|
||||
cfgs[k] = ac
|
||||
}
|
||||
|
||||
// in the case where the auth field in the .docker/conf.json is empty, and the user has credential helpers registered
|
||||
// the auth comes from there
|
||||
for k := range cfg.CredentialHelpers {
|
||||
ac := registry.AuthConfig{}
|
||||
u, p, _ := dockercfg.GetRegistryCredentials(k)
|
||||
ac.Username = u
|
||||
ac.Password = p
|
||||
|
||||
cfgs[k] = ac
|
||||
}
|
||||
|
||||
return cfgs, nil
|
||||
}
|
||||
|
||||
// getDockerConfig returns the docker config file. It will internally check, in this particular order:
|
||||
// 1. the DOCKER_AUTH_CONFIG environment variable, unmarshalling it into a dockercfg.Config
|
||||
// 2. the DOCKER_CONFIG environment variable, as the path to the config file
|
||||
// 3. else it will load the default config file, which is ~/.docker/config.json
|
||||
func getDockerConfig() (dockercfg.Config, error) {
|
||||
dockerAuthConfig := os.Getenv("DOCKER_AUTH_CONFIG")
|
||||
if dockerAuthConfig != "" {
|
||||
cfg := dockercfg.Config{}
|
||||
err := json.Unmarshal([]byte(dockerAuthConfig), &cfg)
|
||||
if err == nil {
|
||||
return cfg, nil
|
||||
}
|
||||
}
|
||||
|
||||
cfg, err := dockercfg.LoadDefaultConfig()
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
116
vendor/github.com/testcontainers/testcontainers-go/docker_mounts.go
generated
vendored
Normal file
116
vendor/github.com/testcontainers/testcontainers-go/docker_mounts.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package testcontainers
|
||||
|
||||
import "github.com/docker/docker/api/types/mount"
|
||||
|
||||
var (
|
||||
mountTypeMapping = map[MountType]mount.Type{
|
||||
MountTypeBind: mount.TypeBind,
|
||||
MountTypeVolume: mount.TypeVolume,
|
||||
MountTypeTmpfs: mount.TypeTmpfs,
|
||||
MountTypePipe: mount.TypeNamedPipe,
|
||||
}
|
||||
)
|
||||
|
||||
// BindMounter can optionally be implemented by mount sources
|
||||
// to support advanced scenarios based on mount.BindOptions
|
||||
type BindMounter interface {
|
||||
GetBindOptions() *mount.BindOptions
|
||||
}
|
||||
|
||||
// VolumeMounter can optionally be implemented by mount sources
|
||||
// to support advanced scenarios based on mount.VolumeOptions
|
||||
type VolumeMounter interface {
|
||||
GetVolumeOptions() *mount.VolumeOptions
|
||||
}
|
||||
|
||||
// TmpfsMounter can optionally be implemented by mount sources
|
||||
// to support advanced scenarios based on mount.TmpfsOptions
|
||||
type TmpfsMounter interface {
|
||||
GetTmpfsOptions() *mount.TmpfsOptions
|
||||
}
|
||||
|
||||
type DockerBindMountSource struct {
|
||||
*mount.BindOptions
|
||||
|
||||
// HostPath is the path mounted into the container
|
||||
// the same host path might be mounted to multiple locations within a single container
|
||||
HostPath string
|
||||
}
|
||||
|
||||
func (s DockerBindMountSource) Source() string {
|
||||
return s.HostPath
|
||||
}
|
||||
|
||||
func (DockerBindMountSource) Type() MountType {
|
||||
return MountTypeBind
|
||||
}
|
||||
|
||||
func (s DockerBindMountSource) GetBindOptions() *mount.BindOptions {
|
||||
return s.BindOptions
|
||||
}
|
||||
|
||||
type DockerVolumeMountSource struct {
|
||||
*mount.VolumeOptions
|
||||
|
||||
// Name refers to the name of the volume to be mounted
|
||||
// the same volume might be mounted to multiple locations within a single container
|
||||
Name string
|
||||
}
|
||||
|
||||
func (s DockerVolumeMountSource) Source() string {
|
||||
return s.Name
|
||||
}
|
||||
|
||||
func (DockerVolumeMountSource) Type() MountType {
|
||||
return MountTypeVolume
|
||||
}
|
||||
|
||||
func (s DockerVolumeMountSource) GetVolumeOptions() *mount.VolumeOptions {
|
||||
return s.VolumeOptions
|
||||
}
|
||||
|
||||
type DockerTmpfsMountSource struct {
|
||||
GenericTmpfsMountSource
|
||||
*mount.TmpfsOptions
|
||||
}
|
||||
|
||||
func (s DockerTmpfsMountSource) GetTmpfsOptions() *mount.TmpfsOptions {
|
||||
return s.TmpfsOptions
|
||||
}
|
||||
|
||||
// mapToDockerMounts maps the given []ContainerMount to the corresponding
|
||||
// []mount.Mount for further processing
|
||||
func mapToDockerMounts(containerMounts ContainerMounts) []mount.Mount {
|
||||
mounts := make([]mount.Mount, 0, len(containerMounts))
|
||||
|
||||
for idx := range containerMounts {
|
||||
m := containerMounts[idx]
|
||||
|
||||
var mountType mount.Type
|
||||
if mt, ok := mountTypeMapping[m.Source.Type()]; ok {
|
||||
mountType = mt
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
containerMount := mount.Mount{
|
||||
Type: mountType,
|
||||
Source: m.Source.Source(),
|
||||
ReadOnly: m.ReadOnly,
|
||||
Target: m.Target.Target(),
|
||||
}
|
||||
|
||||
switch typedMounter := m.Source.(type) {
|
||||
case BindMounter:
|
||||
containerMount.BindOptions = typedMounter.GetBindOptions()
|
||||
case VolumeMounter:
|
||||
containerMount.VolumeOptions = typedMounter.GetVolumeOptions()
|
||||
case TmpfsMounter:
|
||||
containerMount.TmpfsOptions = typedMounter.GetTmpfsOptions()
|
||||
}
|
||||
|
||||
mounts = append(mounts, containerMount)
|
||||
}
|
||||
|
||||
return mounts
|
||||
}
|
44
vendor/github.com/testcontainers/testcontainers-go/exec/processor.go
generated
vendored
Normal file
44
vendor/github.com/testcontainers/testcontainers-go/exec/processor.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
)
|
||||
|
||||
// ProcessOptions defines options applicable to the reader processor
|
||||
type ProcessOptions struct {
|
||||
Reader io.Reader
|
||||
}
|
||||
|
||||
// ProcessOption defines a common interface to modify the reader processor
|
||||
// These options can be passed to the Exec function in a variadic way to customize the returned Reader instance
|
||||
type ProcessOption interface {
|
||||
Apply(opts *ProcessOptions)
|
||||
}
|
||||
|
||||
type ProcessOptionFunc func(opts *ProcessOptions)
|
||||
|
||||
func (fn ProcessOptionFunc) Apply(opts *ProcessOptions) {
|
||||
fn(opts)
|
||||
}
|
||||
|
||||
func Multiplexed() ProcessOption {
|
||||
return ProcessOptionFunc(func(opts *ProcessOptions) {
|
||||
done := make(chan struct{})
|
||||
|
||||
var outBuff bytes.Buffer
|
||||
var errBuff bytes.Buffer
|
||||
go func() {
|
||||
if _, err := stdcopy.StdCopy(&outBuff, &errBuff, opts.Reader); err != nil {
|
||||
return
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
<-done
|
||||
|
||||
opts.Reader = &outBuff
|
||||
})
|
||||
}
|
141
vendor/github.com/testcontainers/testcontainers-go/file.go
generated
vendored
Normal file
141
vendor/github.com/testcontainers/testcontainers-go/file.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
package testcontainers
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func isDir(path string) (bool, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
fileInfo, err := file.Stat()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if fileInfo.IsDir() {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// tarDir compress a directory using tar + gzip algorithms
|
||||
func tarDir(src string, fileMode int64) (*bytes.Buffer, error) {
|
||||
// always pass src as absolute path
|
||||
abs, err := filepath.Abs(src)
|
||||
if err != nil {
|
||||
return &bytes.Buffer{}, fmt.Errorf("error getting absolute path: %w", err)
|
||||
}
|
||||
src = abs
|
||||
|
||||
buffer := &bytes.Buffer{}
|
||||
|
||||
fmt.Printf(">> creating TAR file from directory: %s\n", src)
|
||||
|
||||
// tar > gzip > buffer
|
||||
zr := gzip.NewWriter(buffer)
|
||||
tw := tar.NewWriter(zr)
|
||||
|
||||
_, baseDir := filepath.Split(src)
|
||||
// keep the path relative to the parent directory
|
||||
index := strings.LastIndex(src, baseDir)
|
||||
|
||||
// walk through every file in the folder
|
||||
err = filepath.Walk(src, func(file string, fi os.FileInfo, errFn error) error {
|
||||
if errFn != nil {
|
||||
return fmt.Errorf("error traversing the file system: %w", errFn)
|
||||
}
|
||||
|
||||
// if a symlink, skip file
|
||||
if fi.Mode().Type() == os.ModeSymlink {
|
||||
fmt.Printf(">> skipping symlink: %s\n", file)
|
||||
return nil
|
||||
}
|
||||
|
||||
// generate tar header
|
||||
header, err := tar.FileInfoHeader(fi, file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting file info header: %w", err)
|
||||
}
|
||||
|
||||
// see https://pkg.go.dev/archive/tar#FileInfoHeader:
|
||||
// Since fs.FileInfo's Name method only returns the base name of the file it describes,
|
||||
// it may be necessary to modify Header.Name to provide the full path name of the file.
|
||||
header.Name = filepath.ToSlash(file[index:])
|
||||
header.Mode = fileMode
|
||||
|
||||
// write header
|
||||
if err := tw.WriteHeader(header); err != nil {
|
||||
return fmt.Errorf("error writing header: %w", err)
|
||||
}
|
||||
|
||||
// if not a dir, write file content
|
||||
if !fi.IsDir() {
|
||||
data, err := os.Open(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening file: %w", err)
|
||||
}
|
||||
defer data.Close()
|
||||
if _, err := io.Copy(tw, data); err != nil {
|
||||
return fmt.Errorf("error compressing file: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return buffer, err
|
||||
}
|
||||
|
||||
// produce tar
|
||||
if err := tw.Close(); err != nil {
|
||||
return buffer, fmt.Errorf("error closing tar file: %w", err)
|
||||
}
|
||||
// produce gzip
|
||||
if err := zr.Close(); err != nil {
|
||||
return buffer, fmt.Errorf("error closing gzip file: %w", err)
|
||||
}
|
||||
|
||||
return buffer, nil
|
||||
}
|
||||
|
||||
// tarFile compress a single file using tar + gzip algorithms
|
||||
func tarFile(fileContent []byte, basePath string, fileMode int64) (*bytes.Buffer, error) {
|
||||
buffer := &bytes.Buffer{}
|
||||
|
||||
zr := gzip.NewWriter(buffer)
|
||||
tw := tar.NewWriter(zr)
|
||||
|
||||
hdr := &tar.Header{
|
||||
Name: filepath.Base(basePath),
|
||||
Mode: fileMode,
|
||||
Size: int64(len(fileContent)),
|
||||
}
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return buffer, err
|
||||
}
|
||||
if _, err := tw.Write(fileContent); err != nil {
|
||||
return buffer, err
|
||||
}
|
||||
|
||||
// produce tar
|
||||
if err := tw.Close(); err != nil {
|
||||
return buffer, fmt.Errorf("error closing tar file: %w", err)
|
||||
}
|
||||
// produce gzip
|
||||
if err := zr.Close(); err != nil {
|
||||
return buffer, fmt.Errorf("error closing gzip file: %w", err)
|
||||
}
|
||||
|
||||
return buffer, nil
|
||||
}
|
113
vendor/github.com/testcontainers/testcontainers-go/generic.go
generated
vendored
113
vendor/github.com/testcontainers/testcontainers-go/generic.go
generated
vendored
@@ -2,8 +2,20 @@ package testcontainers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"dario.cat/mergo"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
)
|
||||
|
||||
var (
|
||||
reuseContainerMx sync.Mutex
|
||||
ErrReuseEmptyName = errors.New("with reuse option a container name mustn't be empty")
|
||||
)
|
||||
|
||||
// GenericContainerRequest represents parameters to a generic container
|
||||
@@ -11,6 +23,73 @@ type GenericContainerRequest struct {
|
||||
ContainerRequest // embedded request for provider
|
||||
Started bool // whether to auto-start the container
|
||||
ProviderType ProviderType // which provider to use, Docker if empty
|
||||
Logger Logging // provide a container specific Logging - use default global logger if empty
|
||||
Reuse bool // reuse an existing container if it exists or create a new one. a container name mustn't be empty
|
||||
}
|
||||
|
||||
// ContainerCustomizer is an interface that can be used to configure the Testcontainers container
|
||||
// request. The passed request will be merged with the default one.
|
||||
type ContainerCustomizer interface {
|
||||
Customize(req *GenericContainerRequest)
|
||||
}
|
||||
|
||||
// CustomizeRequestOption is a type that can be used to configure the Testcontainers container request.
|
||||
// The passed request will be merged with the default one.
|
||||
type CustomizeRequestOption func(req *GenericContainerRequest)
|
||||
|
||||
func (opt CustomizeRequestOption) Customize(req *GenericContainerRequest) {
|
||||
opt(req)
|
||||
}
|
||||
|
||||
// CustomizeRequest returns a function that can be used to merge the passed container request with the one that is used by the container.
|
||||
// Slices and Maps will be appended.
|
||||
func CustomizeRequest(src GenericContainerRequest) CustomizeRequestOption {
|
||||
return func(req *GenericContainerRequest) {
|
||||
if err := mergo.Merge(req, &src, mergo.WithOverride, mergo.WithAppendSlice); err != nil {
|
||||
fmt.Printf("error merging container request, keeping the original one. Error: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithImage sets the image for a container
|
||||
func WithImage(image string) CustomizeRequestOption {
|
||||
return func(req *GenericContainerRequest) {
|
||||
req.Image = image
|
||||
}
|
||||
}
|
||||
|
||||
// WithConfigModifier allows to override the default container config
|
||||
func WithConfigModifier(modifier func(config *container.Config)) CustomizeRequestOption {
|
||||
return func(req *GenericContainerRequest) {
|
||||
req.ConfigModifier = modifier
|
||||
}
|
||||
}
|
||||
|
||||
// WithEndpointSettingsModifier allows to override the default endpoint settings
|
||||
func WithEndpointSettingsModifier(modifier func(settings map[string]*network.EndpointSettings)) CustomizeRequestOption {
|
||||
return func(req *GenericContainerRequest) {
|
||||
req.EnpointSettingsModifier = modifier
|
||||
}
|
||||
}
|
||||
|
||||
// WithHostConfigModifier allows to override the default host config
|
||||
func WithHostConfigModifier(modifier func(hostConfig *container.HostConfig)) CustomizeRequestOption {
|
||||
return func(req *GenericContainerRequest) {
|
||||
req.HostConfigModifier = modifier
|
||||
}
|
||||
}
|
||||
|
||||
// WithWaitStrategy sets the wait strategy for a container, using 60 seconds as deadline
|
||||
func WithWaitStrategy(strategies ...wait.Strategy) CustomizeRequestOption {
|
||||
return WithWaitStrategyAndDeadline(60*time.Second, strategies...)
|
||||
}
|
||||
|
||||
// WithWaitStrategyAndDeadline sets the wait strategy for a container, including deadline
|
||||
func WithWaitStrategyAndDeadline(deadline time.Duration, strategies ...wait.Strategy) CustomizeRequestOption {
|
||||
return func(req *GenericContainerRequest) {
|
||||
req.WaitingFor = wait.ForAll(strategies...).WithDeadline(deadline)
|
||||
}
|
||||
}
|
||||
|
||||
// GenericNetworkRequest represents parameters to a generic network
|
||||
@@ -27,7 +106,7 @@ func GenericNetwork(ctx context.Context, req GenericNetworkRequest) (Network, er
|
||||
}
|
||||
network, err := provider.CreateNetwork(ctx, req.NetworkRequest)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create network")
|
||||
return nil, fmt.Errorf("%w: failed to create network", err)
|
||||
}
|
||||
|
||||
return network, nil
|
||||
@@ -35,22 +114,40 @@ func GenericNetwork(ctx context.Context, req GenericNetworkRequest) (Network, er
|
||||
|
||||
// GenericContainer creates a generic container with parameters
|
||||
func GenericContainer(ctx context.Context, req GenericContainerRequest) (Container, error) {
|
||||
provider, err := req.ProviderType.GetProvider()
|
||||
if req.Reuse && req.Name == "" {
|
||||
return nil, ErrReuseEmptyName
|
||||
}
|
||||
|
||||
logging := req.Logger
|
||||
if logging == nil {
|
||||
logging = Logger
|
||||
}
|
||||
provider, err := req.ProviderType.GetProvider(WithLogger(logging))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer provider.Close()
|
||||
|
||||
c, err := provider.CreateContainer(ctx, req.ContainerRequest)
|
||||
var c Container
|
||||
if req.Reuse {
|
||||
// we must protect the reusability of the container in the case it's invoked
|
||||
// in a parallel execution, via ParallelContainers or t.Parallel()
|
||||
reuseContainerMx.Lock()
|
||||
defer reuseContainerMx.Unlock()
|
||||
|
||||
c, err = provider.ReuseOrCreateContainer(ctx, req.ContainerRequest)
|
||||
} else {
|
||||
c, err = provider.CreateContainer(ctx, req.ContainerRequest)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create container")
|
||||
return nil, fmt.Errorf("%w: failed to create container", err)
|
||||
}
|
||||
|
||||
if req.Started {
|
||||
if req.Started && !c.IsRunning() {
|
||||
if err := c.Start(ctx); err != nil {
|
||||
return c, errors.Wrap(err, "failed to start container")
|
||||
return c, fmt.Errorf("%w: failed to start container", err)
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
|
100
vendor/github.com/testcontainers/testcontainers-go/internal/config/config.go
generated
vendored
Normal file
100
vendor/github.com/testcontainers/testcontainers-go/internal/config/config.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/magiconair/properties"
|
||||
)
|
||||
|
||||
var tcConfig Config
|
||||
var tcConfigOnce *sync.Once = new(sync.Once)
|
||||
|
||||
// Config represents the configuration for Testcontainers
|
||||
// testcontainersConfig {
|
||||
type Config struct {
|
||||
Host string `properties:"docker.host,default="`
|
||||
TLSVerify int `properties:"docker.tls.verify,default=0"`
|
||||
CertPath string `properties:"docker.cert.path,default="`
|
||||
RyukDisabled bool `properties:"ryuk.disabled,default=false"`
|
||||
RyukPrivileged bool `properties:"ryuk.container.privileged,default=false"`
|
||||
TestcontainersHost string `properties:"tc.host,default="`
|
||||
}
|
||||
|
||||
// }
|
||||
|
||||
// Read reads from testcontainers properties file, if it exists
|
||||
// it is possible that certain values get overridden when set as environment variables
|
||||
func Read() Config {
|
||||
tcConfigOnce.Do(func() {
|
||||
tcConfig = read()
|
||||
|
||||
if tcConfig.RyukDisabled {
|
||||
ryukDisabledMessage := `
|
||||
**********************************************************************************************
|
||||
Ryuk has been disabled for the current execution. This can cause unexpected behavior in your environment.
|
||||
More on this: https://golang.testcontainers.org/features/garbage_collector/
|
||||
**********************************************************************************************`
|
||||
fmt.Println(ryukDisabledMessage)
|
||||
}
|
||||
})
|
||||
|
||||
return tcConfig
|
||||
}
|
||||
|
||||
// Reset resets the singleton instance of the Config struct,
|
||||
// allowing to read the configuration again.
|
||||
// Handy for testing, so do not use it in production code
|
||||
// This function is not thread-safe
|
||||
func Reset() {
|
||||
tcConfigOnce = new(sync.Once)
|
||||
}
|
||||
|
||||
func read() Config {
|
||||
config := Config{}
|
||||
|
||||
applyEnvironmentConfiguration := func(config Config) Config {
|
||||
ryukDisabledEnv := os.Getenv("TESTCONTAINERS_RYUK_DISABLED")
|
||||
if parseBool(ryukDisabledEnv) {
|
||||
config.RyukDisabled = ryukDisabledEnv == "true"
|
||||
}
|
||||
|
||||
ryukPrivilegedEnv := os.Getenv("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED")
|
||||
if parseBool(ryukPrivilegedEnv) {
|
||||
config.RyukPrivileged = ryukPrivilegedEnv == "true"
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return applyEnvironmentConfiguration(config)
|
||||
}
|
||||
|
||||
tcProp := filepath.Join(home, ".testcontainers.properties")
|
||||
// init from a file
|
||||
properties, err := properties.LoadFile(tcProp, properties.UTF8)
|
||||
if err != nil {
|
||||
return applyEnvironmentConfiguration(config)
|
||||
}
|
||||
|
||||
if err := properties.Decode(&config); err != nil {
|
||||
fmt.Printf("invalid testcontainers properties file, returning an empty Testcontainers configuration: %v\n", err)
|
||||
return applyEnvironmentConfiguration(config)
|
||||
}
|
||||
|
||||
fmt.Printf("Testcontainers properties file has been found: %s\n", tcProp)
|
||||
|
||||
return applyEnvironmentConfiguration(config)
|
||||
}
|
||||
|
||||
func parseBool(input string) bool {
|
||||
if _, err := strconv.ParseBool(input); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
67
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainersdocker/client.go
generated
vendored
Normal file
67
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainersdocker/client.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
package testcontainersdocker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/testcontainers/testcontainers-go/internal"
|
||||
"github.com/testcontainers/testcontainers-go/internal/config"
|
||||
"github.com/testcontainers/testcontainers-go/internal/testcontainerssession"
|
||||
)
|
||||
|
||||
// NewClient returns a new docker client extracting the docker host from the different alternatives
|
||||
func NewClient(ctx context.Context, ops ...client.Opt) (*client.Client, error) {
|
||||
tcConfig := config.Read()
|
||||
|
||||
dockerHost := ExtractDockerHost(ctx)
|
||||
|
||||
opts := []client.Opt{client.FromEnv, client.WithAPIVersionNegotiation()}
|
||||
if dockerHost != "" {
|
||||
opts = append(opts, client.WithHost(dockerHost))
|
||||
|
||||
// for further information, read https://docs.docker.com/engine/security/protect-access/
|
||||
if tcConfig.TLSVerify == 1 {
|
||||
cacertPath := filepath.Join(tcConfig.CertPath, "ca.pem")
|
||||
certPath := filepath.Join(tcConfig.CertPath, "cert.pem")
|
||||
keyPath := filepath.Join(tcConfig.CertPath, "key.pem")
|
||||
|
||||
opts = append(opts, client.WithTLSClientConfig(cacertPath, certPath, keyPath))
|
||||
}
|
||||
}
|
||||
|
||||
opts = append(opts, client.WithHTTPHeaders(
|
||||
map[string]string{
|
||||
"x-tc-sid": testcontainerssession.String(),
|
||||
"User-Agent": "tc-go/" + internal.Version,
|
||||
}),
|
||||
)
|
||||
|
||||
// passed options have priority over the default ones
|
||||
opts = append(opts, ops...)
|
||||
|
||||
cli, err := client.NewClientWithOpts(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err = cli.Ping(context.Background()); err != nil {
|
||||
// Fallback to environment, including the original options
|
||||
cli, err = defaultClient(context.Background(), ops...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
// defaultClient returns a plain, new docker client with the default options
|
||||
func defaultClient(ctx context.Context, ops ...client.Opt) (*client.Client, error) {
|
||||
if len(ops) == 0 {
|
||||
ops = []client.Opt{client.FromEnv, client.WithAPIVersionNegotiation()}
|
||||
}
|
||||
|
||||
return client.NewClientWithOpts(ops...)
|
||||
}
|
269
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainersdocker/docker_host.go
generated
vendored
Normal file
269
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainersdocker/docker_host.go
generated
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
package testcontainersdocker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/testcontainers/testcontainers-go/internal/config"
|
||||
)
|
||||
|
||||
type dockerHostContext string
|
||||
|
||||
var DockerHostContextKey = dockerHostContext("docker_host")
|
||||
|
||||
var (
|
||||
ErrDockerHostNotSet = errors.New("DOCKER_HOST is not set")
|
||||
ErrDockerSocketOverrideNotSet = errors.New("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE is not set")
|
||||
ErrDockerSocketNotSetInContext = errors.New("socket not set in context")
|
||||
ErrDockerSocketNotSetInProperties = errors.New("socket not set in ~/.testcontainers.properties")
|
||||
ErrNoUnixSchema = errors.New("URL schema is not unix")
|
||||
ErrSocketNotFound = errors.New("socket not found")
|
||||
ErrSocketNotFoundInPath = errors.New("docker socket not found in " + DockerSocketPath)
|
||||
// ErrTestcontainersHostNotSetInProperties this error is specific to Testcontainers
|
||||
ErrTestcontainersHostNotSetInProperties = errors.New("tc.host not set in ~/.testcontainers.properties")
|
||||
)
|
||||
|
||||
var dockerHostCache string
|
||||
var dockerHostOnce sync.Once
|
||||
|
||||
var dockerSocketPathCache string
|
||||
var dockerSocketPathOnce sync.Once
|
||||
|
||||
// deprecated
|
||||
// see https://github.com/testcontainers/testcontainers-java/blob/main/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java#L46
|
||||
func DefaultGatewayIP() (string, error) {
|
||||
// see https://github.com/testcontainers/testcontainers-java/blob/3ad8d80e2484864e554744a4800a81f6b7982168/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java#L27
|
||||
cmd := exec.Command("sh", "-c", "ip route|awk '/default/ { print $3 }'")
|
||||
stdout, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", errors.New("failed to detect docker host")
|
||||
}
|
||||
ip := strings.TrimSpace(string(stdout))
|
||||
if len(ip) == 0 {
|
||||
return "", errors.New("failed to parse default gateway IP")
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// ExtractDockerHost Extracts the docker host from the different alternatives, caching the result to avoid unnecessary
|
||||
// calculations. Use this function to get the actual Docker host. This function does not consider Windows containers at the moment.
|
||||
// The possible alternatives are:
|
||||
//
|
||||
// 1. Docker host from the "tc.host" property in the ~/.testcontainers.properties file.
|
||||
// 2. DOCKER_HOST environment variable.
|
||||
// 3. Docker host from context.
|
||||
// 4. Docker host from the default docker socket path, without the unix schema.
|
||||
// 5. Docker host from the "docker.host" property in the ~/.testcontainers.properties file.
|
||||
// 6. Rootless docker socket path.
|
||||
// 7. Else, the default Docker socket including schema will be returned.
|
||||
func ExtractDockerHost(ctx context.Context) string {
|
||||
dockerHostOnce.Do(func() {
|
||||
dockerHostCache = extractDockerHost(ctx)
|
||||
})
|
||||
|
||||
return dockerHostCache
|
||||
}
|
||||
|
||||
// ExtractDockerSocket Extracts the docker socket from the different alternatives, removing the socket schema and
|
||||
// caching the result to avoid unnecessary calculations. Use this function to get the docker socket path,
|
||||
// not the host (e.g. mounting the socket in a container). This function does not consider Windows containers at the moment.
|
||||
// The possible alternatives are:
|
||||
//
|
||||
// 1. Docker host from the "tc.host" property in the ~/.testcontainers.properties file.
|
||||
// 2. The TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE environment variable.
|
||||
// 3. Using a Docker client, check if the Info().OperativeSystem is "Docker Desktop" and return the default docker socket path for rootless docker.
|
||||
// 4. Else, Get the current Docker Host from the existing strategies: see ExtractDockerHost.
|
||||
// 5. If the socket contains the unix schema, the schema is removed (e.g. unix:///var/run/docker.sock -> /var/run/docker.sock)
|
||||
// 6. Else, the default location of the docker socket is used (/var/run/docker.sock)
|
||||
//
|
||||
// In any case, if the docker socket schema is "tcp://", the default docker socket path will be returned.
|
||||
func ExtractDockerSocket(ctx context.Context) string {
|
||||
dockerSocketPathOnce.Do(func() {
|
||||
dockerSocketPathCache = extractDockerSocket(ctx)
|
||||
})
|
||||
|
||||
return dockerSocketPathCache
|
||||
}
|
||||
|
||||
// extractDockerHost Extracts the docker host from the different alternatives, without caching the result.
|
||||
// This internal method is handy for testing purposes.
|
||||
func extractDockerHost(ctx context.Context) string {
|
||||
dockerHostFns := []func(context.Context) (string, error){
|
||||
testcontainersHostFromProperties,
|
||||
dockerHostFromEnv,
|
||||
dockerHostFromContext,
|
||||
dockerSocketPath,
|
||||
dockerHostFromProperties,
|
||||
rootlessDockerSocketPath,
|
||||
}
|
||||
|
||||
outerErr := ErrSocketNotFound
|
||||
for _, dockerHostFn := range dockerHostFns {
|
||||
dockerHost, err := dockerHostFn(ctx)
|
||||
if err != nil {
|
||||
outerErr = fmt.Errorf("%w: %v", outerErr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
return dockerHost
|
||||
}
|
||||
|
||||
// We are not supporting Windows containers at the moment
|
||||
return DockerSocketPathWithSchema
|
||||
}
|
||||
|
||||
// extractDockerHost Extracts the docker socket from the different alternatives, without caching the result.
|
||||
// It will internally use the default Docker client, calling the internal method extractDockerSocketFromClient with it.
|
||||
// This internal method is handy for testing purposes.
|
||||
// If a Docker client cannot be created, the program will panic.
|
||||
func extractDockerSocket(ctx context.Context) string {
|
||||
cli, err := NewClient(ctx)
|
||||
if err != nil {
|
||||
panic(err) // a Docker client is required to get the Docker info
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
return extractDockerSocketFromClient(ctx, cli)
|
||||
}
|
||||
|
||||
// extractDockerSocketFromClient Extracts the docker socket from the different alternatives, without caching the result,
|
||||
// and receiving an instance of the Docker API client interface.
|
||||
// This internal method is handy for testing purposes, passing a mock type simulating the desired behaviour.
|
||||
func extractDockerSocketFromClient(ctx context.Context, cli client.APIClient) string {
|
||||
// check that the socket is not a tcp or unix socket
|
||||
checkDockerSocketFn := func(socket string) string {
|
||||
// this use case will cover the case when the docker host is a tcp socket
|
||||
if strings.HasPrefix(socket, TCPSchema) {
|
||||
return DockerSocketPath
|
||||
}
|
||||
|
||||
if strings.HasPrefix(socket, DockerSocketSchema) {
|
||||
return strings.Replace(socket, DockerSocketSchema, "", 1)
|
||||
}
|
||||
|
||||
return socket
|
||||
}
|
||||
|
||||
tcHost, err := testcontainersHostFromProperties(ctx)
|
||||
if err == nil {
|
||||
return checkDockerSocketFn(tcHost)
|
||||
}
|
||||
|
||||
testcontainersDockerSocket, err := dockerSocketOverridePath(ctx)
|
||||
if err == nil {
|
||||
return checkDockerSocketFn(testcontainersDockerSocket)
|
||||
}
|
||||
|
||||
info, err := cli.Info(ctx)
|
||||
if err != nil {
|
||||
panic(err) // Docker Info is required to get the Operating System
|
||||
}
|
||||
|
||||
// Because Docker Desktop runs in a VM, we need to use the default docker path for rootless docker
|
||||
if info.OperatingSystem == "Docker Desktop" {
|
||||
if IsWindows() {
|
||||
return WindowsDockerSocketPath
|
||||
}
|
||||
|
||||
return DockerSocketPath
|
||||
}
|
||||
|
||||
dockerHost := extractDockerHost(ctx)
|
||||
|
||||
return checkDockerSocketFn(dockerHost)
|
||||
}
|
||||
|
||||
// dockerHostFromEnv returns the docker host from the DOCKER_HOST environment variable, if it's not empty
|
||||
func dockerHostFromEnv(ctx context.Context) (string, error) {
|
||||
if dockerHostPath := os.Getenv("DOCKER_HOST"); dockerHostPath != "" {
|
||||
return dockerHostPath, nil
|
||||
}
|
||||
|
||||
return "", ErrDockerHostNotSet
|
||||
}
|
||||
|
||||
// dockerHostFromContext returns the docker host from the Go context, if it's not empty
|
||||
func dockerHostFromContext(ctx context.Context) (string, error) {
|
||||
if socketPath, ok := ctx.Value(DockerHostContextKey).(string); ok && socketPath != "" {
|
||||
parsed, err := parseURL(socketPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
return "", ErrDockerSocketNotSetInContext
|
||||
}
|
||||
|
||||
// dockerHostFromProperties returns the docker host from the ~/.testcontainers.properties file, if it's not empty
|
||||
func dockerHostFromProperties(ctx context.Context) (string, error) {
|
||||
cfg := config.Read()
|
||||
socketPath := cfg.Host
|
||||
if socketPath != "" {
|
||||
parsed, err := parseURL(socketPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
return "", ErrDockerSocketNotSetInProperties
|
||||
}
|
||||
|
||||
// dockerSocketOverridePath returns the docker socket from the TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE environment variable,
|
||||
// if it's not empty
|
||||
func dockerSocketOverridePath(ctx context.Context) (string, error) {
|
||||
if dockerHostPath, exists := os.LookupEnv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE"); exists {
|
||||
return dockerHostPath, nil
|
||||
}
|
||||
|
||||
return "", ErrDockerSocketOverrideNotSet
|
||||
}
|
||||
|
||||
// dockerSocketPath returns the docker socket from the default docker socket path, if it's not empty
|
||||
// and the socket exists
|
||||
func dockerSocketPath(ctx context.Context) (string, error) {
|
||||
if fileExists(DockerSocketPath) {
|
||||
return DockerSocketPathWithSchema, nil
|
||||
}
|
||||
|
||||
return "", ErrSocketNotFoundInPath
|
||||
}
|
||||
|
||||
// testcontainersHostFromProperties returns the testcontainers host from the ~/.testcontainers.properties file, if it's not empty
|
||||
func testcontainersHostFromProperties(ctx context.Context) (string, error) {
|
||||
cfg := config.Read()
|
||||
testcontainersHost := cfg.TestcontainersHost
|
||||
if testcontainersHost != "" {
|
||||
parsed, err := parseURL(testcontainersHost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
return "", ErrTestcontainersHostNotSetInProperties
|
||||
}
|
||||
|
||||
// InAContainer returns true if the code is running inside a container
|
||||
// See https://github.com/docker/docker/blob/a9fa38b1edf30b23cae3eade0be48b3d4b1de14b/daemon/initlayer/setup_unix.go#L25
|
||||
func InAContainer() bool {
|
||||
return inAContainer("/.dockerenv")
|
||||
}
|
||||
|
||||
func inAContainer(path string) bool {
|
||||
// see https://github.com/testcontainers/testcontainers-java/blob/3ad8d80e2484864e554744a4800a81f6b7982168/core/src/main/java/org/testcontainers/dockerclient/DockerClientConfigUtils.java#L15
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
146
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainersdocker/docker_rootless.go
generated
vendored
Normal file
146
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainersdocker/docker_rootless.go
generated
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
package testcontainersdocker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRootlessDockerNotFound = errors.New("rootless Docker not found")
|
||||
ErrRootlessDockerNotFoundHomeDesktopDir = errors.New("checked path: ~/.docker/desktop/docker.sock")
|
||||
ErrRootlessDockerNotFoundHomeRunDir = errors.New("checked path: ~/.docker/run/docker.sock")
|
||||
ErrRootlessDockerNotFoundRunDir = errors.New("checked path: /run/user/${uid}/docker.sock")
|
||||
ErrRootlessDockerNotFoundXDGRuntimeDir = errors.New("checked path: $XDG_RUNTIME_DIR")
|
||||
ErrRootlessDockerNotSupportedWindows = errors.New("rootless Docker is not supported on Windows")
|
||||
ErrXDGRuntimeDirNotSet = errors.New("XDG_RUNTIME_DIR is not set")
|
||||
)
|
||||
|
||||
// baseRunDir is the base directory for the "/run/user/${uid}" directory.
|
||||
// It is a variable so it can be modified for testing.
|
||||
var baseRunDir = "/run"
|
||||
|
||||
// IsWindows returns if the current OS is Windows. For that it checks the GOOS environment variable or the runtime.GOOS constant.
|
||||
func IsWindows() bool {
|
||||
return os.Getenv("GOOS") == "windows" || runtime.GOOS == "windows"
|
||||
}
|
||||
|
||||
// rootlessDockerSocketPath returns if the path to the rootless Docker socket exists.
|
||||
// The rootless socket path is determined by the following order:
|
||||
//
|
||||
// 1. XDG_RUNTIME_DIR environment variable.
|
||||
// 2. ~/.docker/run/docker.sock file.
|
||||
// 3. ~/.docker/desktop/docker.sock file.
|
||||
// 4. /run/user/${uid}/docker.sock file.
|
||||
// 5. Else, return ErrRootlessDockerNotFound, wrapping secific errors for each of the above paths.
|
||||
//
|
||||
// It should include the Docker socket schema (unix://) in the returned path.
|
||||
func rootlessDockerSocketPath(_ context.Context) (string, error) {
|
||||
// adding a manner to test it on non-windows machines, setting the GOOS env var to windows
|
||||
// This is needed because runtime.GOOS is a constant that returns the OS of the machine running the test
|
||||
if IsWindows() {
|
||||
return "", ErrRootlessDockerNotSupportedWindows
|
||||
}
|
||||
|
||||
socketPathFns := []func() (string, error){
|
||||
rootlessSocketPathFromEnv,
|
||||
rootlessSocketPathFromHomeRunDir,
|
||||
rootlessSocketPathFromHomeDesktopDir,
|
||||
rootlessSocketPathFromRunDir,
|
||||
}
|
||||
|
||||
outerErr := ErrRootlessDockerNotFound
|
||||
for _, socketPathFn := range socketPathFns {
|
||||
s, err := socketPathFn()
|
||||
if err != nil {
|
||||
outerErr = fmt.Errorf("%w: %v", outerErr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
return DockerSocketSchema + s, nil
|
||||
}
|
||||
|
||||
return "", outerErr
|
||||
}
|
||||
|
||||
func fileExists(f string) bool {
|
||||
_, err := os.Stat(f)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func parseURL(s string) (string, error) {
|
||||
var hostURL *url.URL
|
||||
if u, err := url.Parse(s); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
hostURL = u
|
||||
}
|
||||
|
||||
switch hostURL.Scheme {
|
||||
case "unix", "npipe":
|
||||
return hostURL.Path, nil
|
||||
case "tcp":
|
||||
// return the original URL, as it is a valid TCP URL
|
||||
return s, nil
|
||||
default:
|
||||
return "", ErrNoUnixSchema
|
||||
}
|
||||
}
|
||||
|
||||
// rootlessSocketPathFromEnv returns the path to the rootless Docker socket from the XDG_RUNTIME_DIR environment variable.
|
||||
// It should include the Docker socket schema (unix://) in the returned path.
|
||||
func rootlessSocketPathFromEnv() (string, error) {
|
||||
xdgRuntimeDir, exists := os.LookupEnv("XDG_RUNTIME_DIR")
|
||||
if exists {
|
||||
f := filepath.Join(xdgRuntimeDir, "docker.sock")
|
||||
if fileExists(f) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
return "", ErrRootlessDockerNotFoundXDGRuntimeDir
|
||||
}
|
||||
|
||||
return "", ErrXDGRuntimeDirNotSet
|
||||
}
|
||||
|
||||
// rootlessSocketPathFromHomeRunDir returns the path to the rootless Docker socket from the ~/.docker/run/docker.sock file.
|
||||
func rootlessSocketPathFromHomeRunDir() (string, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
f := filepath.Join(home, ".docker", "run", "docker.sock")
|
||||
if fileExists(f) {
|
||||
return f, nil
|
||||
}
|
||||
return "", ErrRootlessDockerNotFoundHomeRunDir
|
||||
}
|
||||
|
||||
// rootlessSocketPathFromHomeDesktopDir returns the path to the rootless Docker socket from the ~/.docker/desktop/docker.sock file.
|
||||
func rootlessSocketPathFromHomeDesktopDir() (string, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
f := filepath.Join(home, ".docker", "desktop", "docker.sock")
|
||||
if fileExists(f) {
|
||||
return f, nil
|
||||
}
|
||||
return "", ErrRootlessDockerNotFoundHomeDesktopDir
|
||||
}
|
||||
|
||||
// rootlessSocketPathFromRunDir returns the path to the rootless Docker socket from the /run/user/<uid>/docker.sock file.
|
||||
func rootlessSocketPathFromRunDir() (string, error) {
|
||||
uid := os.Getuid()
|
||||
f := filepath.Join(baseRunDir, "user", fmt.Sprintf("%d", uid), "docker.sock")
|
||||
if fileExists(f) {
|
||||
return f, nil
|
||||
}
|
||||
return "", ErrRootlessDockerNotFoundRunDir
|
||||
}
|
49
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainersdocker/docker_socket.go
generated
vendored
Normal file
49
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainersdocker/docker_socket.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
package testcontainersdocker
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
// DockerSocketSchema is the unix schema.
|
||||
var DockerSocketSchema = "unix://"
|
||||
|
||||
// DockerSocketPath is the path to the docker socket under unix systems.
|
||||
var DockerSocketPath = "/var/run/docker.sock"
|
||||
|
||||
// DockerSocketPathWithSchema is the path to the docker socket under unix systems with the unix schema.
|
||||
var DockerSocketPathWithSchema = DockerSocketSchema + DockerSocketPath
|
||||
|
||||
// TCPSchema is the tcp schema.
|
||||
var TCPSchema = "tcp://"
|
||||
|
||||
// WindowsDockerSocketPath is the path to the docker socket under windows systems.
|
||||
var WindowsDockerSocketPath = "//var/run/docker.sock"
|
||||
|
||||
func init() {
|
||||
const DefaultDockerHost = client.DefaultDockerHost
|
||||
|
||||
u, err := url.Parse(DefaultDockerHost)
|
||||
if err != nil {
|
||||
// unsupported default host specified by the docker client package,
|
||||
// so revert to the default unix docker socket path
|
||||
return
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "unix", "npipe":
|
||||
DockerSocketSchema = u.Scheme + "://"
|
||||
DockerSocketPath = u.Path
|
||||
if !strings.HasPrefix(DockerSocketPath, "/") {
|
||||
// seeing as the code in this module depends on DockerSocketPath having
|
||||
// a slash (`/`) prefix, we add it here if it is missing.
|
||||
// for the known environments, we do not foresee how the socket-path
|
||||
// should miss the slash, however this extra if-condition is worth to
|
||||
// save future pain from innocent users.
|
||||
DockerSocketPath = "/" + DockerSocketPath
|
||||
}
|
||||
DockerSocketPathWithSchema = DockerSocketSchema + DockerSocketPath
|
||||
}
|
||||
}
|
126
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainersdocker/images.go
generated
vendored
Normal file
126
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainersdocker/images.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
package testcontainersdocker
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
IndexDockerIO = "https://index.docker.io/v1/"
|
||||
maxURLRuneCount = 2083
|
||||
minURLRuneCount = 3
|
||||
URLSchema = `((ftp|tcp|udp|wss?|https?):\/\/)`
|
||||
URLUsername = `(\S+(:\S*)?@)`
|
||||
URLIP = `([1-9]\d?|1\d\d|2[01]\d|22[0-3]|24\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-5]))`
|
||||
IP = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`
|
||||
URLSubdomain = `((www\.)|([a-zA-Z0-9]+([-_\.]?[a-zA-Z0-9])*[a-zA-Z0-9]\.[a-zA-Z0-9]+))`
|
||||
URLPath = `((\/|\?|#)[^\s]*)`
|
||||
URLPort = `(:(\d{1,5}))`
|
||||
URL = `^` + URLSchema + `?` + URLUsername + `?` + `((` + URLIP + `|(\[` + IP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + URLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + URLPort + `?` + URLPath + `?$`
|
||||
)
|
||||
|
||||
var rxURL = regexp.MustCompile(URL)
|
||||
|
||||
func ExtractImagesFromDockerfile(dockerfile string, buildArgs map[string]*string) ([]string, error) {
|
||||
var images []string
|
||||
|
||||
file, err := os.Open(dockerfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var lines []string
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
lines = append(lines, scanner.Text())
|
||||
}
|
||||
if scanner.Err() != nil {
|
||||
return nil, scanner.Err()
|
||||
}
|
||||
|
||||
// extract images from dockerfile
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if !strings.HasPrefix(strings.ToUpper(line), "FROM") {
|
||||
continue
|
||||
}
|
||||
|
||||
// remove FROM
|
||||
line = strings.TrimPrefix(line, "FROM")
|
||||
parts := strings.Split(strings.TrimSpace(line), " ")
|
||||
if len(parts) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// interpolate build args
|
||||
for k, v := range buildArgs {
|
||||
if v != nil {
|
||||
parts[0] = strings.Replace(parts[0], "${"+k+"}", *v, -1)
|
||||
}
|
||||
}
|
||||
images = append(images, parts[0])
|
||||
}
|
||||
|
||||
return images, nil
|
||||
}
|
||||
|
||||
// ExtractRegistry extracts the registry from the image name, using a regular expression to extract the registry from the image name.
|
||||
// regular expression to extract the registry from the image name
|
||||
// the regular expression is based on the grammar defined in
|
||||
// - image:tag
|
||||
// - image
|
||||
// - repository/image:tag
|
||||
// - repository/image
|
||||
// - registry/image:tag
|
||||
// - registry/image
|
||||
// - registry/repository/image:tag
|
||||
// - registry/repository/image
|
||||
// - registry:port/repository/image:tag
|
||||
// - registry:port/repository/image
|
||||
// - registry:port/image:tag
|
||||
// - registry:port/image
|
||||
// Once extracted the registry, it is validated to check if it is a valid URL or an IP address.
|
||||
func ExtractRegistry(image string, fallback string) string {
|
||||
exp := regexp.MustCompile(`^(?:(?P<registry>(https?://)?[^/]+)(?::(?P<port>\d+))?/)?(?:(?P<repository>[^/]+)/)?(?P<image>[^:]+)(?::(?P<tag>.+))?$`).FindStringSubmatch(image)
|
||||
if len(exp) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
registry := exp[1]
|
||||
|
||||
if IsURL(registry) {
|
||||
return registry
|
||||
}
|
||||
|
||||
return fallback
|
||||
}
|
||||
|
||||
// IsURL checks if the string is an URL.
|
||||
// Extracted from https://github.com/asaskevich/govalidator/blob/f21760c49a8d/validator.go#L104
|
||||
func IsURL(str string) bool {
|
||||
if str == "" || utf8.RuneCountInString(str) >= maxURLRuneCount || len(str) <= minURLRuneCount || strings.HasPrefix(str, ".") {
|
||||
return false
|
||||
}
|
||||
strTemp := str
|
||||
if strings.Contains(str, ":") && !strings.Contains(str, "://") {
|
||||
// support no indicated urlscheme but with colon for port number
|
||||
// http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString
|
||||
strTemp = "http://" + str
|
||||
}
|
||||
u, err := url.Parse(strTemp)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if strings.HasPrefix(u.Host, ".") {
|
||||
return false
|
||||
}
|
||||
if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {
|
||||
return false
|
||||
}
|
||||
return rxURL.MatchString(str)
|
||||
}
|
19
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainersdocker/labels.go
generated
vendored
Normal file
19
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainersdocker/labels.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package testcontainersdocker
|
||||
|
||||
import "github.com/testcontainers/testcontainers-go/internal"
|
||||
|
||||
const (
|
||||
LabelBase = "org.testcontainers"
|
||||
LabelLang = LabelBase + ".lang"
|
||||
LabelReaper = LabelBase + ".reaper"
|
||||
LabelSessionID = LabelBase + ".sessionId"
|
||||
LabelVersion = LabelBase + ".version"
|
||||
)
|
||||
|
||||
func DefaultLabels() map[string]string {
|
||||
return map[string]string{
|
||||
LabelBase: "true",
|
||||
LabelLang: "go",
|
||||
LabelVersion: internal.Version,
|
||||
}
|
||||
}
|
22
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainerssession/session.go
generated
vendored
Normal file
22
vendor/github.com/testcontainers/testcontainers-go/internal/testcontainerssession/session.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package testcontainerssession
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var id uuid.UUID
|
||||
var idOnce sync.Once
|
||||
|
||||
func ID() uuid.UUID {
|
||||
idOnce.Do(func() {
|
||||
id = uuid.New()
|
||||
})
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func String() string {
|
||||
return ID().String()
|
||||
}
|
4
vendor/github.com/testcontainers/testcontainers-go/internal/version.go
generated
vendored
Normal file
4
vendor/github.com/testcontainers/testcontainers-go/internal/version.go
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
package internal
|
||||
|
||||
// Version is the next development version of the application
|
||||
const Version = "0.23.0"
|
379
vendor/github.com/testcontainers/testcontainers-go/lifecycle.go
generated
vendored
Normal file
379
vendor/github.com/testcontainers/testcontainers-go/lifecycle.go
generated
vendored
Normal file
@@ -0,0 +1,379 @@
|
||||
package testcontainers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// ContainerRequestHook is a hook that will be called before a container is created.
|
||||
// It can be used to modify container configuration before it is created,
|
||||
// using the different lifecycle hooks that are available:
|
||||
// - Creating
|
||||
// For that, it will receive a ContainerRequest, modify it and return an error if needed.
|
||||
type ContainerRequestHook func(ctx context.Context, req ContainerRequest) error
|
||||
|
||||
// ContainerHook is a hook that will be called after a container is created
|
||||
// It can be used to modify the state of the container after it is created,
|
||||
// using the different lifecycle hooks that are available:
|
||||
// - Created
|
||||
// - Starting
|
||||
// - Started
|
||||
// - Stopping
|
||||
// - Stopped
|
||||
// - Terminating
|
||||
// - Terminated
|
||||
// For that, it will receive a Container, modify it and return an error if needed.
|
||||
type ContainerHook func(ctx context.Context, container Container) error
|
||||
|
||||
// ContainerLifecycleHooks is a struct that contains all the hooks that can be used
|
||||
// to modify the container lifecycle. All the container lifecycle hooks except the PreCreates hooks
|
||||
// will be passed to the container once it's created
|
||||
type ContainerLifecycleHooks struct {
|
||||
PreCreates []ContainerRequestHook
|
||||
PostCreates []ContainerHook
|
||||
PreStarts []ContainerHook
|
||||
PostStarts []ContainerHook
|
||||
PreStops []ContainerHook
|
||||
PostStops []ContainerHook
|
||||
PreTerminates []ContainerHook
|
||||
PostTerminates []ContainerHook
|
||||
}
|
||||
|
||||
var DefaultLoggingHook = func(logger Logging) ContainerLifecycleHooks {
|
||||
shortContainerID := func(c Container) string {
|
||||
return c.GetContainerID()[:12]
|
||||
}
|
||||
|
||||
return ContainerLifecycleHooks{
|
||||
PreCreates: []ContainerRequestHook{
|
||||
func(ctx context.Context, req ContainerRequest) error {
|
||||
logger.Printf("🐳 Creating container for image %s", req.Image)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
PostCreates: []ContainerHook{
|
||||
func(ctx context.Context, c Container) error {
|
||||
logger.Printf("✅ Container created: %s", shortContainerID(c))
|
||||
return nil
|
||||
},
|
||||
},
|
||||
PreStarts: []ContainerHook{
|
||||
func(ctx context.Context, c Container) error {
|
||||
logger.Printf("🐳 Starting container: %s", shortContainerID(c))
|
||||
return nil
|
||||
},
|
||||
},
|
||||
PostStarts: []ContainerHook{
|
||||
func(ctx context.Context, c Container) error {
|
||||
logger.Printf("✅ Container started: %s", shortContainerID(c))
|
||||
return nil
|
||||
},
|
||||
},
|
||||
PreStops: []ContainerHook{
|
||||
func(ctx context.Context, c Container) error {
|
||||
logger.Printf("🐳 Stopping container: %s", shortContainerID(c))
|
||||
return nil
|
||||
},
|
||||
},
|
||||
PostStops: []ContainerHook{
|
||||
func(ctx context.Context, c Container) error {
|
||||
logger.Printf("✋ Container stopped: %s", shortContainerID(c))
|
||||
return nil
|
||||
},
|
||||
},
|
||||
PreTerminates: []ContainerHook{
|
||||
func(ctx context.Context, c Container) error {
|
||||
logger.Printf("🐳 Terminating container: %s", shortContainerID(c))
|
||||
return nil
|
||||
},
|
||||
},
|
||||
PostTerminates: []ContainerHook{
|
||||
func(ctx context.Context, c Container) error {
|
||||
logger.Printf("🚫 Container terminated: %s", shortContainerID(c))
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// creatingHook is a hook that will be called before a container is created.
|
||||
func (req ContainerRequest) creatingHook(ctx context.Context) error {
|
||||
for _, lifecycleHooks := range req.LifecycleHooks {
|
||||
err := lifecycleHooks.Creating(ctx)(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createdHook is a hook that will be called after a container is created
|
||||
func (c *DockerContainer) createdHook(ctx context.Context) error {
|
||||
for _, lifecycleHooks := range c.lifecycleHooks {
|
||||
err := containerHookFn(ctx, lifecycleHooks.PostCreates)(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// startingHook is a hook that will be called before a container is started
|
||||
func (c *DockerContainer) startingHook(ctx context.Context) error {
|
||||
for _, lifecycleHooks := range c.lifecycleHooks {
|
||||
err := containerHookFn(ctx, lifecycleHooks.PreStarts)(c)
|
||||
if err != nil {
|
||||
c.printLogs(ctx)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// startedHook is a hook that will be called after a container is started
|
||||
func (c *DockerContainer) startedHook(ctx context.Context) error {
|
||||
for _, lifecycleHooks := range c.lifecycleHooks {
|
||||
err := containerHookFn(ctx, lifecycleHooks.PostStarts)(c)
|
||||
if err != nil {
|
||||
c.printLogs(ctx)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// printLogs is a helper function that will print the logs of a Docker container
|
||||
// We are going to use this helper function to inform the user of the logs when an error occurs
|
||||
func (c *DockerContainer) printLogs(ctx context.Context) {
|
||||
reader, err := c.Logs(ctx)
|
||||
if err != nil {
|
||||
c.logger.Printf("failed accessing container logs: %w\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
c.logger.Printf("failed reading container logs: %w\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.logger.Printf("container logs:\n%s", b)
|
||||
}
|
||||
|
||||
// stoppingHook is a hook that will be called before a container is stopped
|
||||
func (c *DockerContainer) stoppingHook(ctx context.Context) error {
|
||||
for _, lifecycleHooks := range c.lifecycleHooks {
|
||||
err := containerHookFn(ctx, lifecycleHooks.PreStops)(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// stoppedHook is a hook that will be called after a container is stopped
|
||||
func (c *DockerContainer) stoppedHook(ctx context.Context) error {
|
||||
for _, lifecycleHooks := range c.lifecycleHooks {
|
||||
err := containerHookFn(ctx, lifecycleHooks.PostStops)(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// terminatingHook is a hook that will be called before a container is terminated
|
||||
func (c *DockerContainer) terminatingHook(ctx context.Context) error {
|
||||
for _, lifecycleHooks := range c.lifecycleHooks {
|
||||
err := containerHookFn(ctx, lifecycleHooks.PreTerminates)(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// terminatedHook is a hook that will be called after a container is terminated
|
||||
func (c *DockerContainer) terminatedHook(ctx context.Context) error {
|
||||
for _, lifecycleHooks := range c.lifecycleHooks {
|
||||
err := containerHookFn(ctx, lifecycleHooks.PostTerminates)(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creating is a hook that will be called before a container is created.
|
||||
func (c ContainerLifecycleHooks) Creating(ctx context.Context) func(req ContainerRequest) error {
|
||||
return func(req ContainerRequest) error {
|
||||
for _, hook := range c.PreCreates {
|
||||
if err := hook(ctx, req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// containerHookFn is a helper function that will create a function to be returned by all the different
|
||||
// container lifecycle hooks. The created function will iterate over all the hooks and call them one by one.
|
||||
func containerHookFn(ctx context.Context, containerHook []ContainerHook) func(container Container) error {
|
||||
return func(container Container) error {
|
||||
for _, hook := range containerHook {
|
||||
if err := hook(ctx, container); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Created is a hook that will be called after a container is created
|
||||
func (c ContainerLifecycleHooks) Created(ctx context.Context) func(container Container) error {
|
||||
return containerHookFn(ctx, c.PostCreates)
|
||||
}
|
||||
|
||||
// Starting is a hook that will be called before a container is started
|
||||
func (c ContainerLifecycleHooks) Starting(ctx context.Context) func(container Container) error {
|
||||
return containerHookFn(ctx, c.PreStarts)
|
||||
}
|
||||
|
||||
// Started is a hook that will be called after a container is started
|
||||
func (c ContainerLifecycleHooks) Started(ctx context.Context) func(container Container) error {
|
||||
return containerHookFn(ctx, c.PostStarts)
|
||||
}
|
||||
|
||||
// Stopping is a hook that will be called before a container is stopped
|
||||
func (c ContainerLifecycleHooks) Stopping(ctx context.Context) func(container Container) error {
|
||||
return containerHookFn(ctx, c.PreStops)
|
||||
}
|
||||
|
||||
// Stopped is a hook that will be called after a container is stopped
|
||||
func (c ContainerLifecycleHooks) Stopped(ctx context.Context) func(container Container) error {
|
||||
return containerHookFn(ctx, c.PostStops)
|
||||
}
|
||||
|
||||
// Terminating is a hook that will be called before a container is terminated
|
||||
func (c ContainerLifecycleHooks) Terminating(ctx context.Context) func(container Container) error {
|
||||
return containerHookFn(ctx, c.PreTerminates)
|
||||
}
|
||||
|
||||
// Terminated is a hook that will be called after a container is terminated
|
||||
func (c ContainerLifecycleHooks) Terminated(ctx context.Context) func(container Container) error {
|
||||
return containerHookFn(ctx, c.PostTerminates)
|
||||
}
|
||||
|
||||
func (p *DockerProvider) preCreateContainerHook(ctx context.Context, req ContainerRequest, dockerInput *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig) error {
|
||||
// prepare mounts
|
||||
hostConfig.Mounts = mapToDockerMounts(req.Mounts)
|
||||
|
||||
endpointSettings := map[string]*network.EndpointSettings{}
|
||||
|
||||
// #248: Docker allows only one network to be specified during container creation
|
||||
// If there is more than one network specified in the request container should be attached to them
|
||||
// once it is created. We will take a first network if any specified in the request and use it to create container
|
||||
if len(req.Networks) > 0 {
|
||||
attachContainerTo := req.Networks[0]
|
||||
|
||||
nw, err := p.GetNetwork(ctx, NetworkRequest{
|
||||
Name: attachContainerTo,
|
||||
})
|
||||
if err == nil {
|
||||
aliases := []string{}
|
||||
if _, ok := req.NetworkAliases[attachContainerTo]; ok {
|
||||
aliases = req.NetworkAliases[attachContainerTo]
|
||||
}
|
||||
endpointSetting := network.EndpointSettings{
|
||||
Aliases: aliases,
|
||||
NetworkID: nw.ID,
|
||||
}
|
||||
endpointSettings[attachContainerTo] = &endpointSetting
|
||||
}
|
||||
}
|
||||
|
||||
if req.ConfigModifier != nil {
|
||||
req.ConfigModifier(dockerInput)
|
||||
}
|
||||
|
||||
if req.HostConfigModifier == nil {
|
||||
req.HostConfigModifier = defaultHostConfigModifier(req)
|
||||
}
|
||||
req.HostConfigModifier(hostConfig)
|
||||
|
||||
if req.EnpointSettingsModifier != nil {
|
||||
req.EnpointSettingsModifier(endpointSettings)
|
||||
}
|
||||
|
||||
networkingConfig.EndpointsConfig = endpointSettings
|
||||
|
||||
exposedPorts := req.ExposedPorts
|
||||
// this check must be done after the pre-creation Modifiers are called, so the network mode is already set
|
||||
if len(exposedPorts) == 0 && !hostConfig.NetworkMode.IsContainer() {
|
||||
image, _, err := p.client.ImageInspectWithRaw(ctx, dockerInput.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for p := range image.ContainerConfig.ExposedPorts {
|
||||
exposedPorts = append(exposedPorts, string(p))
|
||||
}
|
||||
}
|
||||
|
||||
exposedPortSet, exposedPortMap, err := nat.ParsePortSpecs(exposedPorts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dockerInput.ExposedPorts = exposedPortSet
|
||||
|
||||
// only exposing those ports automatically if the container request exposes zero ports and the container does not run in a container network
|
||||
if len(exposedPorts) == 0 && !hostConfig.NetworkMode.IsContainer() {
|
||||
hostConfig.PortBindings = exposedPortMap
|
||||
} else {
|
||||
hostConfig.PortBindings = mergePortBindings(hostConfig.PortBindings, exposedPortMap, req.ExposedPorts)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergePortBindings(configPortMap, exposedPortMap nat.PortMap, exposedPorts []string) nat.PortMap {
|
||||
if exposedPortMap == nil {
|
||||
exposedPortMap = make(map[nat.Port][]nat.PortBinding)
|
||||
}
|
||||
|
||||
for k, v := range configPortMap {
|
||||
if slices.Contains(exposedPorts, strings.Split(string(k), "/")[0]) {
|
||||
exposedPortMap[k] = v
|
||||
}
|
||||
}
|
||||
return exposedPortMap
|
||||
}
|
||||
|
||||
// defaultHostConfigModifier provides a default modifier including the deprecated fields
|
||||
func defaultHostConfigModifier(req ContainerRequest) func(hostConfig *container.HostConfig) {
|
||||
return func(hostConfig *container.HostConfig) {
|
||||
hostConfig.AutoRemove = req.AutoRemove
|
||||
hostConfig.CapAdd = req.CapAdd
|
||||
hostConfig.CapDrop = req.CapDrop
|
||||
hostConfig.Binds = req.Binds
|
||||
hostConfig.ExtraHosts = req.ExtraHosts
|
||||
hostConfig.NetworkMode = req.NetworkMode
|
||||
hostConfig.Resources = req.Resources
|
||||
}
|
||||
}
|
81
vendor/github.com/testcontainers/testcontainers-go/logger.go
generated
vendored
Normal file
81
vendor/github.com/testcontainers/testcontainers-go/logger.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package testcontainers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
|
||||
)
|
||||
|
||||
// Logger is the default log instance
|
||||
var Logger Logging = log.New(os.Stderr, "", log.LstdFlags)
|
||||
|
||||
// Logging defines the Logger interface
|
||||
type Logging interface {
|
||||
Printf(format string, v ...interface{})
|
||||
}
|
||||
|
||||
// LogDockerServerInfo logs the docker server info using the provided logger and Docker client
|
||||
func LogDockerServerInfo(ctx context.Context, client client.APIClient, logger Logging) {
|
||||
infoMessage := `%v - Connected to docker:
|
||||
Server Version: %v
|
||||
API Version: %v
|
||||
Operating System: %v
|
||||
Total Memory: %v MB
|
||||
Resolved Docker Host: %s
|
||||
Resolved Docker Socket Path: %s
|
||||
`
|
||||
|
||||
info, err := client.Info(ctx)
|
||||
if err != nil {
|
||||
logger.Printf("failed getting information about docker server: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Printf(infoMessage, packagePath,
|
||||
info.ServerVersion, client.ClientVersion(),
|
||||
info.OperatingSystem, info.MemTotal/1024/1024,
|
||||
testcontainersdocker.ExtractDockerHost(ctx),
|
||||
testcontainersdocker.ExtractDockerSocket(ctx),
|
||||
)
|
||||
}
|
||||
|
||||
// TestLogger returns a Logging implementation for testing.TB
|
||||
// This way logs from testcontainers are part of the test output of a test suite or test case
|
||||
func TestLogger(tb testing.TB) Logging {
|
||||
tb.Helper()
|
||||
return testLogger{TB: tb}
|
||||
}
|
||||
|
||||
// WithLogger is a generic option that implements GenericProviderOption, DockerProviderOption
|
||||
// It replaces the global Logging implementation with a user defined one e.g. to aggregate logs from testcontainers
|
||||
// with the logs of specific test case
|
||||
func WithLogger(logger Logging) LoggerOption {
|
||||
return LoggerOption{
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
type LoggerOption struct {
|
||||
logger Logging
|
||||
}
|
||||
|
||||
func (o LoggerOption) ApplyGenericTo(opts *GenericProviderOptions) {
|
||||
opts.Logger = o.logger
|
||||
}
|
||||
|
||||
func (o LoggerOption) ApplyDockerTo(opts *DockerProviderOptions) {
|
||||
opts.Logger = o.logger
|
||||
}
|
||||
|
||||
type testLogger struct {
|
||||
testing.TB
|
||||
}
|
||||
|
||||
func (t testLogger) Printf(format string, v ...interface{}) {
|
||||
t.Helper()
|
||||
t.Logf(format, v...)
|
||||
}
|
114
vendor/github.com/testcontainers/testcontainers-go/mkdocs.yml
generated
vendored
114
vendor/github.com/testcontainers/testcontainers-go/mkdocs.yml
generated
vendored
@@ -1,39 +1,109 @@
|
||||
site_name: Testcontainers-Go
|
||||
site_name: Testcontainers for Go
|
||||
site_url: https://golang.testcontainers.org
|
||||
plugins:
|
||||
- search
|
||||
- codeinclude
|
||||
- markdownextradata
|
||||
theme:
|
||||
name: 'material'
|
||||
name: material
|
||||
custom_dir: docs/theme
|
||||
palette:
|
||||
primary: 'blue'
|
||||
accent: 'teal'
|
||||
font: false
|
||||
logo: 'logo.svg'
|
||||
favicon: 'favicon.ico'
|
||||
scheme: testcontainers
|
||||
font:
|
||||
text: Roboto
|
||||
code: Roboto Mono
|
||||
logo: logo.svg
|
||||
favicon: favicon.ico
|
||||
extra_css:
|
||||
- 'css/extra.css'
|
||||
repo_name: 'testcontainers-go'
|
||||
repo_url: 'https://github.com/testcontainers/testcontainers-go'
|
||||
- css/extra.css
|
||||
- css/tc-header.css
|
||||
repo_name: testcontainers-go
|
||||
repo_url: https://github.com/testcontainers/testcontainers-go
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- codehilite:
|
||||
linenums: False
|
||||
linenums: false
|
||||
- pymdownx.superfences
|
||||
- pymdownx.tabbed:
|
||||
alternate_style: true
|
||||
- pymdownx.snippets
|
||||
- toc:
|
||||
permalink: true
|
||||
- attr_list
|
||||
- pymdownx.emoji:
|
||||
emoji_index: !!python/name:materialx.emoji.twemoji
|
||||
emoji_generator: !!python/name:materialx.emoji.to_svg
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Quickstart:
|
||||
- quickstart/gotest.md
|
||||
- Quickstart: quickstart.md
|
||||
- Features:
|
||||
- features/creating_container.md
|
||||
- features/garbage_collector.md
|
||||
- features/build_from_dockerfile.md
|
||||
- features/docker_compose.md
|
||||
- features/follow_logs.md
|
||||
- features/override_container_command.md
|
||||
- features/creating_container.md
|
||||
- features/files_and_mounts.md
|
||||
- features/configuration.md
|
||||
- features/networking.md
|
||||
- features/garbage_collector.md
|
||||
- features/build_from_dockerfile.md
|
||||
- features/docker_auth.md
|
||||
- features/docker_compose.md
|
||||
- features/follow_logs.md
|
||||
- features/override_container_command.md
|
||||
- features/copy_file.md
|
||||
- Wait Strategies:
|
||||
- Introduction: features/wait/introduction.md
|
||||
- Exec: features/wait/exec.md
|
||||
- Exit: features/wait/exit.md
|
||||
- Health: features/wait/health.md
|
||||
- HostPort: features/wait/host_port.md
|
||||
- HTTP: features/wait/http.md
|
||||
- Log: features/wait/log.md
|
||||
- Multi: features/wait/multi.md
|
||||
- SQL: features/wait/sql.md
|
||||
- Modules:
|
||||
- modules/index.md
|
||||
- modules/artemis.md
|
||||
- modules/clickhouse.md
|
||||
- modules/couchbase.md
|
||||
- modules/k3s.md
|
||||
- modules/localstack.md
|
||||
- modules/mongodb.md
|
||||
- modules/mysql.md
|
||||
- modules/neo4j.md
|
||||
- modules/postgres.md
|
||||
- modules/pulsar.md
|
||||
- modules/redis.md
|
||||
- modules/redpanda.md
|
||||
- modules/vault.md
|
||||
- Examples:
|
||||
- examples/index.md
|
||||
- examples/bigtable.md
|
||||
- examples/cockroachdb.md
|
||||
- examples/consul.md
|
||||
- examples/datastore.md
|
||||
- examples/firestore.md
|
||||
- examples/nats.md
|
||||
- examples/nginx.md
|
||||
- examples/pubsub.md
|
||||
- examples/spanner.md
|
||||
- examples/toxiproxy.md
|
||||
- System Requirements:
|
||||
- system_requirements/index.md
|
||||
- system_requirements/docker.md
|
||||
- Continuous Integration:
|
||||
- system_requirements/ci/aws_codebuild.md
|
||||
- system_requirements/ci/bitbucket_pipelines.md
|
||||
- system_requirements/ci/circle_ci.md
|
||||
- system_requirements/ci/concourse_ci.md
|
||||
- system_requirements/ci/dind_patterns.md
|
||||
- system_requirements/ci/drone.md
|
||||
- system_requirements/ci/gitlab_ci.md
|
||||
- system_requirements/ci/tekton.md
|
||||
- system_requirements/ci/travis.md
|
||||
- system_requirements/using_colima.md
|
||||
- system_requirements/using_podman.md
|
||||
- Contributing:
|
||||
- contributing.md
|
||||
- contributing_docs.md
|
||||
- contributing.md
|
||||
- contributing_docs.md
|
||||
- Getting help: getting_help.md
|
||||
edit_uri: edit/main/docs/
|
||||
extra:
|
||||
latest_version: 1.14.1
|
||||
latest_version: v0.23.0
|
||||
|
116
vendor/github.com/testcontainers/testcontainers-go/mounts.go
generated
vendored
Normal file
116
vendor/github.com/testcontainers/testcontainers-go/mounts.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package testcontainers
|
||||
|
||||
const (
|
||||
MountTypeBind MountType = iota
|
||||
MountTypeVolume
|
||||
MountTypeTmpfs
|
||||
MountTypePipe
|
||||
)
|
||||
|
||||
var (
|
||||
_ ContainerMountSource = (*GenericBindMountSource)(nil)
|
||||
_ ContainerMountSource = (*GenericVolumeMountSource)(nil)
|
||||
_ ContainerMountSource = (*GenericTmpfsMountSource)(nil)
|
||||
)
|
||||
|
||||
type (
|
||||
// ContainerMounts represents a collection of mounts for a container
|
||||
ContainerMounts []ContainerMount
|
||||
MountType uint
|
||||
)
|
||||
|
||||
// ContainerMountSource is the base for all mount sources
|
||||
type ContainerMountSource interface {
|
||||
// Source will be used as Source field in the final mount
|
||||
// this might either be a volume name, a host path or might be empty e.g. for Tmpfs
|
||||
Source() string
|
||||
|
||||
// Type determines the final mount type
|
||||
// possible options are limited by the Docker API
|
||||
Type() MountType
|
||||
}
|
||||
|
||||
// GenericBindMountSource implements ContainerMountSource and represents a bind mount
|
||||
// Optionally mount.BindOptions might be added for advanced scenarios
|
||||
type GenericBindMountSource struct {
|
||||
// HostPath is the path mounted into the container
|
||||
// the same host path might be mounted to multiple locations within a single container
|
||||
HostPath string
|
||||
}
|
||||
|
||||
func (s GenericBindMountSource) Source() string {
|
||||
return s.HostPath
|
||||
}
|
||||
|
||||
func (GenericBindMountSource) Type() MountType {
|
||||
return MountTypeBind
|
||||
}
|
||||
|
||||
// GenericVolumeMountSource implements ContainerMountSource and represents a volume mount
|
||||
type GenericVolumeMountSource struct {
|
||||
// Name refers to the name of the volume to be mounted
|
||||
// the same volume might be mounted to multiple locations within a single container
|
||||
Name string
|
||||
}
|
||||
|
||||
func (s GenericVolumeMountSource) Source() string {
|
||||
return s.Name
|
||||
}
|
||||
|
||||
func (GenericVolumeMountSource) Type() MountType {
|
||||
return MountTypeVolume
|
||||
}
|
||||
|
||||
// GenericTmpfsMountSource implements ContainerMountSource and represents a TmpFS mount
|
||||
// Optionally mount.TmpfsOptions might be added for advanced scenarios
|
||||
type GenericTmpfsMountSource struct {
|
||||
}
|
||||
|
||||
func (s GenericTmpfsMountSource) Source() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (GenericTmpfsMountSource) Type() MountType {
|
||||
return MountTypeTmpfs
|
||||
}
|
||||
|
||||
// ContainerMountTarget represents the target path within a container where the mount will be available
|
||||
// Note that mount targets must be unique. It's not supported to mount different sources to the same target.
|
||||
type ContainerMountTarget string
|
||||
|
||||
func (t ContainerMountTarget) Target() string {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
// BindMount returns a new ContainerMount with a GenericBindMountSource as source
|
||||
// This is a convenience method to cover typical use cases.
|
||||
func BindMount(hostPath string, mountTarget ContainerMountTarget) ContainerMount {
|
||||
return ContainerMount{
|
||||
Source: GenericBindMountSource{HostPath: hostPath},
|
||||
Target: mountTarget,
|
||||
}
|
||||
}
|
||||
|
||||
// VolumeMount returns a new ContainerMount with a GenericVolumeMountSource as source
|
||||
// This is a convenience method to cover typical use cases.
|
||||
func VolumeMount(volumeName string, mountTarget ContainerMountTarget) ContainerMount {
|
||||
return ContainerMount{
|
||||
Source: GenericVolumeMountSource{Name: volumeName},
|
||||
Target: mountTarget,
|
||||
}
|
||||
}
|
||||
|
||||
// Mounts returns a ContainerMounts to support a more fluent API
|
||||
func Mounts(mounts ...ContainerMount) ContainerMounts {
|
||||
return mounts
|
||||
}
|
||||
|
||||
// ContainerMount models a mount into a container
|
||||
type ContainerMount struct {
|
||||
// Source is typically either a GenericBindMountSource or a GenericVolumeMountSource
|
||||
Source ContainerMountSource
|
||||
// Target is the path where the mount should be mounted within the container
|
||||
Target ContainerMountTarget
|
||||
// ReadOnly determines if the mount should be read-only
|
||||
ReadOnly bool
|
||||
}
|
17
vendor/github.com/testcontainers/testcontainers-go/network.go
generated
vendored
17
vendor/github.com/testcontainers/testcontainers-go/network.go
generated
vendored
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
)
|
||||
|
||||
// NetworkProvider allows the creation of networks on an arbitrary system
|
||||
@@ -17,6 +18,16 @@ type Network interface {
|
||||
Remove(context.Context) error // removes the network
|
||||
}
|
||||
|
||||
type DefaultNetwork string
|
||||
|
||||
func (n DefaultNetwork) ApplyGenericTo(opts *GenericProviderOptions) {
|
||||
opts.DefaultNetwork = string(n)
|
||||
}
|
||||
|
||||
func (n DefaultNetwork) ApplyDockerTo(opts *DockerProviderOptions) {
|
||||
opts.DefaultNetwork = string(n)
|
||||
}
|
||||
|
||||
// NetworkRequest represents the parameters used to get a network
|
||||
type NetworkRequest struct {
|
||||
Driver string
|
||||
@@ -26,7 +37,9 @@ type NetworkRequest struct {
|
||||
Name string
|
||||
Labels map[string]string
|
||||
Attachable bool
|
||||
IPAM *network.IPAM
|
||||
|
||||
SkipReaper bool // indicates whether we skip setting up a reaper for this
|
||||
ReaperImage string //alternative reaper registry
|
||||
SkipReaper bool // Deprecated: The reaper is globally controlled by the .testcontainers.properties file or the TESTCONTAINERS_RYUK_DISABLED environment variable
|
||||
ReaperImage string // Deprecated: use WithImageName ContainerOption instead. Alternative reaper registry
|
||||
ReaperOptions []ContainerOption // Reaper options to use for this network
|
||||
}
|
||||
|
122
vendor/github.com/testcontainers/testcontainers-go/parallel.go
generated
vendored
Normal file
122
vendor/github.com/testcontainers/testcontainers-go/parallel.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
package testcontainers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultWorkersCount = 8
|
||||
)
|
||||
|
||||
type ParallelContainerRequest []GenericContainerRequest
|
||||
|
||||
// ParallelContainersOptions represents additional options for parallel running
|
||||
type ParallelContainersOptions struct {
|
||||
WorkersCount int // count of parallel workers. If field empty(zero), default value will be 'defaultWorkersCount'
|
||||
}
|
||||
|
||||
// ParallelContainersRequestError represents error from parallel request
|
||||
type ParallelContainersRequestError struct {
|
||||
Request GenericContainerRequest
|
||||
Error error
|
||||
}
|
||||
|
||||
type ParallelContainersError struct {
|
||||
Errors []ParallelContainersRequestError
|
||||
}
|
||||
|
||||
func (gpe ParallelContainersError) Error() string {
|
||||
return fmt.Sprintf("%v", gpe.Errors)
|
||||
}
|
||||
|
||||
func parallelContainersRunner(
|
||||
ctx context.Context,
|
||||
requests <-chan GenericContainerRequest,
|
||||
errors chan<- ParallelContainersRequestError,
|
||||
containers chan<- Container,
|
||||
wg *sync.WaitGroup) {
|
||||
|
||||
for req := range requests {
|
||||
c, err := GenericContainer(ctx, req)
|
||||
if err != nil {
|
||||
errors <- ParallelContainersRequestError{
|
||||
Request: req,
|
||||
Error: err,
|
||||
}
|
||||
continue
|
||||
}
|
||||
containers <- c
|
||||
}
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
// ParallelContainers creates a generic containers with parameters and run it in parallel mode
|
||||
func ParallelContainers(ctx context.Context, reqs ParallelContainerRequest, opt ParallelContainersOptions) ([]Container, error) {
|
||||
if opt.WorkersCount == 0 {
|
||||
opt.WorkersCount = defaultWorkersCount
|
||||
}
|
||||
|
||||
tasksChanSize := opt.WorkersCount
|
||||
if tasksChanSize > len(reqs) {
|
||||
tasksChanSize = len(reqs)
|
||||
}
|
||||
|
||||
tasksChan := make(chan GenericContainerRequest, tasksChanSize)
|
||||
errsChan := make(chan ParallelContainersRequestError)
|
||||
resChan := make(chan Container)
|
||||
waitRes := make(chan struct{})
|
||||
|
||||
containers := make([]Container, 0)
|
||||
errors := make([]ParallelContainersRequestError, 0)
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(tasksChanSize)
|
||||
|
||||
// run workers
|
||||
for i := 0; i < tasksChanSize; i++ {
|
||||
go parallelContainersRunner(ctx, tasksChan, errsChan, resChan, &wg)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case c, ok := <-resChan:
|
||||
if !ok {
|
||||
resChan = nil
|
||||
} else {
|
||||
containers = append(containers, c)
|
||||
}
|
||||
case e, ok := <-errsChan:
|
||||
if !ok {
|
||||
errsChan = nil
|
||||
} else {
|
||||
errors = append(errors, e)
|
||||
}
|
||||
}
|
||||
|
||||
if resChan == nil && errsChan == nil {
|
||||
waitRes <- struct{}{}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
for _, req := range reqs {
|
||||
tasksChan <- req
|
||||
}
|
||||
close(tasksChan)
|
||||
wg.Wait()
|
||||
close(resChan)
|
||||
close(errsChan)
|
||||
|
||||
<-waitRes
|
||||
|
||||
if len(errors) != 0 {
|
||||
return containers, ParallelContainersError{Errors: errors}
|
||||
}
|
||||
|
||||
return containers, nil
|
||||
}
|
163
vendor/github.com/testcontainers/testcontainers-go/provider.go
generated
vendored
Normal file
163
vendor/github.com/testcontainers/testcontainers-go/provider.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
package testcontainers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
|
||||
)
|
||||
|
||||
// possible provider types
|
||||
const (
|
||||
ProviderDefault ProviderType = iota // default will auto-detect provider from DOCKER_HOST environment variable
|
||||
ProviderDocker
|
||||
ProviderPodman
|
||||
)
|
||||
|
||||
type (
|
||||
// ProviderType is an enum for the possible providers
|
||||
ProviderType int
|
||||
|
||||
// GenericProviderOptions defines options applicable to all providers
|
||||
GenericProviderOptions struct {
|
||||
Logger Logging
|
||||
DefaultNetwork string
|
||||
}
|
||||
|
||||
// GenericProviderOption defines a common interface to modify GenericProviderOptions
|
||||
// These options can be passed to GetProvider in a variadic way to customize the returned GenericProvider instance
|
||||
GenericProviderOption interface {
|
||||
ApplyGenericTo(opts *GenericProviderOptions)
|
||||
}
|
||||
|
||||
// GenericProviderOptionFunc is a shorthand to implement the GenericProviderOption interface
|
||||
GenericProviderOptionFunc func(opts *GenericProviderOptions)
|
||||
|
||||
// DockerProviderOptions defines options applicable to DockerProvider
|
||||
DockerProviderOptions struct {
|
||||
defaultBridgeNetworkName string
|
||||
*GenericProviderOptions
|
||||
}
|
||||
|
||||
// DockerProviderOption defines a common interface to modify DockerProviderOptions
|
||||
// These can be passed to NewDockerProvider in a variadic way to customize the returned DockerProvider instance
|
||||
DockerProviderOption interface {
|
||||
ApplyDockerTo(opts *DockerProviderOptions)
|
||||
}
|
||||
|
||||
// DockerProviderOptionFunc is a shorthand to implement the DockerProviderOption interface
|
||||
DockerProviderOptionFunc func(opts *DockerProviderOptions)
|
||||
)
|
||||
|
||||
func (f DockerProviderOptionFunc) ApplyDockerTo(opts *DockerProviderOptions) {
|
||||
f(opts)
|
||||
}
|
||||
|
||||
func Generic2DockerOptions(opts ...GenericProviderOption) []DockerProviderOption {
|
||||
converted := make([]DockerProviderOption, 0, len(opts))
|
||||
for _, o := range opts {
|
||||
switch c := o.(type) {
|
||||
case DockerProviderOption:
|
||||
converted = append(converted, c)
|
||||
default:
|
||||
converted = append(converted, DockerProviderOptionFunc(func(opts *DockerProviderOptions) {
|
||||
o.ApplyGenericTo(opts.GenericProviderOptions)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
return converted
|
||||
}
|
||||
|
||||
func WithDefaultBridgeNetwork(bridgeNetworkName string) DockerProviderOption {
|
||||
return DockerProviderOptionFunc(func(opts *DockerProviderOptions) {
|
||||
opts.defaultBridgeNetworkName = bridgeNetworkName
|
||||
})
|
||||
}
|
||||
|
||||
func (f GenericProviderOptionFunc) ApplyGenericTo(opts *GenericProviderOptions) {
|
||||
f(opts)
|
||||
}
|
||||
|
||||
// ContainerProvider allows the creation of containers on an arbitrary system
|
||||
type ContainerProvider interface {
|
||||
Close() error // close the provider
|
||||
CreateContainer(context.Context, ContainerRequest) (Container, error) // create a container without starting it
|
||||
ReuseOrCreateContainer(context.Context, ContainerRequest) (Container, error) // reuses a container if it exists or creates a container without starting
|
||||
RunContainer(context.Context, ContainerRequest) (Container, error) // create a container and start it
|
||||
Health(context.Context) error
|
||||
Config() TestcontainersConfig
|
||||
}
|
||||
|
||||
// GetProvider provides the provider implementation for a certain type
|
||||
func (t ProviderType) GetProvider(opts ...GenericProviderOption) (GenericProvider, error) {
|
||||
opt := &GenericProviderOptions{
|
||||
Logger: Logger,
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o.ApplyGenericTo(opt)
|
||||
}
|
||||
|
||||
pt := t
|
||||
if pt == ProviderDefault && strings.Contains(os.Getenv("DOCKER_HOST"), "podman.sock") {
|
||||
pt = ProviderPodman
|
||||
}
|
||||
|
||||
switch pt {
|
||||
case ProviderDefault, ProviderDocker:
|
||||
providerOptions := append(Generic2DockerOptions(opts...), WithDefaultBridgeNetwork(Bridge))
|
||||
provider, err := NewDockerProvider(providerOptions...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w, failed to create Docker provider", err)
|
||||
}
|
||||
return provider, nil
|
||||
case ProviderPodman:
|
||||
providerOptions := append(Generic2DockerOptions(opts...), WithDefaultBridgeNetwork(Podman))
|
||||
provider, err := NewDockerProvider(providerOptions...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w, failed to create Docker provider", err)
|
||||
}
|
||||
return provider, nil
|
||||
}
|
||||
return nil, errors.New("unknown provider")
|
||||
}
|
||||
|
||||
// NewDockerProvider creates a Docker provider with the EnvClient
|
||||
func NewDockerProvider(provOpts ...DockerProviderOption) (*DockerProvider, error) {
|
||||
o := &DockerProviderOptions{
|
||||
GenericProviderOptions: &GenericProviderOptions{
|
||||
Logger: Logger,
|
||||
},
|
||||
}
|
||||
|
||||
for idx := range provOpts {
|
||||
provOpts[idx].ApplyDockerTo(o)
|
||||
}
|
||||
|
||||
c, err := NewDockerClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tcConfig := ReadConfig()
|
||||
|
||||
dockerHost := testcontainersdocker.ExtractDockerHost(context.Background())
|
||||
|
||||
p := &DockerProvider{
|
||||
DockerProviderOptions: o,
|
||||
host: dockerHost,
|
||||
client: c,
|
||||
config: tcConfig,
|
||||
}
|
||||
|
||||
// log docker server info only once
|
||||
logOnce.Do(func() {
|
||||
LogDockerServerInfo(context.Background(), p.client, p.Logger)
|
||||
})
|
||||
|
||||
return p, nil
|
||||
}
|
142
vendor/github.com/testcontainers/testcontainers-go/reaper.go
generated
vendored
142
vendor/github.com/testcontainers/testcontainers-go/reaper.go
generated
vendored
@@ -4,81 +4,128 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
)
|
||||
|
||||
const (
|
||||
TestcontainerLabel = "org.testcontainers.golang"
|
||||
// Deprecated: it has been replaced by the internal testcontainersdocker.LabelLang
|
||||
TestcontainerLabel = "org.testcontainers.golang"
|
||||
// Deprecated: it has been replaced by the internal testcontainersdocker.LabelSessionID
|
||||
TestcontainerLabelSessionID = TestcontainerLabel + ".sessionId"
|
||||
TestcontainerLabelIsReaper = TestcontainerLabel + ".reaper"
|
||||
// Deprecated: it has been replaced by the internal testcontainersdocker.LabelReaper
|
||||
TestcontainerLabelIsReaper = TestcontainerLabel + ".reaper"
|
||||
|
||||
ReaperDefaultImage = "quay.io/testcontainers/ryuk:0.2.3"
|
||||
ReaperDefaultImage = "docker.io/testcontainers/ryuk:0.5.1"
|
||||
)
|
||||
|
||||
var reaper *Reaper // We would like to create reaper only once
|
||||
var mutex sync.Mutex
|
||||
var (
|
||||
reaperInstance *Reaper // We would like to create reaper only once
|
||||
mutex sync.Mutex
|
||||
)
|
||||
|
||||
// ReaperProvider represents a provider for the reaper to run itself with
|
||||
// The ContainerProvider interface should usually satisfy this as well, so it is pluggable
|
||||
type ReaperProvider interface {
|
||||
RunContainer(ctx context.Context, req ContainerRequest) (Container, error)
|
||||
}
|
||||
|
||||
// Reaper is used to start a sidecar container that cleans up resources
|
||||
type Reaper struct {
|
||||
Provider ReaperProvider
|
||||
SessionID string
|
||||
Endpoint string
|
||||
Config() TestcontainersConfig
|
||||
}
|
||||
|
||||
// NewReaper creates a Reaper with a sessionID to identify containers and a provider to use
|
||||
// Deprecated: it's not possible to create a reaper anymore.
|
||||
func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, reaperImageName string) (*Reaper, error) {
|
||||
return reuseOrCreateReaper(ctx, sessionID, provider, WithImageName(reaperImageName))
|
||||
}
|
||||
|
||||
// reuseOrCreateReaper returns an existing Reaper instance if it exists and is running. Otherwise, a new Reaper instance
|
||||
// will be created with a sessionID to identify containers and a provider to use
|
||||
func reuseOrCreateReaper(ctx context.Context, sessionID string, provider ReaperProvider, opts ...ContainerOption) (*Reaper, error) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
// If reaper already exists re-use it
|
||||
if reaper != nil {
|
||||
return reaper, nil
|
||||
// If reaper already exists and healthy, re-use it
|
||||
if reaperInstance != nil {
|
||||
// Verify this instance is still running by checking state.
|
||||
// Can't use Container.IsRunning because the bool is not updated when Reaper is terminated
|
||||
state, err := reaperInstance.container.State(ctx)
|
||||
if err == nil && state.Running {
|
||||
return reaperInstance, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise create a new one
|
||||
reaper = &Reaper{
|
||||
r, err := newReaper(ctx, sessionID, provider, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reaperInstance = r
|
||||
return reaperInstance, nil
|
||||
}
|
||||
|
||||
// newReaper creates a Reaper with a sessionID to identify containers and a provider to use
|
||||
// Should only be used internally and instead use reuseOrCreateReaper to prefer reusing an existing Reaper instance
|
||||
func newReaper(ctx context.Context, sessionID string, provider ReaperProvider, opts ...ContainerOption) (*Reaper, error) {
|
||||
dockerHostMount := testcontainersdocker.ExtractDockerSocket(ctx)
|
||||
|
||||
reaper := &Reaper{
|
||||
Provider: provider,
|
||||
SessionID: sessionID,
|
||||
}
|
||||
|
||||
listeningPort := nat.Port("8080/tcp")
|
||||
|
||||
tcConfig := provider.Config().Config
|
||||
|
||||
reaperOpts := containerOptions{}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(&reaperOpts)
|
||||
}
|
||||
|
||||
req := ContainerRequest{
|
||||
Image: reaperImage(reaperImageName),
|
||||
Image: reaperImage(reaperOpts.ImageName),
|
||||
ExposedPorts: []string{string(listeningPort)},
|
||||
Labels: map[string]string{
|
||||
TestcontainerLabel: "true",
|
||||
TestcontainerLabelIsReaper: "true",
|
||||
TestcontainerLabelIsReaper: "true",
|
||||
testcontainersdocker.LabelReaper: "true",
|
||||
},
|
||||
SkipReaper: true,
|
||||
BindMounts: map[string]string{
|
||||
"/var/run/docker.sock": "/var/run/docker.sock",
|
||||
Mounts: Mounts(BindMount(dockerHostMount, "/var/run/docker.sock")),
|
||||
Privileged: tcConfig.RyukPrivileged,
|
||||
WaitingFor: wait.ForListeningPort(listeningPort),
|
||||
ReaperOptions: opts,
|
||||
HostConfigModifier: func(hc *container.HostConfig) {
|
||||
hc.AutoRemove = true
|
||||
hc.NetworkMode = Bridge
|
||||
},
|
||||
AutoRemove: true,
|
||||
WaitingFor: wait.ForListeningPort(listeningPort),
|
||||
}
|
||||
|
||||
// keep backwards compatibility
|
||||
req.ReaperImage = req.Image
|
||||
|
||||
// include reaper-specific labels to the reaper container
|
||||
for k, v := range reaper.Labels() {
|
||||
if k == TestcontainerLabelSessionID || k == testcontainersdocker.LabelSessionID {
|
||||
continue
|
||||
}
|
||||
req.Labels[k] = v
|
||||
}
|
||||
|
||||
// Attach reaper container to a requested network if it is specified
|
||||
if p, ok := provider.(*DockerProvider); ok {
|
||||
req.Networks = append(req.Networks, p.defaultNetwork)
|
||||
req.Networks = append(req.Networks, p.DefaultNetwork)
|
||||
}
|
||||
|
||||
c, err := provider.RunContainer(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reaper.container = c
|
||||
|
||||
endpoint, err := c.PortEndpoint(ctx, "8080", "")
|
||||
if err != nil {
|
||||
@@ -89,19 +136,19 @@ func NewReaper(ctx context.Context, sessionID string, provider ReaperProvider, r
|
||||
return reaper, nil
|
||||
}
|
||||
|
||||
func reaperImage(reaperImageName string) string {
|
||||
if reaperImageName == "" {
|
||||
return ReaperDefaultImage
|
||||
} else {
|
||||
return reaperImageName
|
||||
}
|
||||
// Reaper is used to start a sidecar container that cleans up resources
|
||||
type Reaper struct {
|
||||
Provider ReaperProvider
|
||||
SessionID string
|
||||
Endpoint string
|
||||
container Container
|
||||
}
|
||||
|
||||
// Connect runs a goroutine which can be terminated by sending true into the returned channel
|
||||
func (r *Reaper) Connect() (chan bool, error) {
|
||||
conn, err := net.DialTimeout("tcp", r.Endpoint, 10*time.Second)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Connecting to Ryuk on "+r.Endpoint+" failed")
|
||||
return nil, fmt.Errorf("%w: Connecting to Ryuk on %s failed", err, r.Endpoint)
|
||||
}
|
||||
|
||||
terminationSignal := make(chan bool)
|
||||
@@ -118,8 +165,14 @@ func (r *Reaper) Connect() (chan bool, error) {
|
||||
for retryLimit > 0 {
|
||||
retryLimit--
|
||||
|
||||
sock.WriteString(strings.Join(labelFilters, "&"))
|
||||
sock.WriteString("\n")
|
||||
if _, err := sock.WriteString(strings.Join(labelFilters, "&")); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err := sock.WriteString("\n"); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := sock.Flush(); err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -128,6 +181,7 @@ func (r *Reaper) Connect() (chan bool, error) {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if resp == "ACK\n" {
|
||||
break
|
||||
}
|
||||
@@ -141,7 +195,15 @@ func (r *Reaper) Connect() (chan bool, error) {
|
||||
// Labels returns the container labels to use so that this Reaper cleans them up
|
||||
func (r *Reaper) Labels() map[string]string {
|
||||
return map[string]string{
|
||||
TestcontainerLabel: "true",
|
||||
TestcontainerLabelSessionID: r.SessionID,
|
||||
TestcontainerLabel: "true",
|
||||
TestcontainerLabelSessionID: r.SessionID,
|
||||
testcontainersdocker.LabelSessionID: r.SessionID,
|
||||
}
|
||||
}
|
||||
|
||||
func reaperImage(reaperImageName string) string {
|
||||
if reaperImageName == "" {
|
||||
return ReaperDefaultImage
|
||||
}
|
||||
return reaperImageName
|
||||
}
|
||||
|
8
vendor/github.com/testcontainers/testcontainers-go/requirements.txt
generated
vendored
8
vendor/github.com/testcontainers/testcontainers-go/requirements.txt
generated
vendored
@@ -1,4 +1,4 @@
|
||||
mkdocs==1.0.4
|
||||
mkdocs-material==4.6.0
|
||||
mkdocs-markdownextradata-plugin==0.1.1
|
||||
markdown>=3.1,<3.2
|
||||
mkdocs==1.5.2
|
||||
mkdocs-codeinclude-plugin==0.2.1
|
||||
mkdocs-material==8.2.7
|
||||
mkdocs-markdownextradata-plugin==0.2.5
|
||||
|
2
vendor/github.com/testcontainers/testcontainers-go/runtime.txt
generated
vendored
2
vendor/github.com/testcontainers/testcontainers-go/runtime.txt
generated
vendored
@@ -1 +1 @@
|
||||
3.7
|
||||
3.8
|
||||
|
1
vendor/github.com/testcontainers/testcontainers-go/testcontainer.go
generated
vendored
Normal file
1
vendor/github.com/testcontainers/testcontainers-go/testcontainer.go
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package testcontainers
|
22
vendor/github.com/testcontainers/testcontainers-go/testing.go
generated
vendored
22
vendor/github.com/testcontainers/testcontainers-go/testing.go
generated
vendored
@@ -3,12 +3,14 @@ package testcontainers
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/testcontainers/testcontainers-go/internal/testcontainersdocker"
|
||||
)
|
||||
|
||||
// SkipIfProviderIsNotHealthy is a utility function capable of skipping tests
|
||||
// if the provider is not healthy, or running at all.
|
||||
// This is a function designed to be used in your test, when Docker is not mandatory for CI/CD.
|
||||
// In this way tests that depend on testcontainers won't run if the provider is provisioned correctly.
|
||||
// In this way tests that depend on Testcontainers won't run if the provider is provisioned correctly.
|
||||
func SkipIfProviderIsNotHealthy(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
provider, err := ProviderDocker.GetProvider()
|
||||
@@ -20,3 +22,21 @@ func SkipIfProviderIsNotHealthy(t *testing.T) {
|
||||
t.Skipf("Docker is not running. TestContainers can't perform is work without it: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SkipIfDockerDesktop is a utility function capable of skipping tests
|
||||
// if tests are run using Docker Desktop.
|
||||
func SkipIfDockerDesktop(t *testing.T, ctx context.Context) {
|
||||
cli, err := testcontainersdocker.NewClient(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create docker client: %s", err)
|
||||
}
|
||||
|
||||
info, err := cli.Info(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get docker info: %s", err)
|
||||
}
|
||||
|
||||
if info.OperatingSystem == "Docker Desktop" {
|
||||
t.Skip("Skipping test that requires host network access when running in Docker Desktop")
|
||||
}
|
||||
}
|
||||
|
80
vendor/github.com/testcontainers/testcontainers-go/wait/all.go
generated
vendored
Normal file
80
vendor/github.com/testcontainers/testcontainers-go/wait/all.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
package wait
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Implement interface
|
||||
var _ Strategy = (*MultiStrategy)(nil)
|
||||
var _ StrategyTimeout = (*MultiStrategy)(nil)
|
||||
|
||||
type MultiStrategy struct {
|
||||
// all Strategies should have a startupTimeout to avoid waiting infinitely
|
||||
timeout *time.Duration
|
||||
deadline *time.Duration
|
||||
|
||||
// additional properties
|
||||
Strategies []Strategy
|
||||
}
|
||||
|
||||
// WithStartupTimeoutDefault sets the default timeout for all inner wait strategies
|
||||
func (ms *MultiStrategy) WithStartupTimeoutDefault(timeout time.Duration) *MultiStrategy {
|
||||
ms.timeout = &timeout
|
||||
return ms
|
||||
}
|
||||
|
||||
// WithStartupTimeout sets a time.Duration which limits all wait strategies
|
||||
//
|
||||
// Deprecated: use WithDeadline
|
||||
func (ms *MultiStrategy) WithStartupTimeout(timeout time.Duration) Strategy {
|
||||
return ms.WithDeadline(timeout)
|
||||
}
|
||||
|
||||
// WithDeadline sets a time.Duration which limits all wait strategies
|
||||
func (ms *MultiStrategy) WithDeadline(deadline time.Duration) *MultiStrategy {
|
||||
ms.deadline = &deadline
|
||||
return ms
|
||||
}
|
||||
|
||||
func ForAll(strategies ...Strategy) *MultiStrategy {
|
||||
return &MultiStrategy{
|
||||
Strategies: strategies,
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *MultiStrategy) Timeout() *time.Duration {
|
||||
return ms.timeout
|
||||
}
|
||||
|
||||
func (ms *MultiStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) error {
|
||||
var cancel context.CancelFunc
|
||||
if ms.deadline != nil {
|
||||
ctx, cancel = context.WithTimeout(ctx, *ms.deadline)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
if len(ms.Strategies) == 0 {
|
||||
return fmt.Errorf("no wait strategy supplied")
|
||||
}
|
||||
|
||||
for _, strategy := range ms.Strategies {
|
||||
strategyCtx := ctx
|
||||
|
||||
// Set default Timeout when strategy implements StrategyTimeout
|
||||
if st, ok := strategy.(StrategyTimeout); ok {
|
||||
if ms.Timeout() != nil && st.Timeout() == nil {
|
||||
strategyCtx, cancel = context.WithTimeout(ctx, *ms.Timeout())
|
||||
defer cancel()
|
||||
}
|
||||
}
|
||||
|
||||
err := strategy.WaitUntilReady(strategyCtx, target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
1
vendor/github.com/testcontainers/testcontainers-go/wait/errors.go
generated
vendored
1
vendor/github.com/testcontainers/testcontainers-go/wait/errors.go
generated
vendored
@@ -1,3 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package wait
|
||||
|
99
vendor/github.com/testcontainers/testcontainers-go/wait/exec.go
generated
vendored
Normal file
99
vendor/github.com/testcontainers/testcontainers-go/wait/exec.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
package wait
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
tcexec "github.com/testcontainers/testcontainers-go/exec"
|
||||
)
|
||||
|
||||
// Implement interface
|
||||
var _ Strategy = (*ExecStrategy)(nil)
|
||||
var _ StrategyTimeout = (*ExecStrategy)(nil)
|
||||
|
||||
type ExecStrategy struct {
|
||||
// all Strategies should have a startupTimeout to avoid waiting infinitely
|
||||
timeout *time.Duration
|
||||
cmd []string
|
||||
|
||||
// additional properties
|
||||
ExitCodeMatcher func(exitCode int) bool
|
||||
ResponseMatcher func(body io.Reader) bool
|
||||
PollInterval time.Duration
|
||||
}
|
||||
|
||||
// NewExecStrategy constructs an Exec strategy ...
|
||||
func NewExecStrategy(cmd []string) *ExecStrategy {
|
||||
return &ExecStrategy{
|
||||
cmd: cmd,
|
||||
ExitCodeMatcher: defaultExitCodeMatcher,
|
||||
ResponseMatcher: func(body io.Reader) bool { return true },
|
||||
PollInterval: defaultPollInterval(),
|
||||
}
|
||||
}
|
||||
|
||||
func defaultExitCodeMatcher(exitCode int) bool {
|
||||
return exitCode == 0
|
||||
}
|
||||
|
||||
// WithStartupTimeout can be used to change the default startup timeout
|
||||
func (ws *ExecStrategy) WithStartupTimeout(startupTimeout time.Duration) *ExecStrategy {
|
||||
ws.timeout = &startupTimeout
|
||||
return ws
|
||||
}
|
||||
|
||||
func (ws *ExecStrategy) WithExitCodeMatcher(exitCodeMatcher func(exitCode int) bool) *ExecStrategy {
|
||||
ws.ExitCodeMatcher = exitCodeMatcher
|
||||
return ws
|
||||
}
|
||||
|
||||
func (ws *ExecStrategy) WithResponseMatcher(matcher func(body io.Reader) bool) *ExecStrategy {
|
||||
ws.ResponseMatcher = matcher
|
||||
return ws
|
||||
}
|
||||
|
||||
// WithPollInterval can be used to override the default polling interval of 100 milliseconds
|
||||
func (ws *ExecStrategy) WithPollInterval(pollInterval time.Duration) *ExecStrategy {
|
||||
ws.PollInterval = pollInterval
|
||||
return ws
|
||||
}
|
||||
|
||||
// ForExec is a convenience method to assign ExecStrategy
|
||||
func ForExec(cmd []string) *ExecStrategy {
|
||||
return NewExecStrategy(cmd)
|
||||
}
|
||||
|
||||
func (ws *ExecStrategy) Timeout() *time.Duration {
|
||||
return ws.timeout
|
||||
}
|
||||
|
||||
func (ws *ExecStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) error {
|
||||
timeout := defaultStartupTimeout()
|
||||
if ws.timeout != nil {
|
||||
timeout = *ws.timeout
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(ws.PollInterval):
|
||||
exitCode, resp, err := target.Exec(ctx, ws.cmd, tcexec.Multiplexed())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ws.ExitCodeMatcher(exitCode) {
|
||||
continue
|
||||
}
|
||||
if ws.ResponseMatcher != nil && !ws.ResponseMatcher(resp) {
|
||||
continue
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
89
vendor/github.com/testcontainers/testcontainers-go/wait/exit.go
generated
vendored
Normal file
89
vendor/github.com/testcontainers/testcontainers-go/wait/exit.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
package wait
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Implement interface
|
||||
var _ Strategy = (*ExitStrategy)(nil)
|
||||
var _ StrategyTimeout = (*ExitStrategy)(nil)
|
||||
|
||||
// ExitStrategy will wait until container exit
|
||||
type ExitStrategy struct {
|
||||
// all Strategies should have a timeout to avoid waiting infinitely
|
||||
timeout *time.Duration
|
||||
|
||||
// additional properties
|
||||
PollInterval time.Duration
|
||||
}
|
||||
|
||||
// NewExitStrategy constructs with polling interval of 100 milliseconds without timeout by default
|
||||
func NewExitStrategy() *ExitStrategy {
|
||||
return &ExitStrategy{
|
||||
PollInterval: defaultPollInterval(),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// fluent builders for each property
|
||||
// since go has neither covariance nor generics, the return type must be the type of the concrete implementation
|
||||
// this is true for all properties, even the "shared" ones
|
||||
|
||||
// WithExitTimeout can be used to change the default exit timeout
|
||||
func (ws *ExitStrategy) WithExitTimeout(exitTimeout time.Duration) *ExitStrategy {
|
||||
ws.timeout = &exitTimeout
|
||||
return ws
|
||||
}
|
||||
|
||||
// WithPollInterval can be used to override the default polling interval of 100 milliseconds
|
||||
func (ws *ExitStrategy) WithPollInterval(pollInterval time.Duration) *ExitStrategy {
|
||||
ws.PollInterval = pollInterval
|
||||
return ws
|
||||
}
|
||||
|
||||
// ForExit is the default construction for the fluid interface.
|
||||
//
|
||||
// For Example:
|
||||
//
|
||||
// wait.
|
||||
// ForExit().
|
||||
// WithPollInterval(1 * time.Second)
|
||||
func ForExit() *ExitStrategy {
|
||||
return NewExitStrategy()
|
||||
}
|
||||
|
||||
func (ws *ExitStrategy) Timeout() *time.Duration {
|
||||
return ws.timeout
|
||||
}
|
||||
|
||||
// WaitUntilReady implements Strategy.WaitUntilReady
|
||||
func (ws *ExitStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) (err error) {
|
||||
if ws.timeout != nil {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, *ws.timeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
state, err := target.State(ctx)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "No such container") {
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if state.Running {
|
||||
time.Sleep(ws.PollInterval)
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
91
vendor/github.com/testcontainers/testcontainers-go/wait/health.go
generated
vendored
Normal file
91
vendor/github.com/testcontainers/testcontainers-go/wait/health.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package wait
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
// Implement interface
|
||||
var _ Strategy = (*HealthStrategy)(nil)
|
||||
var _ StrategyTimeout = (*HealthStrategy)(nil)
|
||||
|
||||
// HealthStrategy will wait until the container becomes healthy
|
||||
type HealthStrategy struct {
|
||||
// all Strategies should have a startupTimeout to avoid waiting infinitely
|
||||
timeout *time.Duration
|
||||
|
||||
// additional properties
|
||||
PollInterval time.Duration
|
||||
}
|
||||
|
||||
// NewHealthStrategy constructs with polling interval of 100 milliseconds and startup timeout of 60 seconds by default
|
||||
func NewHealthStrategy() *HealthStrategy {
|
||||
return &HealthStrategy{
|
||||
PollInterval: defaultPollInterval(),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// fluent builders for each property
|
||||
// since go has neither covariance nor generics, the return type must be the type of the concrete implementation
|
||||
// this is true for all properties, even the "shared" ones like startupTimeout
|
||||
|
||||
// WithStartupTimeout can be used to change the default startup timeout
|
||||
func (ws *HealthStrategy) WithStartupTimeout(startupTimeout time.Duration) *HealthStrategy {
|
||||
ws.timeout = &startupTimeout
|
||||
return ws
|
||||
}
|
||||
|
||||
// WithPollInterval can be used to override the default polling interval of 100 milliseconds
|
||||
func (ws *HealthStrategy) WithPollInterval(pollInterval time.Duration) *HealthStrategy {
|
||||
ws.PollInterval = pollInterval
|
||||
return ws
|
||||
}
|
||||
|
||||
// ForHealthCheck is the default construction for the fluid interface.
|
||||
//
|
||||
// For Example:
|
||||
//
|
||||
// wait.
|
||||
// ForHealthCheck().
|
||||
// WithPollInterval(1 * time.Second)
|
||||
func ForHealthCheck() *HealthStrategy {
|
||||
return NewHealthStrategy()
|
||||
}
|
||||
|
||||
func (ws *HealthStrategy) Timeout() *time.Duration {
|
||||
return ws.timeout
|
||||
}
|
||||
|
||||
// WaitUntilReady implements Strategy.WaitUntilReady
|
||||
func (ws *HealthStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) (err error) {
|
||||
timeout := defaultStartupTimeout()
|
||||
if ws.timeout != nil {
|
||||
timeout = *ws.timeout
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
state, err := target.State(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkState(state); err != nil {
|
||||
return err
|
||||
}
|
||||
if state.Health == nil || state.Health.Status != types.Healthy {
|
||||
time.Sleep(ws.PollInterval)
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
126
vendor/github.com/testcontainers/testcontainers-go/wait/host_port.go
generated
vendored
126
vendor/github.com/testcontainers/testcontainers-go/wait/host_port.go
generated
vendored
@@ -2,31 +2,37 @@ package wait
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
)
|
||||
|
||||
// Implement interface
|
||||
var _ Strategy = (*HostPortStrategy)(nil)
|
||||
var _ StrategyTimeout = (*HostPortStrategy)(nil)
|
||||
|
||||
var errShellNotExecutable = errors.New("/bin/sh command not executable")
|
||||
|
||||
type HostPortStrategy struct {
|
||||
// Port is a string containing port number and protocol in the format "80/tcp"
|
||||
// which
|
||||
Port nat.Port
|
||||
// all WaitStrategies should have a startupTimeout to avoid waiting infinitely
|
||||
startupTimeout time.Duration
|
||||
timeout *time.Duration
|
||||
PollInterval time.Duration
|
||||
}
|
||||
|
||||
// NewHostPortStrategy constructs a default host port strategy
|
||||
func NewHostPortStrategy(port nat.Port) *HostPortStrategy {
|
||||
return &HostPortStrategy{
|
||||
Port: port,
|
||||
startupTimeout: defaultStartupTimeout(),
|
||||
Port: port,
|
||||
PollInterval: defaultPollInterval(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,70 +46,150 @@ func ForListeningPort(port nat.Port) *HostPortStrategy {
|
||||
return NewHostPortStrategy(port)
|
||||
}
|
||||
|
||||
// ForExposedPort constructs an exposed port strategy. Alias for `NewHostPortStrategy("")`.
|
||||
// This strategy waits for the first port exposed in the Docker container.
|
||||
func ForExposedPort() *HostPortStrategy {
|
||||
return NewHostPortStrategy("")
|
||||
}
|
||||
|
||||
// WithStartupTimeout can be used to change the default startup timeout
|
||||
func (hp *HostPortStrategy) WithStartupTimeout(startupTimeout time.Duration) *HostPortStrategy {
|
||||
hp.startupTimeout = startupTimeout
|
||||
hp.timeout = &startupTimeout
|
||||
return hp
|
||||
}
|
||||
|
||||
// WithPollInterval can be used to override the default polling interval of 100 milliseconds
|
||||
func (hp *HostPortStrategy) WithPollInterval(pollInterval time.Duration) *HostPortStrategy {
|
||||
hp.PollInterval = pollInterval
|
||||
return hp
|
||||
}
|
||||
|
||||
func (hp *HostPortStrategy) Timeout() *time.Duration {
|
||||
return hp.timeout
|
||||
}
|
||||
|
||||
// WaitUntilReady implements Strategy.WaitUntilReady
|
||||
func (hp *HostPortStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) (err error) {
|
||||
// limit context to startupTimeout
|
||||
ctx, cancelContext := context.WithTimeout(ctx, hp.startupTimeout)
|
||||
defer cancelContext()
|
||||
timeout := defaultStartupTimeout()
|
||||
if hp.timeout != nil {
|
||||
timeout = *hp.timeout
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
ipAddress, err := target.Host(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
port, err := target.MappedPort(ctx, hp.Port)
|
||||
if err != nil {
|
||||
var waitInterval = hp.PollInterval
|
||||
|
||||
internalPort := hp.Port
|
||||
if internalPort == "" {
|
||||
var ports nat.PortMap
|
||||
ports, err = target.Ports(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(ports) > 0 {
|
||||
for p := range ports {
|
||||
internalPort = p
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if internalPort == "" {
|
||||
err = fmt.Errorf("no port to wait for")
|
||||
return
|
||||
}
|
||||
|
||||
var port nat.Port
|
||||
port, err = target.MappedPort(ctx, internalPort)
|
||||
var i = 0
|
||||
|
||||
for port == "" {
|
||||
i++
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("%s:%w", ctx.Err(), err)
|
||||
case <-time.After(waitInterval):
|
||||
if err := checkTarget(ctx, target); err != nil {
|
||||
return err
|
||||
}
|
||||
port, err = target.MappedPort(ctx, internalPort)
|
||||
if err != nil {
|
||||
fmt.Printf("(%d) [%s] %s\n", i, port, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := externalCheck(ctx, ipAddress, port, target, waitInterval); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = internalCheck(ctx, internalPort, target)
|
||||
if err != nil && errors.Is(errShellNotExecutable, err) {
|
||||
log.Println("Shell not executable in container, only external port check will be performed")
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func externalCheck(ctx context.Context, ipAddress string, port nat.Port, target StrategyTarget, waitInterval time.Duration) error {
|
||||
proto := port.Proto()
|
||||
portNumber := port.Int()
|
||||
portString := strconv.Itoa(portNumber)
|
||||
|
||||
//external check
|
||||
dialer := net.Dialer{}
|
||||
address := net.JoinHostPort(ipAddress, portString)
|
||||
for {
|
||||
if err := checkTarget(ctx, target); err != nil {
|
||||
return err
|
||||
}
|
||||
conn, err := dialer.DialContext(ctx, proto, address)
|
||||
if err != nil {
|
||||
if v, ok := err.(*net.OpError); ok {
|
||||
if v2, ok := (v.Err).(*os.SyscallError); ok {
|
||||
if isConnRefusedErr(v2.Err) {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
time.Sleep(waitInterval)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
} else {
|
||||
conn.Close()
|
||||
_ = conn.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//internal check
|
||||
command := buildInternalCheckCommand(hp.Port.Int())
|
||||
func internalCheck(ctx context.Context, internalPort nat.Port, target StrategyTarget) error {
|
||||
command := buildInternalCheckCommand(internalPort.Int())
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
exitCode, err := target.Exec(ctx, []string{"/bin/sh", "-c", command})
|
||||
if err := checkTarget(ctx, target); err != nil {
|
||||
return err
|
||||
}
|
||||
exitCode, _, err := target.Exec(ctx, []string{"/bin/sh", "-c", command})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "host port waiting failed")
|
||||
return fmt.Errorf("%w, host port waiting failed", err)
|
||||
}
|
||||
|
||||
if exitCode == 0 {
|
||||
break
|
||||
} else if exitCode == 126 {
|
||||
return errors.New("/bin/sh command not executable")
|
||||
return errShellNotExecutable
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
114
vendor/github.com/testcontainers/testcontainers-go/wait/http.go
generated
vendored
114
vendor/github.com/testcontainers/testcontainers-go/wait/http.go
generated
vendored
@@ -1,6 +1,7 @@
|
||||
package wait
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -16,10 +18,11 @@ import (
|
||||
|
||||
// Implement interface
|
||||
var _ Strategy = (*HTTPStrategy)(nil)
|
||||
var _ StrategyTimeout = (*HTTPStrategy)(nil)
|
||||
|
||||
type HTTPStrategy struct {
|
||||
// all Strategies should have a startupTimeout to avoid waiting infinitely
|
||||
startupTimeout time.Duration
|
||||
timeout *time.Duration
|
||||
|
||||
// additional properties
|
||||
Port nat.Port
|
||||
@@ -32,13 +35,13 @@ type HTTPStrategy struct {
|
||||
Method string // http method
|
||||
Body io.Reader // http request body
|
||||
PollInterval time.Duration
|
||||
UserInfo *url.Userinfo
|
||||
}
|
||||
|
||||
// NewHTTPStrategy constructs a HTTP strategy waiting on port 80 and status code 200
|
||||
func NewHTTPStrategy(path string) *HTTPStrategy {
|
||||
return &HTTPStrategy{
|
||||
startupTimeout: defaultStartupTimeout(),
|
||||
Port: "80/tcp",
|
||||
Port: "",
|
||||
Path: path,
|
||||
StatusCodeMatcher: defaultStatusCodeMatcher,
|
||||
ResponseMatcher: func(body io.Reader) bool { return true },
|
||||
@@ -47,6 +50,7 @@ func NewHTTPStrategy(path string) *HTTPStrategy {
|
||||
Method: http.MethodGet,
|
||||
Body: nil,
|
||||
PollInterval: defaultPollInterval(),
|
||||
UserInfo: nil,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +62,9 @@ func defaultStatusCodeMatcher(status int) bool {
|
||||
// since go has neither covariance nor generics, the return type must be the type of the concrete implementation
|
||||
// this is true for all properties, even the "shared" ones like startupTimeout
|
||||
|
||||
func (ws *HTTPStrategy) WithStartupTimeout(startupTimeout time.Duration) *HTTPStrategy {
|
||||
ws.startupTimeout = startupTimeout
|
||||
// WithStartupTimeout can be used to change the default startup timeout
|
||||
func (ws *HTTPStrategy) WithStartupTimeout(timeout time.Duration) *HTTPStrategy {
|
||||
ws.timeout = &timeout
|
||||
return ws
|
||||
}
|
||||
|
||||
@@ -101,6 +106,11 @@ func (ws *HTTPStrategy) WithBody(reqdata io.Reader) *HTTPStrategy {
|
||||
return ws
|
||||
}
|
||||
|
||||
func (ws *HTTPStrategy) WithBasicAuth(username, password string) *HTTPStrategy {
|
||||
ws.UserInfo = url.UserPassword(username, password)
|
||||
return ws
|
||||
}
|
||||
|
||||
// WithPollInterval can be used to override the default polling interval of 100 milliseconds
|
||||
func (ws *HTTPStrategy) WithPollInterval(pollInterval time.Duration) *HTTPStrategy {
|
||||
ws.PollInterval = pollInterval
|
||||
@@ -113,24 +123,71 @@ func ForHTTP(path string) *HTTPStrategy {
|
||||
return NewHTTPStrategy(path)
|
||||
}
|
||||
|
||||
func (ws *HTTPStrategy) Timeout() *time.Duration {
|
||||
return ws.timeout
|
||||
}
|
||||
|
||||
// WaitUntilReady implements Strategy.WaitUntilReady
|
||||
func (ws *HTTPStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) (err error) {
|
||||
// limit context to startupTimeout
|
||||
ctx, cancelContext := context.WithTimeout(ctx, ws.startupTimeout)
|
||||
defer cancelContext()
|
||||
timeout := defaultStartupTimeout()
|
||||
if ws.timeout != nil {
|
||||
timeout = *ws.timeout
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
ipAddress, err := target.Host(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
port, err := target.MappedPort(ctx, ws.Port)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var mappedPort nat.Port
|
||||
if ws.Port == "" {
|
||||
ports, err := target.Ports(ctx)
|
||||
for err != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("%s:%w", ctx.Err(), err)
|
||||
case <-time.After(ws.PollInterval):
|
||||
if err := checkTarget(ctx, target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if port.Proto() != "tcp" {
|
||||
return errors.New("Cannot use HTTP client on non-TCP ports")
|
||||
ports, err = target.Ports(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
for k, bindings := range ports {
|
||||
if len(bindings) == 0 || k.Proto() != "tcp" {
|
||||
continue
|
||||
}
|
||||
mappedPort, _ = nat.NewPort(k.Proto(), bindings[0].HostPort)
|
||||
break
|
||||
}
|
||||
|
||||
if mappedPort == "" {
|
||||
return errors.New("No exposed tcp ports or mapped ports - cannot wait for status")
|
||||
}
|
||||
} else {
|
||||
mappedPort, err = target.MappedPort(ctx, ws.Port)
|
||||
|
||||
for mappedPort == "" {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("%s:%w", ctx.Err(), err)
|
||||
case <-time.After(ws.PollInterval):
|
||||
if err := checkTarget(ctx, target); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mappedPort, err = target.MappedPort(ctx, ws.Port)
|
||||
}
|
||||
}
|
||||
|
||||
if mappedPort.Proto() != "tcp" {
|
||||
return errors.New("Cannot use HTTP client on non-TCP ports")
|
||||
}
|
||||
}
|
||||
|
||||
switch ws.Method {
|
||||
@@ -174,15 +231,36 @@ func (ws *HTTPStrategy) WaitUntilReady(ctx context.Context, target StrategyTarge
|
||||
}
|
||||
|
||||
client := http.Client{Transport: tripper, Timeout: time.Second}
|
||||
address := net.JoinHostPort(ipAddress, strconv.Itoa(port.Int()))
|
||||
endpoint := fmt.Sprintf("%s://%s%s", proto, address, ws.Path)
|
||||
address := net.JoinHostPort(ipAddress, strconv.Itoa(mappedPort.Int()))
|
||||
|
||||
endpoint := url.URL{
|
||||
Scheme: proto,
|
||||
Host: address,
|
||||
Path: ws.Path,
|
||||
}
|
||||
|
||||
if ws.UserInfo != nil {
|
||||
endpoint.User = ws.UserInfo
|
||||
}
|
||||
|
||||
// cache the body into a byte-slice so that it can be iterated over multiple times
|
||||
var body []byte
|
||||
if ws.Body != nil {
|
||||
body, err = io.ReadAll(ws.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(ws.PollInterval):
|
||||
req, err := http.NewRequestWithContext(ctx, ws.Method, endpoint, ws.Body)
|
||||
if err := checkTarget(ctx, target); err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, ws.Method, endpoint.String(), bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -191,9 +269,11 @@ func (ws *HTTPStrategy) WaitUntilReady(ctx context.Context, target StrategyTarge
|
||||
continue
|
||||
}
|
||||
if ws.StatusCodeMatcher != nil && !ws.StatusCodeMatcher(resp.StatusCode) {
|
||||
_ = resp.Body.Close()
|
||||
continue
|
||||
}
|
||||
if ws.ResponseMatcher != nil && !ws.ResponseMatcher(resp.Body) {
|
||||
_ = resp.Body.Close()
|
||||
continue
|
||||
}
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
|
66
vendor/github.com/testcontainers/testcontainers-go/wait/log.go
generated
vendored
66
vendor/github.com/testcontainers/testcontainers-go/wait/log.go
generated
vendored
@@ -2,18 +2,19 @@ package wait
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Implement interface
|
||||
var _ Strategy = (*LogStrategy)(nil)
|
||||
var _ StrategyTimeout = (*LogStrategy)(nil)
|
||||
|
||||
// LogStrategy will wait until a given log entry shows up in the docker logs
|
||||
type LogStrategy struct {
|
||||
// all Strategies should have a startupTimeout to avoid waiting infinitely
|
||||
startupTimeout time.Duration
|
||||
timeout *time.Duration
|
||||
|
||||
// additional properties
|
||||
Log string
|
||||
@@ -21,15 +22,13 @@ type LogStrategy struct {
|
||||
PollInterval time.Duration
|
||||
}
|
||||
|
||||
// NewLogStrategy constructs a HTTP strategy waiting on port 80 and status code 200
|
||||
// NewLogStrategy constructs with polling interval of 100 milliseconds and startup timeout of 60 seconds by default
|
||||
func NewLogStrategy(log string) *LogStrategy {
|
||||
return &LogStrategy{
|
||||
startupTimeout: defaultStartupTimeout(),
|
||||
Log: log,
|
||||
Occurrence: 1,
|
||||
PollInterval: defaultPollInterval(),
|
||||
Log: log,
|
||||
Occurrence: 1,
|
||||
PollInterval: defaultPollInterval(),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// fluent builders for each property
|
||||
@@ -37,8 +36,8 @@ func NewLogStrategy(log string) *LogStrategy {
|
||||
// this is true for all properties, even the "shared" ones like startupTimeout
|
||||
|
||||
// WithStartupTimeout can be used to change the default startup timeout
|
||||
func (ws *LogStrategy) WithStartupTimeout(startupTimeout time.Duration) *LogStrategy {
|
||||
ws.startupTimeout = startupTimeout
|
||||
func (ws *LogStrategy) WithStartupTimeout(timeout time.Duration) *LogStrategy {
|
||||
ws.timeout = &timeout
|
||||
return ws
|
||||
}
|
||||
|
||||
@@ -49,7 +48,7 @@ func (ws *LogStrategy) WithPollInterval(pollInterval time.Duration) *LogStrategy
|
||||
}
|
||||
|
||||
func (ws *LogStrategy) WithOccurrence(o int) *LogStrategy {
|
||||
// the number of occurence needs to be positive
|
||||
// the number of occurrence needs to be positive
|
||||
if o <= 0 {
|
||||
o = 1
|
||||
}
|
||||
@@ -60,19 +59,29 @@ func (ws *LogStrategy) WithOccurrence(o int) *LogStrategy {
|
||||
// ForLog is the default construction for the fluid interface.
|
||||
//
|
||||
// For Example:
|
||||
// wait.
|
||||
// ForLog("some text").
|
||||
// WithPollInterval(1 * time.Second)
|
||||
//
|
||||
// wait.
|
||||
// ForLog("some text").
|
||||
// WithPollInterval(1 * time.Second)
|
||||
func ForLog(log string) *LogStrategy {
|
||||
return NewLogStrategy(log)
|
||||
}
|
||||
|
||||
func (ws *LogStrategy) Timeout() *time.Duration {
|
||||
return ws.timeout
|
||||
}
|
||||
|
||||
// WaitUntilReady implements Strategy.WaitUntilReady
|
||||
func (ws *LogStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) (err error) {
|
||||
// limit context to startupTimeout
|
||||
ctx, cancelContext := context.WithTimeout(ctx, ws.startupTimeout)
|
||||
defer cancelContext()
|
||||
currentOccurence := 0
|
||||
timeout := defaultStartupTimeout()
|
||||
if ws.timeout != nil {
|
||||
timeout = *ws.timeout
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
length := 0
|
||||
|
||||
LOOP:
|
||||
for {
|
||||
@@ -80,20 +89,27 @@ LOOP:
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
reader, err := target.Logs(ctx)
|
||||
checkErr := checkTarget(ctx, target)
|
||||
|
||||
reader, err := target.Logs(ctx)
|
||||
if err != nil {
|
||||
time.Sleep(ws.PollInterval)
|
||||
continue
|
||||
}
|
||||
b, err := ioutil.ReadAll(reader)
|
||||
|
||||
b, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
time.Sleep(ws.PollInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
logs := string(b)
|
||||
if strings.Contains(logs, ws.Log) {
|
||||
currentOccurence++
|
||||
if ws.Occurrence == 0 || currentOccurence >= ws.Occurrence-1 {
|
||||
break LOOP
|
||||
}
|
||||
if length == len(logs) && checkErr != nil {
|
||||
return checkErr
|
||||
} else if strings.Count(logs, ws.Log) >= ws.Occurrence {
|
||||
break LOOP
|
||||
} else {
|
||||
length = len(logs)
|
||||
time.Sleep(ws.PollInterval)
|
||||
continue
|
||||
}
|
||||
|
47
vendor/github.com/testcontainers/testcontainers-go/wait/multi.go
generated
vendored
47
vendor/github.com/testcontainers/testcontainers-go/wait/multi.go
generated
vendored
@@ -1,47 +0,0 @@
|
||||
package wait
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Implement interface
|
||||
var _ Strategy = (*MultiStrategy)(nil)
|
||||
|
||||
type MultiStrategy struct {
|
||||
// all Strategies should have a startupTimeout to avoid waiting infinitely
|
||||
startupTimeout time.Duration
|
||||
|
||||
// additional properties
|
||||
Strategies []Strategy
|
||||
}
|
||||
|
||||
func (ms *MultiStrategy) WithStartupTimeout(startupTimeout time.Duration) *MultiStrategy {
|
||||
ms.startupTimeout = startupTimeout
|
||||
return ms
|
||||
}
|
||||
|
||||
func ForAll(strategies ...Strategy) *MultiStrategy {
|
||||
return &MultiStrategy{
|
||||
startupTimeout: defaultStartupTimeout(),
|
||||
Strategies: strategies,
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *MultiStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) (err error) {
|
||||
ctx, cancelContext := context.WithTimeout(ctx, ms.startupTimeout)
|
||||
defer cancelContext()
|
||||
|
||||
if len(ms.Strategies) == 0 {
|
||||
return fmt.Errorf("no wait strategy supplied")
|
||||
}
|
||||
|
||||
for _, strategy := range ms.Strategies {
|
||||
err := strategy.WaitUntilReady(ctx, target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
69
vendor/github.com/testcontainers/testcontainers-go/wait/nop.go
generated
vendored
Normal file
69
vendor/github.com/testcontainers/testcontainers-go/wait/nop.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
package wait
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/testcontainers/testcontainers-go/exec"
|
||||
)
|
||||
|
||||
var _ Strategy = (*NopStrategy)(nil)
|
||||
var _ StrategyTimeout = (*NopStrategy)(nil)
|
||||
|
||||
type NopStrategy struct {
|
||||
timeout *time.Duration
|
||||
waitUntilReady func(context.Context, StrategyTarget) error
|
||||
}
|
||||
|
||||
func ForNop(
|
||||
waitUntilReady func(context.Context, StrategyTarget) error,
|
||||
) *NopStrategy {
|
||||
return &NopStrategy{
|
||||
waitUntilReady: waitUntilReady,
|
||||
}
|
||||
}
|
||||
|
||||
func (ws *NopStrategy) Timeout() *time.Duration {
|
||||
return ws.timeout
|
||||
}
|
||||
|
||||
func (ws *NopStrategy) WithStartupTimeout(timeout time.Duration) *NopStrategy {
|
||||
ws.timeout = &timeout
|
||||
return ws
|
||||
}
|
||||
|
||||
func (ws *NopStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) error {
|
||||
return ws.waitUntilReady(ctx, target)
|
||||
}
|
||||
|
||||
type NopStrategyTarget struct {
|
||||
ReaderCloser io.ReadCloser
|
||||
ContainerState types.ContainerState
|
||||
}
|
||||
|
||||
func (st NopStrategyTarget) Host(_ context.Context) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (st NopStrategyTarget) Ports(_ context.Context) (nat.PortMap, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (st NopStrategyTarget) MappedPort(_ context.Context, n nat.Port) (nat.Port, error) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (st NopStrategyTarget) Logs(_ context.Context) (io.ReadCloser, error) {
|
||||
return st.ReaderCloser, nil
|
||||
}
|
||||
|
||||
func (st NopStrategyTarget) Exec(_ context.Context, _ []string, _ ...exec.ProcessOption) (int, io.Reader, error) {
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
func (st NopStrategyTarget) State(_ context.Context) (*types.ContainerState, error) {
|
||||
return &st.ContainerState, nil
|
||||
}
|
74
vendor/github.com/testcontainers/testcontainers-go/wait/sql.go
generated
vendored
74
vendor/github.com/testcontainers/testcontainers-go/wait/sql.go
generated
vendored
@@ -9,52 +9,92 @@ import (
|
||||
"github.com/docker/go-connections/nat"
|
||||
)
|
||||
|
||||
//ForSQL constructs a new waitForSql strategy for the given driver
|
||||
func ForSQL(port nat.Port, driver string, url func(nat.Port) string) *waitForSql {
|
||||
var _ Strategy = (*waitForSql)(nil)
|
||||
var _ StrategyTimeout = (*waitForSql)(nil)
|
||||
|
||||
const defaultForSqlQuery = "SELECT 1"
|
||||
|
||||
// ForSQL constructs a new waitForSql strategy for the given driver
|
||||
func ForSQL(port nat.Port, driver string, url func(host string, port nat.Port) string) *waitForSql {
|
||||
return &waitForSql{
|
||||
Port: port,
|
||||
URL: url,
|
||||
Driver: driver,
|
||||
startupTimeout: defaultStartupTimeout(),
|
||||
PollInterval: defaultPollInterval(),
|
||||
query: defaultForSqlQuery,
|
||||
}
|
||||
}
|
||||
|
||||
type waitForSql struct {
|
||||
URL func(port nat.Port) string
|
||||
timeout *time.Duration
|
||||
|
||||
URL func(host string, port nat.Port) string
|
||||
Driver string
|
||||
Port nat.Port
|
||||
startupTimeout time.Duration
|
||||
PollInterval time.Duration
|
||||
query string
|
||||
}
|
||||
|
||||
//Timeout sets the maximum waiting time for the strategy after which it'll give up and return an error
|
||||
func (w *waitForSql) Timeout(duration time.Duration) *waitForSql {
|
||||
w.startupTimeout = duration
|
||||
// WithStartupTimeout can be used to change the default startup timeout
|
||||
func (w *waitForSql) WithStartupTimeout(timeout time.Duration) *waitForSql {
|
||||
w.timeout = &timeout
|
||||
return w
|
||||
}
|
||||
|
||||
//WithPollInterval can be used to override the default polling interval of 100 milliseconds
|
||||
// WithPollInterval can be used to override the default polling interval of 100 milliseconds
|
||||
func (w *waitForSql) WithPollInterval(pollInterval time.Duration) *waitForSql {
|
||||
w.PollInterval = pollInterval
|
||||
return w
|
||||
}
|
||||
|
||||
//WaitUntilReady repeatedly tries to run "SELECT 1" query on the given port using sql and driver.
|
||||
// If the it doesn't succeed until the timeout value which defaults to 60 seconds, it will return an error
|
||||
// WithQuery can be used to override the default query used in the strategy.
|
||||
func (w *waitForSql) WithQuery(query string) *waitForSql {
|
||||
w.query = query
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *waitForSql) Timeout() *time.Duration {
|
||||
return w.timeout
|
||||
}
|
||||
|
||||
// WaitUntilReady repeatedly tries to run "SELECT 1" or user defined query on the given port using sql and driver.
|
||||
//
|
||||
// If it doesn't succeed until the timeout value which defaults to 60 seconds, it will return an error.
|
||||
func (w *waitForSql) WaitUntilReady(ctx context.Context, target StrategyTarget) (err error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, w.startupTimeout)
|
||||
timeout := defaultStartupTimeout()
|
||||
if w.timeout != nil {
|
||||
timeout = *w.timeout
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
host, err := target.Host(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(w.PollInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
port, err := target.MappedPort(ctx, w.Port)
|
||||
if err != nil {
|
||||
return fmt.Errorf("target.MappedPort: %v", err)
|
||||
var port nat.Port
|
||||
port, err = target.MappedPort(ctx, w.Port)
|
||||
|
||||
for port == "" {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("%s:%w", ctx.Err(), err)
|
||||
case <-ticker.C:
|
||||
if err := checkTarget(ctx, target); err != nil {
|
||||
return err
|
||||
}
|
||||
port, err = target.MappedPort(ctx, w.Port)
|
||||
}
|
||||
}
|
||||
|
||||
db, err := sql.Open(w.Driver, w.URL(port))
|
||||
db, err := sql.Open(w.Driver, w.URL(host, port))
|
||||
if err != nil {
|
||||
return fmt.Errorf("sql.Open: %v", err)
|
||||
}
|
||||
@@ -64,8 +104,10 @@ func (w *waitForSql) WaitUntilReady(ctx context.Context, target StrategyTarget)
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-ticker.C:
|
||||
|
||||
if _, err := db.ExecContext(ctx, "SELECT 1"); err != nil {
|
||||
if err := checkTarget(ctx, target); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.ExecContext(ctx, w.query); err != nil {
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
|
36
vendor/github.com/testcontainers/testcontainers-go/wait/wait.go
generated
vendored
36
vendor/github.com/testcontainers/testcontainers-go/wait/wait.go
generated
vendored
@@ -2,21 +2,55 @@ package wait
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/testcontainers/testcontainers-go/exec"
|
||||
)
|
||||
|
||||
// Strategy defines the basic interface for a Wait Strategy
|
||||
type Strategy interface {
|
||||
WaitUntilReady(context.Context, StrategyTarget) error
|
||||
}
|
||||
|
||||
// StrategyTimeout allows MultiStrategy to configure a Strategy's Timeout
|
||||
type StrategyTimeout interface {
|
||||
Timeout() *time.Duration
|
||||
}
|
||||
|
||||
type StrategyTarget interface {
|
||||
Host(context.Context) (string, error)
|
||||
Ports(ctx context.Context) (nat.PortMap, error)
|
||||
MappedPort(context.Context, nat.Port) (nat.Port, error)
|
||||
Logs(context.Context) (io.ReadCloser, error)
|
||||
Exec(ctx context.Context, cmd []string) (int, error)
|
||||
Exec(context.Context, []string, ...exec.ProcessOption) (int, io.Reader, error)
|
||||
State(context.Context) (*types.ContainerState, error)
|
||||
}
|
||||
|
||||
func checkTarget(ctx context.Context, target StrategyTarget) error {
|
||||
state, err := target.State(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkState(state)
|
||||
}
|
||||
|
||||
func checkState(state *types.ContainerState) error {
|
||||
switch {
|
||||
case state.Running:
|
||||
return nil
|
||||
case state.OOMKilled:
|
||||
return errors.New("container crashed with out-of-memory (OOMKilled)")
|
||||
case state.Status == "exited":
|
||||
return fmt.Errorf("container exited with code %d", state.ExitCode)
|
||||
default:
|
||||
return fmt.Errorf("unexpected container status %q", state.Status)
|
||||
}
|
||||
}
|
||||
|
||||
func defaultStartupTimeout() time.Duration {
|
||||
|
Reference in New Issue
Block a user