diff --git a/.gitignore b/.gitignore index ebee2a0..ef4a254 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ mysqlImporter mysqlExporter +mysqlProxyExporter +mysqlProxyImporter mariadbImporter mariadbExporter dist/ diff --git a/Makefile b/Makefile index cc9cc23..38a9786 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,8 @@ build: ${GO} build -v -o mysqlExporter${EXT} ./plugin/mysql-exporter ${GO} build -v -o mariadbImporter${EXT} ./plugin/mariadb-importer ${GO} build -v -o mariadbExporter${EXT} ./plugin/mariadb-exporter + ${GO} build -v -o mysqlProxyImporter${EXT} ./plugin/mysql-proxy-importer + ${GO} build -v -o mysqlProxyExporter${EXT} ./plugin/mysql-proxy-exporter package: build rm -f $(PTAR) @@ -42,4 +44,4 @@ test: ${GO} test -v ./tests/... clean: - rm -f mysqlImporter mysqlExporter + rm -f mysqlImporter mysqlExporter mariadbImporter mariadbExporter mysqlProxyImporter mysqlProxyExporter diff --git a/README.md b/README.md index 7445658..eeb385b 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,18 @@ and can be restored without Plakar if needed. | Protocol | Target | Dump tool | Client tool | |-------------------|------------------|-----------------|-------------| | `mysql://` | MySQL 5.7 / 8.x | `mysqldump` | `mysql` | +| `mysql+gcsql://` | MySQL 5.7 / 8.x | `mysqldump` | `mysql` | | `mysql+mariadb://`| MariaDB 10.x / 11.x | `mariadb-dump` | `mariadb` | Use the protocol that matches your server. The two connectors are independent: `mysql://` always uses MySQL binaries; `mysql+mariadb://` always uses MariaDB binaries. +The `gcsql` variant is specific to databases hosted at Google Cloud (TM), it +uses Cloud SQL Proxy to connect to the database to make the experience easier. +As Google does not offer MariaDB backends it's only available for the MySQL +flavor. + ## Prerequisites **MySQL** diff --git a/go.mod b/go.mod index d741607..f8a32a7 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,21 @@ module github.com/PlakarKorp/integration-mysql -go 1.25.0 +go 1.25.7 require ( + cloud.google.com/go/cloudsqlconn v1.21.0 github.com/PlakarKorp/go-kloset-sdk v1.1.0-beta.1 github.com/PlakarKorp/kloset v1.1.0-beta.2 - github.com/go-sql-driver/mysql v1.8.1 + github.com/go-sql-driver/mysql v1.9.3 github.com/testcontainers/testcontainers-go v0.41.0 ) require ( + cloud.google.com/go/auth v0.20.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect dario.cat/mergo v1.0.2 // indirect - filippo.io/edwards25519 v1.1.0 // indirect + filippo.io/edwards25519 v1.2.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/PlakarKorp/integration-grpc v1.1.0-beta.3 // indirect @@ -34,8 +38,12 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/snappy v1.0.0 // indirect + github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect + github.com/googleapis/gax-go/v2 v2.21.0 // indirect github.com/klauspost/compress v1.18.2 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect @@ -68,23 +76,27 @@ require ( github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zeebo/blake3 v0.2.4 // indirect + go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect go.opentelemetry.io/otel v1.43.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect go.opentelemetry.io/otel/metric v1.43.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect go.opentelemetry.io/otel/trace v1.43.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect - golang.org/x/crypto v0.48.0 // indirect + golang.org/x/crypto v0.50.0 // indirect golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect - golang.org/x/mod v0.32.0 // indirect - golang.org/x/net v0.50.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/text v0.34.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect - google.golang.org/grpc v1.79.2 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/net v0.53.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.43.0 // indirect + golang.org/x/text v0.36.0 // indirect + golang.org/x/time v0.15.0 // indirect + google.golang.org/api v0.276.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect + google.golang.org/grpc v1.80.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/libc v1.67.6 // indirect diff --git a/go.sum b/go.sum index 36c1dcc..e6bdc94 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,21 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA= +cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/cloudsqlconn v1.21.0 h1:tXXKrKLsUwG+cTxpO2kehC6l8l8urHoNtDXtCv3iOpY= +cloud.google.com/go/cloudsqlconn v1.21.0/go.mod h1:p4ts4hqj4CtBCwETUSzq05HV4XTRPzTsi+Cd1RQ5WWg= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= -filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= -filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE= github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= @@ -30,8 +40,11 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/crlib v0.0.0-20250110162118-b7c9be99e911 h1:X+r2Lb1qj0APqrxM8NhBD3X3JDM1Fe+u+WAvhvKuLdM= github.com/cockroachdb/crlib v0.0.0-20250110162118-b7c9be99e911/go.mod h1:Gq51ZeKaFCXk6QwuGM0w1dnaOqc/F5zKT2zA9D6Xeac= github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= @@ -74,6 +87,10 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= @@ -93,25 +110,64 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= -github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.21.0 h1:h45NjjzEO3faG9Lg/cFrBh2PgegVVgzqKzuZl/wMbiI= +github.com/googleapis/gax-go/v2 v2.21.0/go.mod h1:But/NJU6TnZsrLai/xBAQLLz+Hc7fHZJt/hsCz3Fih4= github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.9.1 h1:uwrxJXBnx76nyISkhr33kQLlUqjv7et7b9FjCen/tdc= +github.com/jackc/pgx/v5 v5.9.1/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= @@ -128,6 +184,8 @@ github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8S github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/microsoft/go-mssqldb v1.9.8 h1:d4IFMvF/o+HdpXUqbBfzHvn/NlFA75YGcfHUUvDFJEM= +github.com/microsoft/go-mssqldb v1.9.8/go.mod h1:eGSRSGAW4hKMy5YcAenhCDjIRm2rhqIdmmwgciMzLus= github.com/minio/minlz v1.0.1-0.20250507153514-87eb42fe8882 h1:0lgqHvJWHLGW5TuObJrfyEi6+ASTKDBWikGvPqy9Yiw= github.com/minio/minlz v1.0.1-0.20250507153514-87eb42fe8882/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= @@ -169,6 +227,7 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= @@ -181,12 +240,19 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI= github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/testcontainers/testcontainers-go v0.41.0 h1:mfpsD0D36YgkxGj2LrIyxuwQ9i2wCKAD+ESsYM1wais= @@ -209,10 +275,12 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= @@ -229,40 +297,92 @@ go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09 go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= -golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= +golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0= -google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= -google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= -google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/api v0.276.0 h1:nVArUtfLEihtW+b0DdcqRGK1xoEm2+ltAihyztq7MKY= +google.golang.org/api v0.276.0/go.mod h1:Fnag/EWUPIcJXuIkP1pjoTgS5vdxlk3eeemL7Do6bvw= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= +google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI= +google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= +google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -275,6 +395,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc= diff --git a/manifest.yaml b/manifest.yaml index 3168060..77c2b55 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -9,32 +9,36 @@ contact: mailto:help@plakar.io connectors: - type: importer executable: mysqlImporter - homepage: https://github.com/PlakarKorp/integration-mysql - license: ISC protocols: [mysql] validator: ./importer/schema.json class: database subclass: mysql - type: exporter executable: mysqlExporter - homepage: https://github.com/PlakarKorp/integration-mysql - license: ISC protocols: [mysql] validator: ./exporter/schema.json class: database subclass: mysql + - type: importer + executable: mysqlProxyImporter + protocols: [mysql+gcsql] + validator: ./plugin/mysql-proxy-importer/schema.json + class: database + subclass: mysql + - type: exporter + executable: mysqlProxyExporter + protocols: [mysql+gcsql] + validator: ./plugin/mysql-proxy-exporter/schema.json + class: database + subclass: mysql - type: importer executable: mariadbImporter - homepage: https://github.com/PlakarKorp/integration-mysql - license: ISC protocols: [mysql+mariadb] validator: ./plugin/mariadb-importer/schema.json class: database subclass: mysql - type: exporter executable: mariadbExporter - homepage: https://github.com/PlakarKorp/integration-mysql - license: ISC protocols: [mysql+mariadb] validator: ./plugin/mariadb-exporter/schema.json class: database diff --git a/manifest/metadata.go b/manifest/metadata.go index 4257987..ed23cf1 100644 --- a/manifest/metadata.go +++ b/manifest/metadata.go @@ -11,8 +11,6 @@ import ( _ "github.com/go-sql-driver/mysql" ) -// --- Types --- - // ServerConfig holds key MySQL server configuration parameters. type ServerConfig struct { DataDir string `json:"datadir,omitempty"` @@ -31,11 +29,11 @@ type ServerConfig struct { // UserInfo describes a MySQL user account. type UserInfo struct { - User string `json:"user"` - Host string `json:"host"` - AuthPlugin string `json:"auth_plugin,omitempty"` - PasswordExpired bool `json:"password_expired,omitempty"` - AccountLocked bool `json:"account_locked,omitempty"` + User string `json:"user"` + Host string `json:"host"` + AuthPlugin string `json:"auth_plugin,omitempty"` + PasswordExpired bool `json:"password_expired,omitempty"` + AccountLocked bool `json:"account_locked,omitempty"` } // ColumnInfo describes a single column in a table. @@ -52,24 +50,24 @@ type ColumnInfo struct { // IndexInfo describes an index on a table. type IndexInfo struct { - Name string `json:"name"` - Columns []string `json:"columns"` - NonUnique bool `json:"non_unique"` - IndexType string `json:"index_type"` // BTREE, HASH, FULLTEXT, SPATIAL + Name string `json:"name"` + Columns []string `json:"columns"` + NonUnique bool `json:"non_unique"` + IndexType string `json:"index_type"` // BTREE, HASH, FULLTEXT, SPATIAL } // ConstraintInfo describes a table constraint. type ConstraintInfo struct { - Name string `json:"name"` - Type string `json:"type"` // PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK - Columns []string `json:"columns,omitempty"` - ReferencedTable string `json:"referenced_table,omitempty"` + Name string `json:"name"` + Type string `json:"type"` // PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK + Columns []string `json:"columns,omitempty"` + ReferencedTable string `json:"referenced_table,omitempty"` } // TableInfo describes a table or view in a database. type TableInfo struct { Name string `json:"name"` - Type string `json:"type"` // BASE TABLE, VIEW, SYSTEM VIEW + Type string `json:"type"` // BASE TABLE, VIEW, SYSTEM VIEW Engine string `json:"engine,omitempty"` RowFormat string `json:"row_format,omitempty"` RowEstimate int64 `json:"row_estimate,omitempty"` @@ -100,13 +98,13 @@ type RoutineInfo struct { // TriggerInfo describes a table trigger. type TriggerInfo struct { - Name string `json:"name"` - Event string `json:"event"` // INSERT, UPDATE, DELETE - Timing string `json:"timing"` // BEFORE, AFTER - Table string `json:"table"` - Definer string `json:"definer,omitempty"` - SQLMode string `json:"sql_mode,omitempty"` - Created string `json:"created,omitempty"` + Name string `json:"name"` + Event string `json:"event"` // INSERT, UPDATE, DELETE + Timing string `json:"timing"` // BEFORE, AFTER + Table string `json:"table"` + Definer string `json:"definer,omitempty"` + SQLMode string `json:"sql_mode,omitempty"` + Created string `json:"created,omitempty"` } // EventInfo describes a MySQL event scheduler event. @@ -132,13 +130,21 @@ type DatabaseInfo struct { Events []EventInfo `json:"events,omitempty"` } -// --- Helpers --- - func openDB(conn mysqlconn.ConnConfig, database string) (*sql.DB, error) { - db, err := sql.Open("mysql", conn.DSN(database)) + connStr := conn.DSN(database) + driver := "mysql" + targetHost := conn.Host + if conn.SqlCloud { + connStr = conn.DSNForCloudSQL(database) + driver = "cloudsql-mysql" + targetHost = conn.TrueHost + } + + db, err := sql.Open(driver, connStr) if err != nil { - return nil, fmt.Errorf("opening connection to %s: %w", conn.Host, err) + return nil, fmt.Errorf("opening connection to %s: %w", targetHost, err) } + db.SetMaxOpenConns(1) return db, nil } @@ -165,7 +171,6 @@ func nullableInt64(ni sql.NullInt64) int64 { return 0 } - // collectServerConfig queries key server variables from INFORMATION_SCHEMA / SHOW VARIABLES. func collectServerConfig(ctx context.Context, db *sql.DB) (ServerConfig, error) { query := `SELECT VARIABLE_NAME, VARIABLE_VALUE @@ -482,7 +487,7 @@ ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX`, schema) // Accumulate columns per (table, index). type key struct{ table, index string } type entry struct { - idx int // position in tables[tableIdx[table]].Indexes + idx int // position in tables[tableIdx[table]].Indexes nonUnique bool indexType string } diff --git a/mysqlconn/conn.go b/mysqlconn/conn.go index 9ac1dc5..005f960 100644 --- a/mysqlconn/conn.go +++ b/mysqlconn/conn.go @@ -3,12 +3,19 @@ package mysqlconn import ( "context" "fmt" + "io" + "log" + "net" "net/url" "os" "os/exec" "path/filepath" "strconv" "strings" + "sync" + + "cloud.google.com/go/cloudsqlconn" + "cloud.google.com/go/cloudsqlconn/mysql/mysql" ) // ConnConfig holds the parsed connection parameters for a MySQL-compatible server. @@ -36,6 +43,11 @@ type ConnConfig struct { // ExpectedFlavor is the server type this connector is configured for // ("mysql" or "mariadb"). When set, Ping rejects a server of the wrong type. ExpectedFlavor string + + SqlCloud bool + // When using SqlCloud this is the real host (the instance connection name) + // that was given, host will always be 127.0.0.1 + TrueHost string } func (cc ConnConfig) clientBin() string { @@ -54,7 +66,7 @@ func (cc ConnConfig) dumpBin() string { // ParseConnConfig builds a ConnConfig from the connector configuration map. // Standalone keys (host, port, …) always take precedence over the location URI. -func ParseConnConfig(config map[string]string) (ConnConfig, error) { +func ParseConnConfig(proxy bool, config map[string]string) (ConnConfig, error) { cc := ConnConfig{ Host: "127.0.0.1", Port: "3306", @@ -109,9 +121,73 @@ func ParseConnConfig(config map[string]string) (ConnConfig, error) { cc.BinDir = v } + // We are in a Google Cloud environment, attempt to use gcloud-sql-proxy + if proxy { + port, err := setupCloudSqlProxy(cc) + if err != nil { + return cc, err + } + + // Override connection params to go through the proxy now that it is + // running + cc.Port = strconv.Itoa(port) + cc.TrueHost = cc.Host + cc.Host = "127.0.0.1" + cc.SqlCloud = true + } + return cc, nil } +func setupCloudSqlProxy(cc ConnConfig) (int, error) { + cleanup, err := mysql.RegisterDriver("cloudsql-mysql") + if err != nil { + return 0, err + } + + d, err := cloudsqlconn.NewDialer(context.Background()) + if err != nil { + return 0, err + } + + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return 0, err + } + + port := l.Addr().(*net.TCPAddr).Port + + go func() { + defer cleanup() + defer d.Close() + for { + c, err := l.Accept() + if err != nil { + return // listener closed just end the background worker. + } + + go func() { + defer c.Close() + s, err := d.Dial(context.Background(), cc.Host) + if err != nil { + log.Printf("failed to dial to %s: %s", cc.Host, err) + return + } + + defer s.Close() + + wg := sync.WaitGroup{} + wg.Go(func() { io.Copy(s, c) }) + wg.Go(func() { io.Copy(c, s) }) + + wg.Wait() + }() + } + }() + + return port, nil +} + func parseURI(uri string, cc *ConnConfig) error { idx := strings.Index(uri, "://") if idx < 0 || !strings.HasPrefix(uri, "mysql") { @@ -250,6 +326,33 @@ func (cc ConnConfig) DSN(database string) string { return dsn.String() } +func (cc ConnConfig) DSNForCloudSQL(database string) string { + // Format: user:pass@cloudsql-mysql(host:port)/database?params + var dsn strings.Builder + if cc.Username != "" { + dsn.WriteString(cc.Username) + if cc.Password != "" { + dsn.WriteByte(':') + dsn.WriteString(cc.Password) + } + dsn.WriteByte('@') + } + dsn.WriteString("cloudsql-mysql(") + dsn.WriteString(cc.TrueHost) + dsn.WriteString(")/") + dsn.WriteString(database) + dsn.WriteString("?parseTime=true&multiStatements=true") + if cc.SSLMode != "" { + // go-sql-driver uses a "tls" parameter with different value names than the CLI. + tls := sslModeToTLS(cc.SSLMode) + if tls != "" { + dsn.WriteString("&tls=") + dsn.WriteString(tls) + } + } + return dsn.String() +} + func sslModeToTLS(mode string) string { switch strings.ToLower(mode) { case "disabled": diff --git a/plugin/mariadb-exporter/mariadb-exporter.go b/plugin/mariadb-exporter/mariadb-exporter.go index 198f39f..07e31ca 100644 --- a/plugin/mariadb-exporter/mariadb-exporter.go +++ b/plugin/mariadb-exporter/mariadb-exporter.go @@ -12,7 +12,7 @@ import ( ) func newMariaDB(_ context.Context, _ *connectors.Options, proto string, config map[string]string) (iexporter.Exporter, error) { - conn, err := mysqlconn.ParseConnConfig(config) + conn, err := mysqlconn.ParseConnConfig(false, config) if err != nil { return nil, err } diff --git a/plugin/mariadb-importer/mariadb-importer.go b/plugin/mariadb-importer/mariadb-importer.go index e9971ba..72b16e3 100644 --- a/plugin/mariadb-importer/mariadb-importer.go +++ b/plugin/mariadb-importer/mariadb-importer.go @@ -17,7 +17,7 @@ type mariadbImporter struct { } func newMariaDB(_ context.Context, _ *connectors.Options, proto string, config map[string]string) (iimporter.Importer, error) { - conn, err := mysqlconn.ParseConnConfig(config) + conn, err := mysqlconn.ParseConnConfig(false, config) if err != nil { return nil, err } diff --git a/plugin/mysql-exporter/mysql-exporter.go b/plugin/mysql-exporter/mysql-exporter.go index 557b27f..9c273c8 100644 --- a/plugin/mysql-exporter/mysql-exporter.go +++ b/plugin/mysql-exporter/mysql-exporter.go @@ -12,7 +12,7 @@ import ( ) func newMySQL(_ context.Context, _ *connectors.Options, proto string, config map[string]string) (iexporter.Exporter, error) { - conn, err := mysqlconn.ParseConnConfig(config) + conn, err := mysqlconn.ParseConnConfig(false, config) if err != nil { return nil, err } diff --git a/plugin/mysql-importer/mysql-importer.go b/plugin/mysql-importer/mysql-importer.go index 10d05d9..f7c8509 100644 --- a/plugin/mysql-importer/mysql-importer.go +++ b/plugin/mysql-importer/mysql-importer.go @@ -22,7 +22,7 @@ type mysqlImporter struct { } func newMySQL(_ context.Context, _ *connectors.Options, proto string, config map[string]string) (iimporter.Importer, error) { - conn, err := mysqlconn.ParseConnConfig(config) + conn, err := mysqlconn.ParseConnConfig(false, config) if err != nil { return nil, err } diff --git a/plugin/mysql-proxy-exporter/mysql-proxy-exporter.go b/plugin/mysql-proxy-exporter/mysql-proxy-exporter.go new file mode 100644 index 0000000..5a756dd --- /dev/null +++ b/plugin/mysql-proxy-exporter/mysql-proxy-exporter.go @@ -0,0 +1,27 @@ +package main + +import ( + "context" + "os" + + sdk "github.com/PlakarKorp/go-kloset-sdk" + "github.com/PlakarKorp/integration-mysql/exporter" + "github.com/PlakarKorp/integration-mysql/mysqlconn" + "github.com/PlakarKorp/kloset/connectors" + iexporter "github.com/PlakarKorp/kloset/connectors/exporter" +) + +func newMySQL(_ context.Context, _ *connectors.Options, proto string, config map[string]string) (iexporter.Exporter, error) { + conn, err := mysqlconn.ParseConnConfig(true, config) + if err != nil { + return nil, err + } + conn.ClientBin = "mysql" + conn.DumpBin = "mysqldump" + conn.ExpectedFlavor = "mysql" + return exporter.New(proto, conn, config, "mysql") +} + +func main() { + sdk.EntrypointExporter(os.Args, newMySQL) +} diff --git a/plugin/mysql-proxy-exporter/schema.json b/plugin/mysql-proxy-exporter/schema.json new file mode 100644 index 0000000..a8eec12 --- /dev/null +++ b/plugin/mysql-proxy-exporter/schema.json @@ -0,0 +1,47 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Plakar MySQL Exporter Connector Config (Google SQL Proxy)", + "type": "object", + "additionalProperties": false, + "properties": { + "location": { + "type": "string", + "minLength": 1, + "description": "MySQL connection URI: mysql+gcsql://[user[:password]@]localhost[/database]. If a database path is included, only that database is backed up; otherwise all databases are backed up with --all-databases.", + "pattern": "^mysql\\+gcsql://.*$" + }, + "host": { + "type": "string", + "minLength": 1, + "description": "Google Cloud SQL Connection name" + }, + "username": { + "type": "string", + "minLength": 1, + "description": "MySQL username. Overrides the username in the location URI." + }, + "password": { + "type": "string", + "minLength": 1, + "description": "MySQL password. Overrides the password in the location URI." + }, + "database": { + "type": "string", + "minLength": 1, + "description": "Target database for restore. If omitted, the database name is inferred from the dump filename (e.g. mydb.sql → mydb). Not used when restoring an all.sql dump." + }, + "create_db": { + "type": "boolean", + "description": "When true, issues CREATE DATABASE IF NOT EXISTS before restoring a single-database dump. Defaults to false." + }, + "force": { + "type": "boolean", + "description": "When true, passes --force to mysql so restore continues after SQL errors. Defaults to false." + }, + "mysql_bin_dir": { + "type": "string", + "minLength": 1, + "description": "Directory containing the MySQL client binary (mysql). When omitted, the binary is resolved via $PATH." + } + } +} diff --git a/plugin/mysql-proxy-importer/mysql-proxy-importer.go b/plugin/mysql-proxy-importer/mysql-proxy-importer.go new file mode 100644 index 0000000..3fc0a83 --- /dev/null +++ b/plugin/mysql-proxy-importer/mysql-proxy-importer.go @@ -0,0 +1,91 @@ +package main + +import ( + "context" + "fmt" + "os" + "strconv" + "strings" + + sdk "github.com/PlakarKorp/go-kloset-sdk" + "github.com/PlakarKorp/integration-mysql/importer" + "github.com/PlakarKorp/integration-mysql/manifest" + "github.com/PlakarKorp/integration-mysql/mysqlconn" + "github.com/PlakarKorp/kloset/connectors" + iimporter "github.com/PlakarKorp/kloset/connectors/importer" +) + +type mysqlImporter struct { + importer.Importer + columnStatistics bool + setGTIDPurged string +} + +func newMySQL(_ context.Context, _ *connectors.Options, proto string, config map[string]string) (iimporter.Importer, error) { + conn, err := mysqlconn.ParseConnConfig(true, config) + if err != nil { + return nil, err + } + conn.ClientBin = "mysql" + conn.DumpBin = "mysqldump" + conn.ExpectedFlavor = "mysql" + + base, err := importer.New(proto, conn, config) + if err != nil { + return nil, err + } + + m := &mysqlImporter{Importer: *base, columnStatistics: true} + + if v, ok := config["column_statistics"]; ok && v != "" { + cs, err := strconv.ParseBool(v) + if err != nil { + return nil, fmt.Errorf("invalid value for column_statistics: %w", err) + } + m.columnStatistics = cs + } + + if v, ok := config["set_gtid_purged"]; ok && v != "" { + v = strings.ToUpper(v) + switch v { + case "AUTO", "ON", "OFF": + default: + return nil, fmt.Errorf("invalid value for set_gtid_purged: %q", v) + } + m.setGTIDPurged = v + } + + return m, nil +} + +func (m *mysqlImporter) Import(ctx context.Context, records chan<- *connectors.Record, _ <-chan *connectors.Result) error { + if err := m.Conn.CheckFlavor(ctx, "mysql"); err != nil { + return err + } + + cs := m.columnStatistics + opts := m.CommonManifestOptions() + opts.ColumnStatistics = &cs + opts.SetGTIDPurged = m.setGTIDPurged + + cfg := manifest.Config{ + Conn: m.Conn, + Flavor: "mysql", + Database: m.Database, + Options: opts, + } + + var extraFlags []string + if !m.columnStatistics { + extraFlags = append(extraFlags, "--column-statistics=0") + } + if m.setGTIDPurged != "" { + extraFlags = append(extraFlags, "--set-gtid-purged="+m.setGTIDPurged) + } + + return m.Run(ctx, records, cfg, extraFlags) +} + +func main() { + sdk.EntrypointImporter(os.Args, newMySQL) +} diff --git a/plugin/mysql-proxy-importer/schema.json b/plugin/mysql-proxy-importer/schema.json new file mode 100644 index 0000000..6810aff --- /dev/null +++ b/plugin/mysql-proxy-importer/schema.json @@ -0,0 +1,80 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Plakar MySQL Importer Connector Config (Google SQL Proxy)", + "type": "object", + "additionalProperties": false, + "properties": { + "location": { + "type": "string", + "minLength": 1, + "description": "MySQL connection URI: mysql+gcsql://[user[:password]@]localhost[/database]. If a database path is included, only that database is backed up; otherwise all databases are backed up with --all-databases.", + "pattern": "^mysql\\+gcsql://.*$" + }, + "host": { + "type": "string", + "minLength": 1, + "description": "Google Cloud SQL Connection name" + }, + "username": { + "type": "string", + "minLength": 1, + "description": "MySQL username. Overrides the username in the location URI." + }, + "password": { + "type": "string", + "minLength": 1, + "description": "MySQL password. Overrides the password in the location URI. Never appears on the command line; passed via MYSQL_PWD." + }, + "database": { + "type": "string", + "minLength": 1, + "description": "Name of the database to back up. If omitted, all non-system databases are backed up using --all-databases. Overrides the database in the location URI." + }, + "single_transaction": { + "type": "boolean", + "description": "Use --single-transaction for a consistent InnoDB snapshot without locking tables (default: true). Strongly recommended for production InnoDB databases. MyISAM tables still require table locks even with this option." + }, + "routines": { + "type": "boolean", + "description": "Include stored procedures and functions via --routines (default: true)." + }, + "events": { + "type": "boolean", + "description": "Include event scheduler events via --events (default: true)." + }, + "triggers": { + "type": "boolean", + "description": "Include triggers (default: true). Set to false to pass --skip-triggers." + }, + "no_data": { + "type": "boolean", + "description": "When true, passes --no-data so only DDL statements are dumped (no INSERT rows). Mutually exclusive with no_create_info." + }, + "no_create_info": { + "type": "boolean", + "description": "When true, passes --no-create-info so only INSERT statements are dumped (no DDL). Mutually exclusive with no_data." + }, + "no_tablespaces": { + "type": "boolean", + "description": "Suppress CREATE LOGFILE GROUP and CREATE TABLESPACE statements (--no-tablespaces, default: true). Recommended for restoring to managed or cloud MySQL instances that do not support custom tablespaces." + }, + "column_statistics": { + "type": "boolean", + "description": "Query information_schema.COLUMN_STATISTICS for histogram data (default: true, matching mysqldump 8.0 behaviour). Set to false to pass --column-statistics=0, which is required when the mysqldump binary is 8.0 but the source server is MySQL 5.7 or MariaDB." + }, + "hex_blob": { + "type": "boolean", + "description": "Dump BINARY, VARBINARY, BLOB, and BIT columns using hexadecimal notation (--hex-blob). Useful when the dump may be loaded on a system with different character set settings." + }, + "set_gtid_purged": { + "type": "string", + "enum": ["AUTO", "ON", "OFF"], + "description": "Controls the SET @@GLOBAL.GTID_PURGED statement in the dump (--set-gtid-purged). AUTO (default) includes it only when GTIDs are enabled on the server. Use OFF when restoring to a server that already has GTID history." + }, + "mysql_bin_dir": { + "type": "string", + "minLength": 1, + "description": "Directory containing the MySQL client binaries (mysqldump, mysql). When omitted, binaries are resolved via $PATH." + } + } +} diff --git a/tests/plakar-mariadb.Dockerfile b/tests/plakar-mariadb.Dockerfile index 834f36d..431a092 100644 --- a/tests/plakar-mariadb.Dockerfile +++ b/tests/plakar-mariadb.Dockerfile @@ -17,18 +17,20 @@ RUN apt-get update && \ RUN go install github.com/PlakarKorp/plakar@${PLAKAR_SHA} -COPY . /src +COPY . /go/src RUN set -e && \ mkdir -p /tmp/mysqlpkg && \ - cd /src && \ + cd /go/src && \ go build -o /tmp/mysqlpkg/mysqlImporter ./plugin/mysql-importer && \ go build -o /tmp/mysqlpkg/mysqlExporter ./plugin/mysql-exporter && \ + go build -o /tmp/mysqlpkg/mysqlProxyImporter ./plugin/mysql-proxy-importer && \ + go build -o /tmp/mysqlpkg/mysqlProxyExporter ./plugin/mysql-proxy-exporter && \ go build -o /tmp/mysqlpkg/mariadbImporter ./plugin/mariadb-importer && \ go build -o /tmp/mysqlpkg/mariadbExporter ./plugin/mariadb-exporter && \ - cp /src/manifest.yaml /tmp/mysqlpkg/ && \ + cp /go/src/manifest.yaml /tmp/mysqlpkg/ && \ cd /tmp/mysqlpkg && \ PTAR="mysql_v0.0.1_$(go env GOOS)_$(go env GOARCH).ptar" && \ plakar pkg create ./manifest.yaml v0.0.1 && \ plakar pkg add "./${PTAR}" && \ - rm -rf /tmp/mysqlpkg /src + rm -rf /tmp/mysqlpkg /go/src \ No newline at end of file diff --git a/tests/plakar-mysql.Dockerfile b/tests/plakar-mysql.Dockerfile index 11b40fe..67d270f 100644 --- a/tests/plakar-mysql.Dockerfile +++ b/tests/plakar-mysql.Dockerfile @@ -25,18 +25,20 @@ RUN go install github.com/PlakarKorp/plakar@${PLAKAR_SHA} ENV PATH="/root/go/bin:${PATH}" -COPY . /src +COPY . /go/src RUN set -e && \ mkdir -p /tmp/mysqlpkg && \ - cd /src && \ + cd /go/src && \ go build -o /tmp/mysqlpkg/mysqlImporter ./plugin/mysql-importer && \ go build -o /tmp/mysqlpkg/mysqlExporter ./plugin/mysql-exporter && \ + go build -o /tmp/mysqlpkg/mysqlProxyImporter ./plugin/mysql-proxy-importer && \ + go build -o /tmp/mysqlpkg/mysqlProxyExporter ./plugin/mysql-proxy-exporter && \ go build -o /tmp/mysqlpkg/mariadbImporter ./plugin/mariadb-importer && \ go build -o /tmp/mysqlpkg/mariadbExporter ./plugin/mariadb-exporter && \ - cp /src/manifest.yaml /tmp/mysqlpkg/ && \ + cp /go/src/manifest.yaml /tmp/mysqlpkg/ && \ cd /tmp/mysqlpkg && \ PTAR="mysql_v0.0.1_$(go env GOOS)_$(go env GOARCH).ptar" && \ plakar pkg create ./manifest.yaml v0.0.1 && \ plakar pkg add "./${PTAR}" && \ - rm -rf /tmp/mysqlpkg /src + rm -rf /tmp/mysqlpkg /go/src