vendor: run make vendor-update

This commit is contained in:
Aliaksandr Valialkin 2023-11-14 22:08:00 +01:00
parent 7227dca1df
commit dcea8dedb6
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
556 changed files with 5227 additions and 16722 deletions

80
go.mod
View file

@ -10,8 +10,8 @@ replace (
)
require (
cloud.google.com/go/storage v1.34.1
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0
cloud.google.com/go/storage v1.35.1
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0
github.com/VictoriaMetrics/fastcache v1.12.1
@ -20,10 +20,10 @@ require (
github.com/VictoriaMetrics/fasthttp v1.2.0
github.com/VictoriaMetrics/metrics v1.24.0
github.com/VictoriaMetrics/metricsql v0.69.0
github.com/aws/aws-sdk-go-v2 v1.22.1
github.com/aws/aws-sdk-go-v2/config v1.22.0
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.1
github.com/aws/aws-sdk-go-v2/service/s3 v1.42.0
github.com/aws/aws-sdk-go-v2 v1.22.2
github.com/aws/aws-sdk-go-v2/config v1.25.0
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.7
github.com/aws/aws-sdk-go-v2/service/s3 v1.42.1
github.com/cespare/xxhash/v2 v2.2.0
github.com/cheggaaa/pb/v3 v3.1.4
github.com/gogo/protobuf v1.3.2
@ -39,10 +39,10 @@ require (
github.com/valyala/gozstd v1.20.1
github.com/valyala/histogram v1.2.0
github.com/valyala/quicktemplate v1.7.0
golang.org/x/net v0.17.0
golang.org/x/oauth2 v0.13.0
golang.org/x/sys v0.13.0
google.golang.org/api v0.149.0
golang.org/x/net v0.18.0
golang.org/x/oauth2 v0.14.0
golang.org/x/sys v0.14.0
google.golang.org/api v0.150.0
gopkg.in/yaml.v2 v2.4.0
)
@ -56,33 +56,33 @@ require (
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/aws/aws-sdk-go v1.47.2 // indirect
github.com/aws/aws-sdk-go v1.47.11 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.0 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.15.1 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.5.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.16.0 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.3 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.2 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.17.0 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.19.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.25.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.17.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.19.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.25.1 // indirect
github.com/aws/smithy-go v1.16.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dennwc/varint v1.0.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
github.com/golang-jwt/jwt/v5 v5.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/s2a-go v0.1.7 // indirect
@ -115,25 +115,25 @@ require (
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/collector/pdata v1.0.0-rcv0017 // indirect
go.opentelemetry.io/collector/semconv v0.88.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect
go.opentelemetry.io/otel v1.19.0 // indirect
go.opentelemetry.io/otel/metric v1.19.0 // indirect
go.opentelemetry.io/otel/trace v1.19.0 // indirect
go.opentelemetry.io/collector/pdata v1.0.0-rcv0018 // indirect
go.opentelemetry.io/collector/semconv v0.89.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0 // indirect
go.opentelemetry.io/otel v1.20.0 // indirect
go.opentelemetry.io/otel/metric v1.20.0 // indirect
go.opentelemetry.io/otel/trace v1.20.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/goleak v1.3.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/sync v0.4.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/crypto v0.15.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
golang.org/x/sync v0.5.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.4.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

158
go.sum
View file

@ -38,11 +38,11 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.34.1 h1:H2Af2dU5J0PF7A5B+ECFIce+RqxVnrVilO+cu0TS3MI=
cloud.google.com/go/storage v1.34.1/go.mod h1:VN1ElqqvR9adg1k9xlkUJ55cMOP1/QjnNNuT5xQL6dY=
cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w=
cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8=
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 h1:9kDVnTz3vbfweTqAUmk/a/pH5pWFCHtvRpHYC0G/dcA=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0/go.mod h1:3Ug6Qzto9anB6mGlEdgYMDF5zHQ+wwhEaYR4s17PHMw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 h1:fb8kj/Dh4CSwgsOzHeZY4Xh68cFVbzXx+ONXGMY//4w=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0/go.mod h1:uReU2sSxZExRPBAg3qKzmAucSi51+SP1OhohieR821Q=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 h1:d81/ng9rET2YqdVkVwkb6EXeRrLJIwyGnJcAlAWKwhs=
@ -86,44 +86,44 @@ github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu
github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.47.2 h1:KEdO2PbjfEBmHvnEwbYEpr65ZIkmwK5aB85Gj19ASuA=
github.com/aws/aws-sdk-go v1.47.2/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go-v2 v1.22.1 h1:sjnni/AuoTXxHitsIdT0FwmqUuNUuHtufcVDErVFT9U=
github.com/aws/aws-sdk-go-v2 v1.22.1/go.mod h1:Kd0OJtkW3Q0M0lUWGszapWjEvrXDzRW+D21JNsroB+c=
github.com/aws/aws-sdk-go v1.47.11 h1:Dol+MA+hQblbnXUI3Vk9qvoekU6O1uDEuAItezjiWNQ=
github.com/aws/aws-sdk-go v1.47.11/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go-v2 v1.22.2 h1:lV0U8fnhAnPz8YcdmZVV60+tr6CakHzqA6P8T46ExJI=
github.com/aws/aws-sdk-go-v2 v1.22.2/go.mod h1:Kd0OJtkW3Q0M0lUWGszapWjEvrXDzRW+D21JNsroB+c=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.0 h1:hHgLiIrTRtddC0AKcJr5s7i/hLgcpTt+q/FKxf1Zayk=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.0/go.mod h1:w4I/v3NOWgD+qvs1NPEwhd++1h3XPHFaVxasfY6HlYQ=
github.com/aws/aws-sdk-go-v2/config v1.22.0 h1:9Mm99OalzZRz0ab5fpodMoHBApHS6pqRNp3M9NmzvDg=
github.com/aws/aws-sdk-go-v2/config v1.22.0/go.mod h1:2eWgw5lps8fKI7LZVTrRTYP6HE6k/uEFUuTSHfXwqP0=
github.com/aws/aws-sdk-go-v2/credentials v1.15.1 h1:hmf6lAm9hk7uLCfapZn/jL05lm6Uwdbn1B0fgjyuf4M=
github.com/aws/aws-sdk-go-v2/credentials v1.15.1/go.mod h1:QTcHga3ZbQOneJuxmGBOCxiClxmp+TlvmjFexAnJ790=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.2 h1:gIeH4+o1MN/caGBWjoGQTUTIu94xD6fI5B2+TcwBf70=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.2/go.mod h1:wLyMIo/zPOhQhPXTddpfdkSleyigtFi8iMnC+2m/SK4=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.1 h1:ULswbgGNVrW8zEhkCNwrwXrs1mUvy2JTqWaCRsD2ZZw=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.1/go.mod h1:pAXgsDPk1rRwwfkz8/9ISO75vXEHqTGIgbLhGqqQ1GY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.1 h1:fi1ga6WysOyYb5PAf3Exd6B5GiSNpnZim4h1rhlBqx0=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.1/go.mod h1:V5CY8wNurvPUibTi9mwqUqpiFZ5LnioKWIFUDtIzdI8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.1 h1:ZpaV/j48RlPc4AmOZuPv22pJliXjXq8/reL63YzyFnw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.1/go.mod h1:R8aXraabD2e3qv1csxM14/X9WF4wFMIY0kH4YEtYD5M=
github.com/aws/aws-sdk-go-v2/internal/ini v1.5.0 h1:DqOQvIfmGkXZUVJnl9VRk0AnxyS59tCtX9k1Pyss4Ak=
github.com/aws/aws-sdk-go-v2/internal/ini v1.5.0/go.mod h1:VV/Kbw9Mg1GWJOT9WK+oTL3cWZiXtapnNvDSRqTZLsg=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.1 h1:vzYLDkwTw4CY0vUk84MeSufRf8XIsC/GsoIFXD60sTg=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.1/go.mod h1:ToBFBnjeGR2ruMx8IWp/y7vSK3Irj5/oPwifruiqoOM=
github.com/aws/aws-sdk-go-v2/config v1.25.0 h1:WCwAqyrM/kqYi6pHjVpq/w2pLydeGKv8Af9vdtO3ciM=
github.com/aws/aws-sdk-go-v2/config v1.25.0/go.mod h1:1QMnmhoWcR6957nC1MUUhhOLx9NOGFSVNG3Mag9vLU4=
github.com/aws/aws-sdk-go-v2/credentials v1.16.0 h1:sSEHkXonpZBSPcyUBDRlZjxOi14qM/UK7/vfKhGwmTo=
github.com/aws/aws-sdk-go-v2/credentials v1.16.0/go.mod h1:tXM8wmaeAhfC7nZoCxb0FzM/aRaB1m1WQ7x0qlBLq80=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.3 h1:G5KawTAkyHH6WyKQCdHiW4h3PmAXNJpOgwKg3H7sDRE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.3/go.mod h1:hugKmSFnZB+HgNI1sYGT14BUPZkO6alC/e0AWu+0IAQ=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.7 h1:HDsYN1Qm6fFDKzaGfYVGGBNkifZAHWVBrzrILGhpdIU=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.7/go.mod h1:998wVfFSQY1hGhRXfv6QYGY08qi/L7Apr1XmJSWS5YI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.2 h1:AaQsr5vvGR7rmeSWBtTCcw16tT9r51mWijuCQhzLnq8=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.2/go.mod h1:o1IiRn7CWocIFTXJjGKJDOwxv1ibL53NpcvcqGWyRBA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.2 h1:UZx8SXZ0YtzRiALzYAWcjb9Y9hZUR7MBKaBQ5ouOjPs=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.2/go.mod h1:ipuRpcSaklmxR6C39G187TpBAO132gUfleTGccUPs8c=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.0 h1:usgqiJtamuGIBj+OvYmMq89+Z1hIKkMJToz1WpoeNUY=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.0/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.2 h1:pyVrNAf7Hwz0u39dLKN5t+n0+K/3rMYKuiOoIum3AsU=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.2/go.mod h1:mydrfOb9uiOYCxuCPR8YHQNQyGQwUQ7gPMZGBKbH8NY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.0 h1:CJxo7ZBbaIzmXfV3hjcx36n9V87gJsIUPJflwqEHl3Q=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.0/go.mod h1:yjVfjuY4nD1EW9i387Kau+I6V5cBA5YnC/mWNopjZrI=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.1 h1:15FUCJzAP9Y25nioTqTrGlZmhOtthaXBWlt4pS+d3Xo=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.1/go.mod h1:5655NW53Un6l7JzkI6AA3rZvf0m532cSnLThA1fVXcA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.1 h1:2OXw3ppu1XsB6rqKEMV4tnecTjIY3PRV2U6IP6KPJQo=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.1/go.mod h1:FZB4AdakIqW/yERVdGJA6Z9jraax1beXfhBBnK2wwR8=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.1 h1:dnl0klXYX9EKpzZbWlH5LJL+YTcEZcJEMPFFr/rAHUQ=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.1/go.mod h1:Mfk/9Joso4tCQYzM4q4HRUIqwln8lnIIMB/OE8Zebdc=
github.com/aws/aws-sdk-go-v2/service/s3 v1.42.0 h1:u0YoSrxjr3Lm+IqIlRAV+4YTFwkXjyB9db9CfUFge2w=
github.com/aws/aws-sdk-go-v2/service/s3 v1.42.0/go.mod h1:98EIdRu+BNsdqITsXfy+57TZfwlUQC9aDn9a9qoo90U=
github.com/aws/aws-sdk-go-v2/service/sso v1.17.0 h1:I/Oh3IxGPfHXiGnwM54TD6hNr/8TlUrBXAtTyGhR+zw=
github.com/aws/aws-sdk-go-v2/service/sso v1.17.0/go.mod h1:H6NCMvDBqA+CvIaXzaSqM6LWtzv9BzZrqBOqz+PzRF8=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.19.0 h1:irbXQkfVYIRaewYSXcu4yVk0m2T+JzZd0dkop7FjmO0=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.19.0/go.mod h1:4wPNCkM22+oRe71oydP66K50ojDUC33XutSMi2pEF/M=
github.com/aws/aws-sdk-go-v2/service/sts v1.25.0 h1:sYIFy8tm1xQwRvVQ4CRuBGXKIg9sHNuG6+3UAQuoujk=
github.com/aws/aws-sdk-go-v2/service/sts v1.25.0/go.mod h1:S/LOQUeYDfJeJpFCIJDMjy7dwL4aA33HUdVi+i7uH8k=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.2 h1:f2LhPofnjcdOQKRtumKjMvIHkfSQ8aH/rwKUDEQ/SB4=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.2/go.mod h1:q+xX0H4OfuWDuBy7y/LDi4v8IBOWuF+vtp8Z6ex+lw4=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.2 h1:h7j73yuAVVjic8pqswh+L/7r2IHP43QwRyOu6zcCDDE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.2/go.mod h1:H07AHdK5LSy8F7EJUQhoxyiCNkePoHj2D8P2yGTWafo=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.2 h1:gbIaOzpXixUpoPK+js/bCBK1QBDXM22SigsnzGZio0U=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.2/go.mod h1:p+S7RNbdGN8qgHDSg2SCQJ9FeMAmvcETQiVpeGhYnNM=
github.com/aws/aws-sdk-go-v2/service/s3 v1.42.1 h1:o6MCcX1rJW8Y3g+hvg2xpjF6JR6DftuYhfl3Nc1WV9Q=
github.com/aws/aws-sdk-go-v2/service/s3 v1.42.1/go.mod h1:UDtxEWbREX6y4KREapT+jjtjoH0TiVSS6f5nfaY1UaM=
github.com/aws/aws-sdk-go-v2/service/sso v1.17.1 h1:km+ZNjtLtpXYf42RdaDZnNHm9s7SYAuDGTafy6nd89A=
github.com/aws/aws-sdk-go-v2/service/sso v1.17.1/go.mod h1:aHBr3pvBSD5MbzOvQtYutyPLLRPbl/y9x86XyJJnUXQ=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.19.1 h1:iRFNqZH4a67IqPvK8xxtyQYnyrlsvwmpHOe9r55ggBA=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.19.1/go.mod h1:pTy5WM+6sNv2tB24JNKFtn6EvciQ5k40ZJ0pq/Iaxj0=
github.com/aws/aws-sdk-go-v2/service/sts v1.25.1 h1:txgVXIXWPXyqdiVn92BV6a/rgtpX31HYdsOYj0sVQQQ=
github.com/aws/aws-sdk-go-v2/service/sts v1.25.1/go.mod h1:VAiJiNaoP1L89STFlEMgmHX1bKixY+FaP+TpRFrmyZ4=
github.com/aws/smithy-go v1.16.0 h1:gJZEH/Fqh+RsvlJ1Zt4tVAtV6bKkp3cC+R6FCZMNzik=
github.com/aws/smithy-go v1.16.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@ -164,10 +164,10 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -195,8 +195,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.1.0 h1:UGKbA/IPjtS6zLcdB7i5TyACMgSbOTiR8qzXgw8HWQU=
github.com/golang-jwt/jwt/v5 v5.1.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -456,18 +456,18 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/collector/pdata v1.0.0-rcv0017 h1:AgALhc2VenoA5l1DvTdg7mkzaBGqoTSuMkAtjsttBFo=
go.opentelemetry.io/collector/pdata v1.0.0-rcv0017/go.mod h1:Rv9fOclA5AtM/JGm0d4jBOIAo1+jBA13UT5Bx0ovXi4=
go.opentelemetry.io/collector/semconv v0.88.0 h1:8TVP4hYaUC87S6CCLKNoSxsUE0ChldE4vqotvNHHUnE=
go.opentelemetry.io/collector/semconv v0.88.0/go.mod h1:j/8THcqVxFna1FpvA2zYIsUperEtOaRaqoLYIN4doWw=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q=
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
go.opentelemetry.io/collector/pdata v1.0.0-rcv0018 h1:a2IHOZKphRzPagcvOHQHHUE0DlITFSKlIBwaWhPZpl4=
go.opentelemetry.io/collector/pdata v1.0.0-rcv0018/go.mod h1:oNIcTRyEJYIfMcRYyyh5lquDU0Vl+ktTL6ka+p+dYvg=
go.opentelemetry.io/collector/semconv v0.89.0 h1:Sw+MiI3/oiYIY+ebkanZsOaBxXMx3sqnH1/6NaD4rLQ=
go.opentelemetry.io/collector/semconv v0.89.0/go.mod h1:j/8THcqVxFna1FpvA2zYIsUperEtOaRaqoLYIN4doWw=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0 h1:1eHu3/pUSWaOgltNK3WJFaywKsTIr/PwvHyDmi0lQA0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.0/go.mod h1:HyABWq60Uy1kjJSa2BVOxUVao8Cdick5AWSKPutqy6U=
go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc=
go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs=
go.opentelemetry.io/otel/metric v1.20.0 h1:ZlrO8Hu9+GAhnepmRGhSU7/VkpjrNowxRN9GyKR4wzA=
go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM=
go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ=
go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@ -483,8 +483,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -540,16 +540,16 @@ golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0=
golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -561,8 +561,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -606,12 +606,12 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -621,13 +621,13 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY=
golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
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=
@ -691,8 +691,8 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.149.0 h1:b2CqT6kG+zqJIVKRQ3ELJVLN1PwHZ6DJ3dW8yl82rgY=
google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI=
google.golang.org/api v0.150.0 h1:Z9k22qD289SZ8gCJrk4DrWXkNjtfvKAUo/l1ma8eBYE=
google.golang.org/api v0.150.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg=
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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -730,12 +730,12 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U=
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4=
google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405 h1:HJMDndgxest5n2y77fnErkM62iUsptE/H8p0dC2Huo4=
google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405/go.mod h1:oT32Z4o8Zv2xPQTg0pbVaPr0MPOH6f14RgXt7zfIpwg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=

View file

@ -1,6 +1,20 @@
# Changes
## [1.35.1](https://github.com/googleapis/google-cloud-go/compare/storage/v1.35.0...storage/v1.35.1) (2023-11-09)
### Bug Fixes
* **storage:** Rename aux.go to auxiliary.go fixing windows build ([ba23673](https://github.com/googleapis/google-cloud-go/commit/ba23673da7707c31292e4aa29d65b7ac1446d4a6))
## [1.35.0](https://github.com/googleapis/google-cloud-go/compare/storage/v1.34.1...storage/v1.35.0) (2023-11-09)
### Features
* **storage:** Change gRPC writes to use bi-directional streams ([#8930](https://github.com/googleapis/google-cloud-go/issues/8930)) ([3e23a36](https://github.com/googleapis/google-cloud-go/commit/3e23a364b1a20c4fda7aef257e4136586ec769a4))
## [1.34.1](https://github.com/googleapis/google-cloud-go/compare/storage/v1.34.0...storage/v1.34.1) (2023-11-01)

View file

@ -1066,7 +1066,7 @@ func (c *grpcStorageClient) OpenWriter(params *openWriterParams, opts ...storage
}
}
o, off, finalized, err := gw.uploadBuffer(recvd, offset, doneReading)
o, off, err := gw.uploadBuffer(recvd, offset, doneReading)
if err != nil {
err = checkCanceled(err)
errorf(err)
@ -1085,9 +1085,9 @@ func (c *grpcStorageClient) OpenWriter(params *openWriterParams, opts ...storage
progress(offset)
}
// When we are done reading data and the chunk has been finalized,
// we are done.
if doneReading && finalized {
// When we are done reading data without errors, set the object and
// finish.
if doneReading {
// Build Object from server's response.
setObj(newObjectFromProto(o))
return
@ -1537,7 +1537,7 @@ type gRPCWriter struct {
chunkSize int
// The gRPC client-stream used for sending buffers.
stream storagepb.Storage_WriteObjectClient
stream storagepb.Storage_BidiWriteObjectClient
// The Resumable Upload ID started by a gRPC-based Writer.
upid string
@ -1581,45 +1581,56 @@ func (w *gRPCWriter) queryProgress() (int64, error) {
return persistedSize, err
}
// uploadBuffer opens a Write stream and uploads the buffer at the given offset (if
// uploading a chunk for a resumable uploadBuffer), and will mark the write as
// finished if we are done receiving data from the user. The resulting write
// offset after uploading the buffer is returned, as well as a boolean
// indicating if the Object has been finalized. If it has been finalized, the
// final Object will be returned as well. Finalizing the upload is primarily
// important for Resumable Uploads. A simple or multi-part upload will always
// be finalized once the entire buffer has been written.
func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*storagepb.Object, int64, bool, error) {
var err error
var finishWrite bool
var sent, limit int = 0, maxPerMessageWriteSize
// uploadBuffer uploads the buffer at the given offset using a bi-directional
// Write stream. It will open a new stream if necessary (on the first call or
// after resuming from failure). The resulting write offset after uploading the
// buffer is returned, as well as well as the final Object if the upload is
// completed.
//
// Returns object, persisted size, and any error that is not retriable.
func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*storagepb.Object, int64, error) {
var shouldRetry = ShouldRetry
if w.settings.retry != nil && w.settings.retry.shouldRetry != nil {
shouldRetry = w.settings.retry.shouldRetry
}
offset := start
var err error
var lastWriteOfEntireObject bool
sent := 0
writeOffset := start
toWrite := w.buf[:recvd]
// Send a request with as many bytes as possible.
// Loop until all bytes are sent.
for {
// This indicates that this is the last message and the remaining
// data fits in one message.
belowLimit := recvd-sent <= limit
if belowLimit {
limit = recvd - sent
bytesNotYetSent := recvd - sent
remainingDataFitsInSingleReq := bytesNotYetSent <= maxPerMessageWriteSize
if remainingDataFitsInSingleReq && doneReading {
lastWriteOfEntireObject = true
}
if belowLimit && doneReading {
finishWrite = true
// Send the maximum amount of bytes we can, unless we don't have that many.
bytesToSendInCurrReq := maxPerMessageWriteSize
if remainingDataFitsInSingleReq {
bytesToSendInCurrReq = bytesNotYetSent
}
// Prepare chunk section for upload.
data := toWrite[sent : sent+limit]
req := &storagepb.WriteObjectRequest{
Data: &storagepb.WriteObjectRequest_ChecksummedData{
data := toWrite[sent : sent+bytesToSendInCurrReq]
req := &storagepb.BidiWriteObjectRequest{
Data: &storagepb.BidiWriteObjectRequest_ChecksummedData{
ChecksummedData: &storagepb.ChecksummedData{
Content: data,
},
},
WriteOffset: offset,
FinishWrite: finishWrite,
WriteOffset: writeOffset,
FinishWrite: lastWriteOfEntireObject,
Flush: remainingDataFitsInSingleReq,
StateLookup: remainingDataFitsInSingleReq,
}
// Open a new stream if necessary and set the first_message field on
@ -1628,19 +1639,20 @@ func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*st
if w.stream == nil {
hds := []string{"x-goog-request-params", fmt.Sprintf("bucket=projects/_/buckets/%s", url.QueryEscape(w.bucket))}
ctx := gax.InsertMetadataIntoOutgoingContext(w.ctx, hds...)
w.stream, err = w.c.raw.WriteObject(ctx)
w.stream, err = w.c.raw.BidiWriteObject(ctx)
if err != nil {
return nil, 0, false, err
return nil, 0, err
}
if w.upid != "" {
req.FirstMessage = &storagepb.WriteObjectRequest_UploadId{UploadId: w.upid}
} else {
if w.upid != "" { // resumable upload
req.FirstMessage = &storagepb.BidiWriteObjectRequest_UploadId{UploadId: w.upid}
} else { // non-resumable
spec, err := w.writeObjectSpec()
if err != nil {
return nil, 0, false, err
return nil, 0, err
}
req.FirstMessage = &storagepb.WriteObjectRequest_WriteObjectSpec{
req.FirstMessage = &storagepb.BidiWriteObjectRequest_WriteObjectSpec{
WriteObjectSpec: spec,
}
req.CommonObjectRequestParams = toProtoCommonObjectRequestParams(w.encryptionKey)
@ -1650,15 +1662,24 @@ func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*st
// on the *last* message of the stream (instead of the first).
req.ObjectChecksums = toProtoChecksums(w.sendCRC32C, w.attrs)
}
}
err = w.stream.Send(req)
if err == io.EOF {
// err was io.EOF. The client-side of a stream only gets an EOF on Send
// when the backend closes the stream and wants to return an error
// status. Closing the stream receives the status as an error.
_, err = w.stream.CloseAndRecv()
// status.
// Receive from the stream Recv() until it returns a non-nil error
// to receive the server's status as an error. We may get multiple
// messages before the error due to buffering.
err = nil
for err == nil {
_, err = w.stream.Recv()
}
// Drop the stream reference as a new one will need to be created if
// we retry.
w.stream = nil
// Drop the stream reference as a new one will need to be created if
// we can retry the upload
@ -1666,26 +1687,28 @@ func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*st
// Retriable errors mean we should start over and attempt to
// resend the entire buffer via a new stream.
// If not retriable, falling through will return the error received
// from closing the stream.
// If not retriable, falling through will return the error received.
if shouldRetry(err) {
sent = 0
finishWrite = false
// TODO: Add test case for failure modes of querying progress.
offset, err = w.determineOffset(start)
if err == nil {
continue
writeOffset, err = w.determineOffset(start)
if err != nil {
return nil, 0, err
}
sent = int(writeOffset) - int(start)
// Continue sending requests, opening a new stream and resending
// any bytes not yet persisted as per QueryWriteStatus
continue
}
}
if err != nil {
return nil, 0, false, err
return nil, 0, err
}
// Update the immediate stream's sent total and the upload offset with
// the data sent.
sent += len(data)
offset += int64(len(data))
writeOffset += int64(len(data))
// Not done sending data, do not attempt to commit it yet, loop around
// and send more data.
@ -1694,31 +1717,81 @@ func (w *gRPCWriter) uploadBuffer(recvd int, start int64, doneReading bool) (*st
}
// The buffer has been uploaded and there is still more data to be
// uploaded, but this is not a resumable upload session. Therefore
// keep the stream open and don't commit yet.
if !finishWrite && w.chunkSize == 0 {
return nil, offset, false, nil
// uploaded, but this is not a resumable upload session. Therefore,
// don't check persisted data.
if !lastWriteOfEntireObject && w.chunkSize == 0 {
return nil, writeOffset, nil
}
// Done sending data. Close the stream to "commit" the data sent.
resp, finalized, err := w.commit()
// Done sending data (remainingDataFitsInSingleReq should == true if we
// reach this code). Receive from the stream to confirm the persisted data.
resp, err := w.stream.Recv()
// Retriable errors mean we should start over and attempt to
// resend the entire buffer via a new stream.
// If not retriable, falling through will return the error received
// from closing the stream.
if shouldRetry(err) {
sent = 0
finishWrite = false
offset, err = w.determineOffset(start)
if err == nil {
continue
writeOffset, err = w.determineOffset(start)
if err != nil {
return nil, 0, err
}
sent = int(writeOffset) - int(start)
// Drop the stream reference as a new one will need to be created.
w.stream = nil
continue
}
if err != nil {
return nil, 0, false, err
return nil, 0, err
}
return resp.GetResource(), offset, finalized, nil
// Confirm the persisted data if we have not finished uploading the object.
if !lastWriteOfEntireObject {
if resp.GetPersistedSize() != writeOffset {
// Retry if not all bytes were persisted.
writeOffset = resp.GetPersistedSize()
sent = int(writeOffset) - int(start)
continue
}
} else {
// If the object is done uploading, close the send stream to signal
// to the server that we are done sending so that we can receive
// from the stream without blocking.
err = w.stream.CloseSend()
if err != nil {
// CloseSend() retries the send internally. It never returns an
// error in the current implementation, but we check it anyway in
// case that it does in the future.
return nil, 0, err
}
// Stream receives do not block once send is closed, but we may not
// receive the response with the object right away; loop until we
// receive the object or error out.
var obj *storagepb.Object
for obj == nil {
resp, err := w.stream.Recv()
if err != nil {
return nil, 0, err
}
obj = resp.GetResource()
}
// Even though we received the object response, continue reading
// until we receive a non-nil error, to ensure the stream does not
// leak even if the context isn't cancelled. See:
// https://pkg.go.dev/google.golang.org/grpc#ClientConn.NewStream
for err == nil {
_, err = w.stream.Recv()
}
return obj, writeOffset, nil
}
return nil, writeOffset, nil
}
}
@ -1738,26 +1811,6 @@ func (w *gRPCWriter) determineOffset(offset int64) (int64, error) {
return offset, nil
}
// commit closes the stream to commit the data sent and potentially receive
// the finalized object if finished uploading. If the last request sent
// indicated that writing was finished, the Object will be finalized and
// returned. If not, then the Object will be nil, and the boolean returned will
// be false.
func (w *gRPCWriter) commit() (*storagepb.WriteObjectResponse, bool, error) {
finalized := true
resp, err := w.stream.CloseAndRecv()
if err == io.EOF {
// Closing a stream for a resumable upload finish_write = false results
// in an EOF which can be ignored, as we aren't done uploading yet.
finalized = false
err = nil
}
// Drop the stream reference as it has been closed.
w.stream = nil
return resp, finalized, err
}
// writeObjectSpec constructs a WriteObjectSpec proto using the Writer's
// ObjectAttrs and applies its Conditions. This is only used for gRPC.
func (w *gRPCWriter) writeObjectSpec() (*storagepb.WriteObjectSpec, error) {

View file

@ -0,0 +1,210 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go_gapic. DO NOT EDIT.
package storage
import (
storagepb "cloud.google.com/go/storage/internal/apiv2/storagepb"
"google.golang.org/api/iterator"
)
// BucketIterator manages a stream of *storagepb.Bucket.
type BucketIterator struct {
items []*storagepb.Bucket
pageInfo *iterator.PageInfo
nextFunc func() error
// Response is the raw response for the current page.
// It must be cast to the RPC response type.
// Calling Next() or InternalFetch() updates this value.
Response interface{}
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.Bucket, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *BucketIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *BucketIterator) Next() (*storagepb.Bucket, error) {
var item *storagepb.Bucket
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *BucketIterator) bufLen() int {
return len(it.items)
}
func (it *BucketIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}
// HmacKeyMetadataIterator manages a stream of *storagepb.HmacKeyMetadata.
type HmacKeyMetadataIterator struct {
items []*storagepb.HmacKeyMetadata
pageInfo *iterator.PageInfo
nextFunc func() error
// Response is the raw response for the current page.
// It must be cast to the RPC response type.
// Calling Next() or InternalFetch() updates this value.
Response interface{}
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.HmacKeyMetadata, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *HmacKeyMetadataIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *HmacKeyMetadataIterator) Next() (*storagepb.HmacKeyMetadata, error) {
var item *storagepb.HmacKeyMetadata
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *HmacKeyMetadataIterator) bufLen() int {
return len(it.items)
}
func (it *HmacKeyMetadataIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}
// NotificationConfigIterator manages a stream of *storagepb.NotificationConfig.
type NotificationConfigIterator struct {
items []*storagepb.NotificationConfig
pageInfo *iterator.PageInfo
nextFunc func() error
// Response is the raw response for the current page.
// It must be cast to the RPC response type.
// Calling Next() or InternalFetch() updates this value.
Response interface{}
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.NotificationConfig, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *NotificationConfigIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *NotificationConfigIterator) Next() (*storagepb.NotificationConfig, error) {
var item *storagepb.NotificationConfig
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *NotificationConfigIterator) bufLen() int {
return len(it.items)
}
func (it *NotificationConfigIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}
// ObjectIterator manages a stream of *storagepb.Object.
type ObjectIterator struct {
items []*storagepb.Object
pageInfo *iterator.PageInfo
nextFunc func() error
// Response is the raw response for the current page.
// It must be cast to the RPC response type.
// Calling Next() or InternalFetch() updates this value.
Response interface{}
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.Object, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *ObjectIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *ObjectIterator) Next() (*storagepb.Object, error) {
var item *storagepb.Object
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *ObjectIterator) bufLen() int {
return len(it.items)
}
func (it *ObjectIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}

View file

@ -76,15 +76,32 @@
// // TODO: Handle error.
// }
// defer c.Close()
//
// req := &storagepb.DeleteBucketRequest{
// // TODO: Fill request struct fields.
// // See https://pkg.go.dev/cloud.google.com/go/storage/internal/apiv2/storagepb#DeleteBucketRequest.
// }
// err = c.DeleteBucket(ctx, req)
// stream, err := c.BidiWriteObject(ctx)
// if err != nil {
// // TODO: Handle error.
// }
// go func() {
// reqs := []*storagepb.BidiWriteObjectRequest{
// // TODO: Create requests.
// }
// for _, req := range reqs {
// if err := stream.Send(req); err != nil {
// // TODO: Handle error.
// }
// }
// stream.CloseSend()
// }()
// for {
// resp, err := stream.Recv()
// if err == io.EOF {
// break
// }
// if err != nil {
// // TODO: handle error.
// }
// // TODO: Use resp.
// _ = resp
// }
//
// # Use of Context
//

View file

@ -1921,191 +1921,3 @@ func (c *gRPCClient) UpdateHmacKey(ctx context.Context, req *storagepb.UpdateHma
}
return resp, nil
}
// BucketIterator manages a stream of *storagepb.Bucket.
type BucketIterator struct {
items []*storagepb.Bucket
pageInfo *iterator.PageInfo
nextFunc func() error
// Response is the raw response for the current page.
// It must be cast to the RPC response type.
// Calling Next() or InternalFetch() updates this value.
Response interface{}
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.Bucket, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *BucketIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *BucketIterator) Next() (*storagepb.Bucket, error) {
var item *storagepb.Bucket
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *BucketIterator) bufLen() int {
return len(it.items)
}
func (it *BucketIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}
// HmacKeyMetadataIterator manages a stream of *storagepb.HmacKeyMetadata.
type HmacKeyMetadataIterator struct {
items []*storagepb.HmacKeyMetadata
pageInfo *iterator.PageInfo
nextFunc func() error
// Response is the raw response for the current page.
// It must be cast to the RPC response type.
// Calling Next() or InternalFetch() updates this value.
Response interface{}
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.HmacKeyMetadata, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *HmacKeyMetadataIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *HmacKeyMetadataIterator) Next() (*storagepb.HmacKeyMetadata, error) {
var item *storagepb.HmacKeyMetadata
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *HmacKeyMetadataIterator) bufLen() int {
return len(it.items)
}
func (it *HmacKeyMetadataIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}
// NotificationConfigIterator manages a stream of *storagepb.NotificationConfig.
type NotificationConfigIterator struct {
items []*storagepb.NotificationConfig
pageInfo *iterator.PageInfo
nextFunc func() error
// Response is the raw response for the current page.
// It must be cast to the RPC response type.
// Calling Next() or InternalFetch() updates this value.
Response interface{}
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.NotificationConfig, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *NotificationConfigIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *NotificationConfigIterator) Next() (*storagepb.NotificationConfig, error) {
var item *storagepb.NotificationConfig
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *NotificationConfigIterator) bufLen() int {
return len(it.items)
}
func (it *NotificationConfigIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}
// ObjectIterator manages a stream of *storagepb.Object.
type ObjectIterator struct {
items []*storagepb.Object
pageInfo *iterator.PageInfo
nextFunc func() error
// Response is the raw response for the current page.
// It must be cast to the RPC response type.
// Calling Next() or InternalFetch() updates this value.
Response interface{}
// InternalFetch is for use by the Google Cloud Libraries only.
// It is not part of the stable interface of this package.
//
// InternalFetch returns results from a single call to the underlying RPC.
// The number of results is no greater than pageSize.
// If there are no more results, nextPageToken is empty and err is nil.
InternalFetch func(pageSize int, pageToken string) (results []*storagepb.Object, nextPageToken string, err error)
}
// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *ObjectIterator) PageInfo() *iterator.PageInfo {
return it.pageInfo
}
// Next returns the next result. Its second return value is iterator.Done if there are no more
// results. Once Next returns Done, all subsequent calls will return Done.
func (it *ObjectIterator) Next() (*storagepb.Object, error) {
var item *storagepb.Object
if err := it.nextFunc(); err != nil {
return item, err
}
item = it.items[0]
it.items = it.items[1:]
return item, nil
}
func (it *ObjectIterator) bufLen() int {
return len(it.items)
}
func (it *ObjectIterator) takeBuf() interface{} {
b := it.items
it.items = nil
return b
}

View file

@ -15,4 +15,4 @@
package internal
// Version is the current tagged release of the library.
const Version = "1.34.1"
const Version = "1.35.1"

View file

@ -1,13 +1,43 @@
# Release History
## 1.9.0 (2023-11-06)
### Breaking Changes
> These changes affect only code written against previous beta versions of `v1.7.0` and `v1.8.0`
* The function `NewTokenCredential` has been removed from the `fake` package. Use a literal `&fake.TokenCredential{}` instead.
* The field `TracingNamespace` in `runtime.PipelineOptions` has been replaced by `TracingOptions`.
### Bugs Fixed
* Fixed an issue that could cause some allowed HTTP header values to not show up in logs.
* Include error text instead of error type in traces when the transport returns an error.
* Fixed an issue that could cause an HTTP/2 request to hang when the TCP connection becomes unresponsive.
* Block key and SAS authentication for non TLS protected endpoints.
* Passing a `nil` credential value will no longer cause a panic. Instead, the authentication is skipped.
* Calling `Error` on a zero-value `azcore.ResponseError` will no longer panic.
* Fixed an issue in `fake.PagerResponder[T]` that would cause a trailing error to be omitted when iterating over pages.
* Context values created by `azcore` will no longer flow across disjoint HTTP requests.
### Other Changes
* Skip generating trace info for no-op tracers.
* The `clientName` paramater in client constructors has been renamed to `moduleName`.
## 1.9.0-beta.1 (2023-10-05)
### Other Changes
* The beta features for tracing and fakes have been reinstated.
## 1.8.0 (2023-10-05)
### Features Added
* Added `Claims` and `EnableCAE` fields to `policy.TokenRequestOptions`.
* ARM bearer token policy handles CAE challenges.
* `messaging/CloudEvent` allows you to serialize/deserialize CloudEvents, as described in the CloudEvents 1.0 specification: [link](https://github.com/cloudevents/spec)
* Added functions `FetcherForNextLink` and `EncodeQueryParams` along with `FetcherForNextLinkOptions` to the `runtime` package to centralize creation of `Pager[T].Fetcher` from a next link URL.
* This includes the following features from `v1.8.0-beta.N` releases.
* Claims and CAE for authentication.
* New `messaging` package.
* Various helpers in the `runtime` package.
* Deprecation of `runtime.With*` funcs and their replacements in the `policy` package.
* Added types `KeyCredential` and `SASCredential` to the `azcore` package.
* Includes their respective constructor functions.
* Added types `KeyCredentialPolicy` and `SASCredentialPolicy` to the `azcore/runtime` package.
@ -24,11 +54,24 @@
### Other Changes
* Updated dependencies.
## 1.8.0-beta.3 (2023-09-07)
### Features Added
* Added function `FetcherForNextLink` and `FetcherForNextLinkOptions` to the `runtime` package to centralize creation of `Pager[T].Fetcher` from a next link URL.
### Bugs Fixed
* Suppress creating spans for nested SDK API calls. The HTTP span will be a child of the outer API span.
### Other Changes
* The following functions in the `runtime` package are now exposed from the `policy` package, and the `runtime` versions have been deprecated.
* `WithCaptureResponse`
* `WithHTTPHeader`
* `WithRetryOptions`
* Updated dependencies.
## 1.7.2 (2023-09-06)
@ -36,12 +79,41 @@
* Fix default HTTP transport to work in WASM modules.
## 1.8.0-beta.2 (2023-08-14)
### Features Added
* Added function `SanitizePagerPollerPath` to the `server` package to centralize sanitization and formalize the contract.
* Added `TokenRequestOptions.EnableCAE` to indicate whether to request a CAE token.
### Breaking Changes
> This change affects only code written against beta version `v1.8.0-beta.1`.
* `messaging.CloudEvent` deserializes JSON objects as `[]byte`, instead of `json.RawMessage`. See the documentation for CloudEvent.Data for more information.
> This change affects only code written against beta versions `v1.7.0-beta.2` and `v1.8.0-beta.1`.
* Removed parameter from method `Span.End()` and its type `tracing.SpanEndOptions`. This API GA'ed in `v1.2.0` so we cannot change it.
### Bugs Fixed
* Propagate any query parameters when constructing a fake poller and/or injecting next links.
## 1.7.1 (2023-08-14)
## Bugs Fixed
* Enable TLS renegotiation in the default transport policy.
## 1.8.0-beta.1 (2023-07-12)
### Features Added
- `messaging/CloudEvent` allows you to serialize/deserialize CloudEvents, as described in the CloudEvents 1.0 specification: [link](https://github.com/cloudevents/spec)
### Other Changes
* The beta features for CAE, tracing, and fakes have been reinstated.
## 1.7.0 (2023-07-12)
### Features Added

View file

@ -84,7 +84,9 @@ func IsNullValue[T any](v T) bool {
return false
}
// ClientOptions contains configuration settings for a client's pipeline.
// ClientOptions contains optional settings for a client's pipeline.
// Instances can be shared across calls to SDK client constructors when uniform configuration is desired.
// Zero-value fields will have their specified default values applied during use.
type ClientOptions = policy.ClientOptions
// Client is a basic HTTP client. It consists of a pipeline and tracing provider.
@ -93,22 +95,17 @@ type Client struct {
tr tracing.Tracer
// cached on the client to support shallow copying with new values
tp tracing.Provider
modVer string
tp tracing.Provider
modVer string
namespace string
}
// NewClient creates a new Client instance with the provided values.
// - clientName - the fully qualified name of the client ("module/package.Client"); this is used by the telemetry policy and tracing provider.
// if module and package are the same value, the "module/" prefix can be omitted.
// - moduleVersion - the semantic version of the containing module; used by the telemetry policy
// - moduleName - the fully qualified name of the module where the client is defined; used by the telemetry policy and tracing provider.
// - moduleVersion - the semantic version of the module; used by the telemetry policy and tracing provider.
// - plOpts - pipeline configuration options; can be the zero-value
// - options - optional client configurations; pass nil to accept the default values
func NewClient(clientName, moduleVersion string, plOpts runtime.PipelineOptions, options *ClientOptions) (*Client, error) {
mod, client, err := shared.ExtractModuleName(clientName)
if err != nil {
return nil, err
}
func NewClient(moduleName, moduleVersion string, plOpts runtime.PipelineOptions, options *ClientOptions) (*Client, error) {
if options == nil {
options = &ClientOptions{}
}
@ -119,15 +116,19 @@ func NewClient(clientName, moduleVersion string, plOpts runtime.PipelineOptions,
}
}
pl := runtime.NewPipeline(mod, moduleVersion, plOpts, options)
pl := runtime.NewPipeline(moduleName, moduleVersion, plOpts, options)
tr := options.TracingProvider.NewTracer(client, moduleVersion)
tr := options.TracingProvider.NewTracer(moduleName, moduleVersion)
if tr.Enabled() && plOpts.Tracing.Namespace != "" {
tr.SetAttributes(tracing.Attribute{Key: shared.TracingNamespaceAttrName, Value: plOpts.Tracing.Namespace})
}
return &Client{
pl: pl,
tr: tr,
tp: options.TracingProvider,
modVer: moduleVersion,
pl: pl,
tr: tr,
tp: options.TracingProvider,
modVer: moduleVersion,
namespace: plOpts.Tracing.Namespace,
}, nil
}
@ -146,5 +147,8 @@ func (c *Client) Tracer() tracing.Tracer {
// - clientName - the fully qualified name of the client ("package.Client"); this is used by the tracing provider when creating spans
func (c *Client) WithClientName(clientName string) *Client {
tr := c.tp.NewTracer(clientName, c.modVer)
return &Client{pl: c.pl, tr: tr, tp: c.tp, modVer: c.modVer}
if tr.Enabled() && c.namespace != "" {
tr.SetAttributes(tracing.Attribute{Key: shared.TracingNamespaceAttrName, Value: c.namespace})
}
return &Client{pl: c.pl, tr: tr, tp: c.tp, modVer: c.modVer, namespace: c.namespace}
}

View file

@ -253,5 +253,12 @@ When resuming a poller, no IO is performed, and zero-value arguments can be used
Resume tokens are unique per service client and operation. Attempting to resume a poller for LRO BeginB() with a token from LRO
BeginA() will result in an error.
# Fakes
The fake package contains types used for constructing in-memory fake servers used in unit tests.
This allows writing tests to cover various success/error conditions without the need for connecting to a live service.
Please see https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/samples/fakes for details and examples on how to use fakes.
*/
package azcore

View file

@ -8,6 +8,8 @@ package exported
import (
"context"
"encoding/base64"
"fmt"
"io"
"net/http"
"sync/atomic"
@ -78,6 +80,38 @@ type TokenCredential interface {
GetToken(ctx context.Context, options TokenRequestOptions) (AccessToken, error)
}
// DecodeByteArray will base-64 decode the provided string into v.
// Exported as runtime.DecodeByteArray()
func DecodeByteArray(s string, v *[]byte, format Base64Encoding) error {
if len(s) == 0 {
return nil
}
payload := string(s)
if payload[0] == '"' {
// remove surrounding quotes
payload = payload[1 : len(payload)-1]
}
switch format {
case Base64StdFormat:
decoded, err := base64.StdEncoding.DecodeString(payload)
if err == nil {
*v = decoded
return nil
}
return err
case Base64URLFormat:
// use raw encoding as URL format should not contain any '=' characters
decoded, err := base64.RawURLEncoding.DecodeString(payload)
if err == nil {
*v = decoded
return nil
}
return err
default:
return fmt.Errorf("unrecognized byte array format: %d", format)
}
}
// KeyCredential contains an authentication key used to authenticate to an Azure service.
// Exported as azcore.KeyCredential.
type KeyCredential struct {

View file

@ -8,10 +8,7 @@ package exported
import (
"errors"
"fmt"
"net/http"
"golang.org/x/net/http/httpguts"
)
// Policy represents an extensibility point for the Pipeline that can mutate the specified
@ -75,23 +72,6 @@ func (p Pipeline) Do(req *Request) (*http.Response, error) {
if req == nil {
return nil, errors.New("request cannot be nil")
}
// check copied from Transport.roundTrip()
for k, vv := range req.Raw().Header {
if !httpguts.ValidHeaderFieldName(k) {
if req.Raw().Body != nil {
req.Raw().Body.Close()
}
return nil, fmt.Errorf("invalid header field name %q", k)
}
for _, v := range vv {
if !httpguts.ValidHeaderFieldValue(v) {
if req.Raw().Body != nil {
req.Raw().Body.Close()
}
return nil, fmt.Errorf("invalid header field value %q for key %v", v, k)
}
}
}
req.policies = p.policies
return req.Next()
}

View file

@ -8,6 +8,7 @@ package exported
import (
"context"
"encoding/base64"
"errors"
"fmt"
"io"
@ -18,6 +19,28 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
)
// Base64Encoding is usesd to specify which base-64 encoder/decoder to use when
// encoding/decoding a slice of bytes to/from a string.
// Exported as runtime.Base64Encoding
type Base64Encoding int
const (
// Base64StdFormat uses base64.StdEncoding for encoding and decoding payloads.
Base64StdFormat Base64Encoding = 0
// Base64URLFormat uses base64.RawURLEncoding for encoding and decoding payloads.
Base64URLFormat Base64Encoding = 1
)
// EncodeByteArray will base-64 encode the byte slice v.
// Exported as runtime.EncodeByteArray()
func EncodeByteArray(v []byte, format Base64Encoding) string {
if format == Base64URLFormat {
return base64.RawURLEncoding.EncodeToString(v)
}
return base64.StdEncoding.EncodeToString(v)
}
// Request is an abstraction over the creation of an HTTP request as it passes through the pipeline.
// Don't use this type directly, use NewRequest() instead.
// Exported as policy.Request.
@ -170,6 +193,14 @@ func (req *Request) Clone(ctx context.Context) *Request {
return &r2
}
// WithContext returns a shallow copy of the request with its context changed to ctx.
func (req *Request) WithContext(ctx context.Context) *Request {
r2 := new(Request)
*r2 = *req
r2.req = r2.req.WithContext(ctx)
return r2
}
// not exported but dependent on Request
// PolicyFunc is a type that implements the Policy interface.

View file

@ -13,6 +13,7 @@ import (
"net/http"
"regexp"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
"github.com/Azure/azure-sdk-for-go/sdk/internal/exported"
)
@ -25,7 +26,7 @@ func NewResponseError(resp *http.Response) error {
}
// prefer the error code in the response header
if ec := resp.Header.Get("x-ms-error-code"); ec != "" {
if ec := resp.Header.Get(shared.HeaderXMSErrorCode); ec != "" {
respErr.ErrorCode = ec
return respErr
}
@ -112,33 +113,45 @@ type ResponseError struct {
// Error implements the error interface for type ResponseError.
// Note that the message contents are not contractual and can change over time.
func (e *ResponseError) Error() string {
const separator = "--------------------------------------------------------------------------------"
// write the request method and URL with response status code
msg := &bytes.Buffer{}
fmt.Fprintf(msg, "%s %s://%s%s\n", e.RawResponse.Request.Method, e.RawResponse.Request.URL.Scheme, e.RawResponse.Request.URL.Host, e.RawResponse.Request.URL.Path)
fmt.Fprintln(msg, "--------------------------------------------------------------------------------")
fmt.Fprintf(msg, "RESPONSE %d: %s\n", e.RawResponse.StatusCode, e.RawResponse.Status)
if e.RawResponse != nil {
if e.RawResponse.Request != nil {
fmt.Fprintf(msg, "%s %s://%s%s\n", e.RawResponse.Request.Method, e.RawResponse.Request.URL.Scheme, e.RawResponse.Request.URL.Host, e.RawResponse.Request.URL.Path)
} else {
fmt.Fprintln(msg, "Request information not available")
}
fmt.Fprintln(msg, separator)
fmt.Fprintf(msg, "RESPONSE %d: %s\n", e.RawResponse.StatusCode, e.RawResponse.Status)
} else {
fmt.Fprintln(msg, "Missing RawResponse")
fmt.Fprintln(msg, separator)
}
if e.ErrorCode != "" {
fmt.Fprintf(msg, "ERROR CODE: %s\n", e.ErrorCode)
} else {
fmt.Fprintln(msg, "ERROR CODE UNAVAILABLE")
}
fmt.Fprintln(msg, "--------------------------------------------------------------------------------")
body, err := exported.Payload(e.RawResponse, nil)
if err != nil {
// this really shouldn't fail at this point as the response
// body is already cached (it was read in NewResponseError)
fmt.Fprintf(msg, "Error reading response body: %v", err)
} else if len(body) > 0 {
if err := json.Indent(msg, body, "", " "); err != nil {
// failed to pretty-print so just dump it verbatim
fmt.Fprint(msg, string(body))
if e.RawResponse != nil {
fmt.Fprintln(msg, separator)
body, err := exported.Payload(e.RawResponse, nil)
if err != nil {
// this really shouldn't fail at this point as the response
// body is already cached (it was read in NewResponseError)
fmt.Fprintf(msg, "Error reading response body: %v", err)
} else if len(body) > 0 {
if err := json.Indent(msg, body, "", " "); err != nil {
// failed to pretty-print so just dump it verbatim
fmt.Fprint(msg, string(body))
}
// the standard library doesn't have a pretty-printer for XML
fmt.Fprintln(msg)
} else {
fmt.Fprintln(msg, "Response contained no body")
}
// the standard library doesn't have a pretty-printer for XML
fmt.Fprintln(msg)
} else {
fmt.Fprintln(msg, "Response contained no body")
}
fmt.Fprintln(msg, "--------------------------------------------------------------------------------")
fmt.Fprintln(msg, separator)
return msg.String()
}

View file

@ -0,0 +1,133 @@
//go:build go1.18
// +build go1.18
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package fake
import (
"context"
"errors"
"fmt"
"net/http"
"strings"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/log"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
"github.com/Azure/azure-sdk-for-go/sdk/internal/poller"
)
// Applicable returns true if the LRO is a fake.
func Applicable(resp *http.Response) bool {
return resp.Header.Get(shared.HeaderFakePollerStatus) != ""
}
// CanResume returns true if the token can rehydrate this poller type.
func CanResume(token map[string]interface{}) bool {
_, ok := token["fakeURL"]
return ok
}
// Poller is an LRO poller that uses the Core-Fake-Poller pattern.
type Poller[T any] struct {
pl exported.Pipeline
resp *http.Response
// The API name from CtxAPINameKey
APIName string `json:"apiName"`
// The URL from Core-Fake-Poller header.
FakeURL string `json:"fakeURL"`
// The LRO's current state.
FakeStatus string `json:"status"`
}
// lroStatusURLSuffix is the URL path suffix for a faked LRO.
const lroStatusURLSuffix = "/get/fake/status"
// New creates a new Poller from the provided initial response.
// Pass nil for response to create an empty Poller for rehydration.
func New[T any](pl exported.Pipeline, resp *http.Response) (*Poller[T], error) {
if resp == nil {
log.Write(log.EventLRO, "Resuming Core-Fake-Poller poller.")
return &Poller[T]{pl: pl}, nil
}
log.Write(log.EventLRO, "Using Core-Fake-Poller poller.")
fakeStatus := resp.Header.Get(shared.HeaderFakePollerStatus)
if fakeStatus == "" {
return nil, errors.New("response is missing Fake-Poller-Status header")
}
ctxVal := resp.Request.Context().Value(shared.CtxAPINameKey{})
if ctxVal == nil {
return nil, errors.New("missing value for CtxAPINameKey")
}
apiName, ok := ctxVal.(string)
if !ok {
return nil, fmt.Errorf("expected string for CtxAPINameKey, the type was %T", ctxVal)
}
qp := ""
if resp.Request.URL.RawQuery != "" {
qp = "?" + resp.Request.URL.RawQuery
}
p := &Poller[T]{
pl: pl,
resp: resp,
APIName: apiName,
// NOTE: any changes to this path format MUST be reflected in SanitizePollerPath()
FakeURL: fmt.Sprintf("%s://%s%s%s%s", resp.Request.URL.Scheme, resp.Request.URL.Host, resp.Request.URL.Path, lroStatusURLSuffix, qp),
FakeStatus: fakeStatus,
}
return p, nil
}
// Done returns true if the LRO is in a terminal state.
func (p *Poller[T]) Done() bool {
return poller.IsTerminalState(p.FakeStatus)
}
// Poll retrieves the current state of the LRO.
func (p *Poller[T]) Poll(ctx context.Context) (*http.Response, error) {
ctx = context.WithValue(ctx, shared.CtxAPINameKey{}, p.APIName)
err := pollers.PollHelper(ctx, p.FakeURL, p.pl, func(resp *http.Response) (string, error) {
if !poller.StatusCodeValid(resp) {
p.resp = resp
return "", exported.NewResponseError(resp)
}
fakeStatus := resp.Header.Get(shared.HeaderFakePollerStatus)
if fakeStatus == "" {
return "", errors.New("response is missing Fake-Poller-Status header")
}
p.resp = resp
p.FakeStatus = fakeStatus
return p.FakeStatus, nil
})
if err != nil {
return nil, err
}
return p.resp, nil
}
func (p *Poller[T]) Result(ctx context.Context, out *T) error {
if p.resp.StatusCode == http.StatusNoContent {
return nil
} else if poller.Failed(p.FakeStatus) {
return exported.NewResponseError(p.resp)
}
return pollers.ResultHelper(p.resp, poller.Failed(p.FakeStatus), out)
}
// SanitizePollerPath removes any fake-appended suffix from a URL's path.
func SanitizePollerPath(path string) string {
return strings.TrimSuffix(path, lroStatusURLSuffix)
}

View file

@ -7,8 +7,9 @@
package shared
const (
ContentTypeAppJSON = "application/json"
ContentTypeAppXML = "application/xml"
ContentTypeAppJSON = "application/json"
ContentTypeAppXML = "application/xml"
ContentTypeTextPlain = "text/plain"
)
const (
@ -17,20 +18,25 @@ const (
HeaderAzureAsync = "Azure-AsyncOperation"
HeaderContentLength = "Content-Length"
HeaderContentType = "Content-Type"
HeaderFakePollerStatus = "Fake-Poller-Status"
HeaderLocation = "Location"
HeaderOperationLocation = "Operation-Location"
HeaderRetryAfter = "Retry-After"
HeaderUserAgent = "User-Agent"
HeaderWWWAuthenticate = "WWW-Authenticate"
HeaderXMSClientRequestID = "x-ms-client-request-id"
HeaderXMSRequestID = "x-ms-request-id"
HeaderXMSErrorCode = "x-ms-error-code"
)
const BearerTokenPrefix = "Bearer "
const TracingNamespaceAttrName = "az.namespace"
const (
// Module is the name of the calling module used in telemetry data.
Module = "azcore"
// Version is the semantic version (see http://semver.org) of this module.
Version = "v1.8.0"
Version = "v1.9.0"
)

View file

@ -14,10 +14,11 @@ import (
"regexp"
"strconv"
"time"
"github.com/Azure/azure-sdk-for-go/sdk/internal/errorinfo"
)
// NOTE: when adding a new context key type, it likely needs to be
// added to the deny-list of key types in ContextWithDeniedValues
// CtxWithHTTPHeaderKey is used as a context key for adding/retrieving http.Header.
type CtxWithHTTPHeaderKey struct{}
@ -27,6 +28,12 @@ type CtxWithRetryOptionsKey struct{}
// CtxWithCaptureResponse is used as a context key for retrieving the raw response.
type CtxWithCaptureResponse struct{}
// CtxWithTracingTracer is used as a context key for adding/retrieving tracing.Tracer.
type CtxWithTracingTracer struct{}
// CtxAPINameKey is used as a context key for adding/retrieving the API name.
type CtxAPINameKey struct{}
// Delay waits for the duration to elapse or the context to be cancelled.
func Delay(ctx context.Context, delay time.Duration) error {
select {
@ -80,49 +87,21 @@ func ValidateModVer(moduleVersion string) error {
return nil
}
// ExtractModuleName returns "module", "package.Client" from "module/package.Client" or
// "package", "package.Client" from "package.Client" when there's no "module/" prefix.
// If clientName is malformed, an error is returned.
func ExtractModuleName(clientName string) (string, string, error) {
// uses unnamed capturing for "module", "package.Client", and "package"
regex, err := regexp.Compile(`^(?:([a-z0-9]+)/)?(([a-z0-9]+)\.(?:[A-Za-z0-9]+))$`)
if err != nil {
return "", "", err
// ContextWithDeniedValues wraps an existing [context.Context], denying access to certain context values.
// Pipeline policies that create new requests to be sent down their own pipeline MUST wrap the caller's
// context with an instance of this type. This is to prevent context values from flowing across disjoint
// requests which can have unintended side-effects.
type ContextWithDeniedValues struct {
context.Context
}
// Value implements part of the [context.Context] interface.
// It acts as a deny-list for certain context keys.
func (c *ContextWithDeniedValues) Value(key any) any {
switch key.(type) {
case CtxAPINameKey, CtxWithCaptureResponse, CtxWithHTTPHeaderKey, CtxWithRetryOptionsKey, CtxWithTracingTracer:
return nil
default:
return c.Context.Value(key)
}
matches := regex.FindStringSubmatch(clientName)
if len(matches) < 4 {
return "", "", fmt.Errorf("malformed clientName %s", clientName)
}
// the first match is the entire string, the second is "module", the third is
// "package.Client" and the fourth is "package".
// if there was no "module/" prefix, the second match will be the empty string
if matches[1] != "" {
return matches[1], matches[2], nil
}
return matches[3], matches[2], nil
}
// NonRetriableError marks the specified error as non-retriable.
func NonRetriableError(err error) error {
return &nonRetriableError{err}
}
type nonRetriableError struct {
error
}
func (p *nonRetriableError) Error() string {
return p.error.Error()
}
func (*nonRetriableError) NonRetriable() {
// marker method
}
func (p *nonRetriableError) Unwrap() error {
return p.error
}
var _ errorinfo.NonRetriable = (*nonRetriableError)(nil)

View file

@ -29,9 +29,11 @@ type Transporter = exported.Transporter
type Request = exported.Request
// ClientOptions contains optional settings for a client's pipeline.
// All zero-value fields will be initialized with default values.
// Instances can be shared across calls to SDK client constructors when uniform configuration is desired.
// Zero-value fields will have their specified default values applied during use.
type ClientOptions struct {
// APIVersion overrides the default version requested of the service. Set with caution as this package version has not been tested with arbitrary service versions.
// APIVersion overrides the default version requested of the service.
// Set with caution as this package version has not been tested with arbitrary service versions.
APIVersion string
// Cloud specifies a cloud for the client. The default is Azure Public Cloud.

View file

@ -10,9 +10,12 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"reflect"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing"
)
// PagingHandler contains the required data for constructing a Pager.
@ -23,12 +26,16 @@ type PagingHandler[T any] struct {
// Fetcher fetches the first and subsequent pages.
Fetcher func(context.Context, *T) (T, error)
// Tracer contains the Tracer from the client that's creating the Pager.
Tracer tracing.Tracer
}
// Pager provides operations for iterating over paged responses.
type Pager[T any] struct {
current *T
handler PagingHandler[T]
tracer tracing.Tracer
firstPage bool
}
@ -37,6 +44,7 @@ type Pager[T any] struct {
func NewPager[T any](handler PagingHandler[T]) *Pager[T] {
return &Pager[T]{
handler: handler,
tracer: handler.Tracer,
firstPage: true,
}
}
@ -51,8 +59,6 @@ func (p *Pager[T]) More() bool {
// NextPage advances the pager to the next page.
func (p *Pager[T]) NextPage(ctx context.Context) (T, error) {
var resp T
var err error
if p.current != nil {
if p.firstPage {
// we get here if it's an LRO-pager, we already have the first page
@ -61,12 +67,16 @@ func (p *Pager[T]) NextPage(ctx context.Context) (T, error) {
} else if !p.handler.More(*p.current) {
return *new(T), errors.New("no more pages")
}
resp, err = p.handler.Fetcher(ctx, p.current)
} else {
// non-LRO case, first page
p.firstPage = false
resp, err = p.handler.Fetcher(ctx, nil)
}
var err error
ctx, endSpan := StartSpan(ctx, fmt.Sprintf("%s.NextPage", shortenTypeName(reflect.TypeOf(*p).Name())), p.tracer, nil)
defer func() { endSpan(err) }()
resp, err := p.handler.Fetcher(ctx, p.current)
if err != nil {
return *new(T), err
}

View file

@ -13,9 +13,35 @@ import (
// PipelineOptions contains Pipeline options for SDK developers
type PipelineOptions struct {
AllowedHeaders, AllowedQueryParameters []string
APIVersion APIVersionOptions
PerCall, PerRetry []policy.Policy
// AllowedHeaders is the slice of headers to log with their values intact.
// All headers not in the slice will have their values REDACTED.
// Applies to request and response headers.
AllowedHeaders []string
// AllowedQueryParameters is the slice of query parameters to log with their values intact.
// All query parameters not in the slice will have their values REDACTED.
AllowedQueryParameters []string
// APIVersion overrides the default version requested of the service.
// Set with caution as this package version has not been tested with arbitrary service versions.
APIVersion APIVersionOptions
// PerCall contains custom policies to inject into the pipeline.
// Each policy is executed once per request.
PerCall []policy.Policy
// PerRetry contains custom policies to inject into the pipeline.
// Each policy is executed once per request, and for each retry of that request.
PerRetry []policy.Policy
// Tracing contains options used to configure distributed tracing.
Tracing TracingOptions
}
// TracingOptions contains tracing options for SDK developers.
type TracingOptions struct {
// Namespace contains the value to use for the az.namespace span attribute.
Namespace string
}
// Pipeline represents a primitive for sending HTTP requests and receiving responses.
@ -56,8 +82,10 @@ func NewPipeline(module, version string, plOpts PipelineOptions, options *policy
policies = append(policies, NewRetryPolicy(&cp.Retry))
policies = append(policies, plOpts.PerRetry...)
policies = append(policies, cp.PerRetryPolicies...)
policies = append(policies, exported.PolicyFunc(httpHeaderPolicy))
policies = append(policies, newHTTPTracePolicy(cp.Logging.AllowedQueryParams))
policies = append(policies, NewLogPolicy(&cp.Logging))
policies = append(policies, exported.PolicyFunc(httpHeaderPolicy), exported.PolicyFunc(bodyDownloadPolicy))
policies = append(policies, exported.PolicyFunc(bodyDownloadPolicy))
transport := cp.Transport
if transport == nil {
transport = defaultHTTPClient

View file

@ -35,7 +35,7 @@ type acquiringResourceState struct {
// acquire acquires or updates the resource; only one
// thread/goroutine at a time ever calls this function
func acquire(state acquiringResourceState) (newResource exported.AccessToken, newExpiration time.Time, err error) {
tk, err := state.p.cred.GetToken(state.req.Raw().Context(), state.tro)
tk, err := state.p.cred.GetToken(&shared.ContextWithDeniedValues{Context: state.req.Raw().Context()}, state.tro)
if err != nil {
return exported.AccessToken{}, time.Time{}, err
}
@ -73,9 +73,17 @@ func (b *BearerTokenPolicy) authenticateAndAuthorize(req *policy.Request) func(p
// Do authorizes a request with a bearer token
func (b *BearerTokenPolicy) Do(req *policy.Request) (*http.Response, error) {
if strings.ToLower(req.Raw().URL.Scheme) != "https" {
return nil, shared.NonRetriableError(errors.New("bearer token authentication is not permitted for non TLS protected (https) endpoints"))
// skip adding the authorization header if no TokenCredential was provided.
// this prevents a panic that might be hard to diagnose and allows testing
// against http endpoints that don't require authentication.
if b.cred == nil {
return req.Next()
}
if err := checkHTTPSForAuth(req); err != nil {
return nil, err
}
var err error
if b.authzHandler.OnRequest != nil {
err = b.authzHandler.OnRequest(req, b.authenticateAndAuthorize(req))
@ -83,7 +91,7 @@ func (b *BearerTokenPolicy) Do(req *policy.Request) (*http.Response, error) {
err = b.authenticateAndAuthorize(req)(policy.TokenRequestOptions{Scopes: b.scopes})
}
if err != nil {
return nil, ensureNonRetriable(err)
return nil, errorinfo.NonRetriableError(err)
}
res, err := req.Next()
@ -99,22 +107,15 @@ func (b *BearerTokenPolicy) Do(req *policy.Request) (*http.Response, error) {
}
}
}
return res, ensureNonRetriable(err)
}
func ensureNonRetriable(err error) error {
var nre errorinfo.NonRetriable
if err != nil && !errors.As(err, &nre) {
err = btpError{err}
if err != nil {
err = errorinfo.NonRetriableError(err)
}
return err
return res, err
}
// btpError is a wrapper that ensures RetryPolicy doesn't retry requests BearerTokenPolicy couldn't authorize
type btpError struct {
error
func checkHTTPSForAuth(req *policy.Request) error {
if strings.ToLower(req.Raw().URL.Scheme) != "https" {
return errorinfo.NonRetriableError(errors.New("authenticated requests are not permitted for non TLS protected (https) endpoints"))
}
return nil
}
func (btpError) NonRetriable() {}
var _ errorinfo.NonRetriable = (*btpError)(nil)

View file

@ -0,0 +1,143 @@
//go:build go1.18
// +build go1.18
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package runtime
import (
"context"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing"
)
const (
attrHTTPMethod = "http.method"
attrHTTPURL = "http.url"
attrHTTPUserAgent = "http.user_agent"
attrHTTPStatusCode = "http.status_code"
attrAZClientReqID = "az.client_request_id"
attrAZServiceReqID = "az.service_request_id"
attrNetPeerName = "net.peer.name"
)
// newHTTPTracePolicy creates a new instance of the httpTracePolicy.
// - allowedQueryParams contains the user-specified query parameters that don't need to be redacted from the trace
func newHTTPTracePolicy(allowedQueryParams []string) exported.Policy {
return &httpTracePolicy{allowedQP: getAllowedQueryParams(allowedQueryParams)}
}
// httpTracePolicy is a policy that creates a trace for the HTTP request and its response
type httpTracePolicy struct {
allowedQP map[string]struct{}
}
// Do implements the pipeline.Policy interfaces for the httpTracePolicy type.
func (h *httpTracePolicy) Do(req *policy.Request) (resp *http.Response, err error) {
rawTracer := req.Raw().Context().Value(shared.CtxWithTracingTracer{})
if tracer, ok := rawTracer.(tracing.Tracer); ok && tracer.Enabled() {
attributes := []tracing.Attribute{
{Key: attrHTTPMethod, Value: req.Raw().Method},
{Key: attrHTTPURL, Value: getSanitizedURL(*req.Raw().URL, h.allowedQP)},
{Key: attrNetPeerName, Value: req.Raw().URL.Host},
}
if ua := req.Raw().Header.Get(shared.HeaderUserAgent); ua != "" {
attributes = append(attributes, tracing.Attribute{Key: attrHTTPUserAgent, Value: ua})
}
if reqID := req.Raw().Header.Get(shared.HeaderXMSClientRequestID); reqID != "" {
attributes = append(attributes, tracing.Attribute{Key: attrAZClientReqID, Value: reqID})
}
ctx := req.Raw().Context()
ctx, span := tracer.Start(ctx, "HTTP "+req.Raw().Method, &tracing.SpanOptions{
Kind: tracing.SpanKindClient,
Attributes: attributes,
})
defer func() {
if resp != nil {
span.SetAttributes(tracing.Attribute{Key: attrHTTPStatusCode, Value: resp.StatusCode})
if resp.StatusCode > 399 {
span.SetStatus(tracing.SpanStatusError, resp.Status)
}
if reqID := resp.Header.Get(shared.HeaderXMSRequestID); reqID != "" {
span.SetAttributes(tracing.Attribute{Key: attrAZServiceReqID, Value: reqID})
}
} else if err != nil {
var urlErr *url.Error
if errors.As(err, &urlErr) {
// calling *url.Error.Error() will include the unsanitized URL
// which we don't want. in addition, we already have the HTTP verb
// and sanitized URL in the trace so we aren't losing any info
err = urlErr.Err
}
span.SetStatus(tracing.SpanStatusError, err.Error())
}
span.End()
}()
req = req.WithContext(ctx)
}
resp, err = req.Next()
return
}
// StartSpanOptions contains the optional values for StartSpan.
type StartSpanOptions struct {
// for future expansion
}
// StartSpan starts a new tracing span.
// You must call the returned func to terminate the span. Pass the applicable error
// if the span will exit with an error condition.
// - ctx is the parent context of the newly created context
// - name is the name of the span. this is typically the fully qualified name of an API ("Client.Method")
// - tracer is the client's Tracer for creating spans
// - options contains optional values. pass nil to accept any default values
func StartSpan(ctx context.Context, name string, tracer tracing.Tracer, options *StartSpanOptions) (context.Context, func(error)) {
if !tracer.Enabled() {
return ctx, func(err error) {}
}
// we MUST propagate the active tracer before returning so that the trace policy can access it
ctx = context.WithValue(ctx, shared.CtxWithTracingTracer{}, tracer)
const newSpanKind = tracing.SpanKindInternal
if activeSpan := ctx.Value(ctxActiveSpan{}); activeSpan != nil {
// per the design guidelines, if a SDK method Foo() calls SDK method Bar(),
// then the span for Bar() must be suppressed. however, if Bar() makes a REST
// call, then Bar's HTTP span must be a child of Foo's span.
// however, there is an exception to this rule. if the SDK method Foo() is a
// messaging producer/consumer, and it takes a callback that's a SDK method
// Bar(), then the span for Bar() must _not_ be suppressed.
if kind := activeSpan.(tracing.SpanKind); kind == tracing.SpanKindClient || kind == tracing.SpanKindInternal {
return ctx, func(err error) {}
}
}
ctx, span := tracer.Start(ctx, name, &tracing.SpanOptions{
Kind: newSpanKind,
})
ctx = context.WithValue(ctx, ctxActiveSpan{}, newSpanKind)
return ctx, func(err error) {
if err != nil {
errType := strings.Replace(fmt.Sprintf("%T", err), "*exported.", "*azcore.", 1)
span.SetStatus(tracing.SpanStatusError, fmt.Sprintf("%s:\n%s", errType, err.Error()))
}
span.End()
}
}
// ctxActiveSpan is used as a context key for indicating a SDK client span is in progress.
type ctxActiveSpan struct{}

View file

@ -40,10 +40,18 @@ func NewKeyCredentialPolicy(cred *exported.KeyCredential, header string, options
// Do implementes the Do method on the [policy.Polilcy] interface.
func (k *KeyCredentialPolicy) Do(req *policy.Request) (*http.Response, error) {
val := exported.KeyCredentialGet(k.cred)
if k.prefix != "" {
val = k.prefix + val
// skip adding the authorization header if no KeyCredential was provided.
// this prevents a panic that might be hard to diagnose and allows testing
// against http endpoints that don't require authentication.
if k.cred != nil {
if err := checkHTTPSForAuth(req); err != nil {
return nil, err
}
val := exported.KeyCredentialGet(k.cred)
if k.prefix != "" {
val = k.prefix + val
}
req.Raw().Header.Add(k.header, val)
}
req.Raw().Header.Add(k.header, val)
return req.Next()
}

View file

@ -191,7 +191,8 @@ func (p *logPolicy) writeHeader(b *bytes.Buffer, header http.Header) {
}
sort.Strings(keys)
for _, k := range keys {
value := header.Get(k)
// don't use Get() as it will canonicalize k which might cause a mismatch
value := header[k][0]
// redact all header values not in the allow-list
if _, ok := p.allowedHeaders[strings.ToLower(k)]; !ok {
value = redactedValue

View file

@ -59,15 +59,7 @@ func setDefaults(o *policy.RetryOptions) {
}
func calcDelay(o policy.RetryOptions, try int32) time.Duration { // try is >=1; never 0
pow := func(number int64, exponent int32) int64 { // pow is nested helper function
var result int64 = 1
for n := int32(0); n < exponent; n++ {
result *= number
}
return result
}
delay := time.Duration(pow(2, try)-1) * o.RetryDelay
delay := time.Duration((1<<try)-1) * o.RetryDelay
// Introduce some jitter: [0.0, 1.0) / 2 = [0.0, 0.5) + 0.8 = [0.8, 1.3)
delay = time.Duration(delay.Seconds() * (rand.Float64()/2 + 0.8) * float64(time.Second)) // NOTE: We want math/rand; not crypto/rand

View file

@ -34,6 +34,14 @@ func NewSASCredentialPolicy(cred *exported.SASCredential, header string, options
// Do implementes the Do method on the [policy.Polilcy] interface.
func (k *SASCredentialPolicy) Do(req *policy.Request) (*http.Response, error) {
req.Raw().Header.Add(k.header, exported.SASCredentialGet(k.cred))
// skip adding the authorization header if no SASCredential was provided.
// this prevents a panic that might be hard to diagnose and allows testing
// against http endpoints that don't require authentication.
if k.cred != nil {
if err := checkHTTPSForAuth(req); err != nil {
return nil, err
}
req.Raw().Header.Add(k.header, exported.SASCredentialGet(k.cred))
}
return req.Next()
}

View file

@ -43,6 +43,10 @@ func NewTelemetryPolicy(mod, ver string, o *policy.TelemetryOptions) policy.Poli
b.WriteString(o.ApplicationID)
b.WriteRune(' ')
}
// mod might be the fully qualified name. in that case, we just want the package name
if i := strings.LastIndex(mod, "/"); i > -1 {
mod = mod[i+1:]
}
b.WriteString(formatTelemetry(mod, ver))
b.WriteRune(' ')
b.WriteString(platformInfo)

View file

@ -13,6 +13,8 @@ import (
"flag"
"fmt"
"net/http"
"reflect"
"strings"
"time"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
@ -20,9 +22,11 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/async"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/body"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/fake"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/loc"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/pollers/op"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing"
"github.com/Azure/azure-sdk-for-go/sdk/internal/poller"
)
@ -54,6 +58,9 @@ type NewPollerOptions[T any] struct {
// Handler[T] contains a custom polling implementation.
Handler PollingHandler[T]
// Tracer contains the Tracer from the client that's creating the Poller.
Tracer tracing.Tracer
}
// NewPoller creates a Poller based on the provided initial response.
@ -70,6 +77,7 @@ func NewPoller[T any](resp *http.Response, pl exported.Pipeline, options *NewPol
op: options.Handler,
resp: resp,
result: result,
tracer: options.Tracer,
}, nil
}
@ -83,7 +91,9 @@ func NewPoller[T any](resp *http.Response, pl exported.Pipeline, options *NewPol
// determine the polling method
var opr PollingHandler[T]
var err error
if async.Applicable(resp) {
if fake.Applicable(resp) {
opr, err = fake.New[T](pl, resp)
} else if async.Applicable(resp) {
// async poller must be checked first as it can also have a location header
opr, err = async.New[T](pl, resp, options.FinalStateVia)
} else if op.Applicable(resp) {
@ -110,6 +120,7 @@ func NewPoller[T any](resp *http.Response, pl exported.Pipeline, options *NewPol
op: opr,
resp: resp,
result: result,
tracer: options.Tracer,
}, nil
}
@ -121,6 +132,9 @@ type NewPollerFromResumeTokenOptions[T any] struct {
// Handler[T] contains a custom polling implementation.
Handler PollingHandler[T]
// Tracer contains the Tracer from the client that's creating the Poller.
Tracer tracing.Tracer
}
// NewPollerFromResumeToken creates a Poller from a resume token string.
@ -147,7 +161,9 @@ func NewPollerFromResumeToken[T any](token string, pl exported.Pipeline, options
opr := options.Handler
// now rehydrate the poller based on the encoded poller type
if opr != nil {
if fake.CanResume(asJSON) {
opr, _ = fake.New[T](pl, nil)
} else if opr != nil {
log.Writef(log.EventLRO, "Resuming custom poller %T.", opr)
} else if async.CanResume(asJSON) {
opr, _ = async.New[T](pl, nil, "")
@ -166,6 +182,7 @@ func NewPollerFromResumeToken[T any](token string, pl exported.Pipeline, options
return &Poller[T]{
op: opr,
result: result,
tracer: options.Tracer,
}, nil
}
@ -188,6 +205,7 @@ type Poller[T any] struct {
resp *http.Response
err error
result *T
tracer tracing.Tracer
done bool
}
@ -203,7 +221,7 @@ type PollUntilDoneOptions struct {
// options: pass nil to accept the default values.
// NOTE: the default polling frequency is 30 seconds which works well for most operations. However, some operations might
// benefit from a shorter or longer duration.
func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOptions) (T, error) {
func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOptions) (res T, err error) {
if options == nil {
options = &PollUntilDoneOptions{}
}
@ -212,9 +230,13 @@ func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOpt
cp.Frequency = 30 * time.Second
}
ctx, endSpan := StartSpan(ctx, fmt.Sprintf("%s.PollUntilDone", shortenTypeName(reflect.TypeOf(*p).Name())), p.tracer, nil)
defer func() { endSpan(err) }()
// skip the floor check when executing tests so they don't take so long
if isTest := flag.Lookup("test.v"); isTest == nil && cp.Frequency < time.Second {
return *new(T), errors.New("polling frequency minimum is one second")
err = errors.New("polling frequency minimum is one second")
return
}
start := time.Now()
@ -226,22 +248,24 @@ func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOpt
// initial check for a retry-after header existing on the initial response
if retryAfter := shared.RetryAfter(p.resp); retryAfter > 0 {
log.Writef(log.EventLRO, "initial Retry-After delay for %s", retryAfter.String())
if err := shared.Delay(ctx, retryAfter); err != nil {
if err = shared.Delay(ctx, retryAfter); err != nil {
logPollUntilDoneExit(err)
return *new(T), err
return
}
}
}
// begin polling the endpoint until a terminal state is reached
for {
resp, err := p.Poll(ctx)
var resp *http.Response
resp, err = p.Poll(ctx)
if err != nil {
logPollUntilDoneExit(err)
return *new(T), err
return
}
if p.Done() {
logPollUntilDoneExit("succeeded")
return p.Result(ctx)
res, err = p.Result(ctx)
return
}
d := cp.Frequency
if retryAfter := shared.RetryAfter(resp); retryAfter > 0 {
@ -252,7 +276,7 @@ func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOpt
}
if err = shared.Delay(ctx, d); err != nil {
logPollUntilDoneExit(err)
return *new(T), err
return
}
}
}
@ -261,17 +285,22 @@ func (p *Poller[T]) PollUntilDone(ctx context.Context, options *PollUntilDoneOpt
// If Poll succeeds, the poller's state is updated and the HTTP response is returned.
// If Poll fails, the poller's state is unmodified and the error is returned.
// Calling Poll on an LRO that has reached a terminal state will return the last HTTP response.
func (p *Poller[T]) Poll(ctx context.Context) (*http.Response, error) {
func (p *Poller[T]) Poll(ctx context.Context) (resp *http.Response, err error) {
if p.Done() {
// the LRO has reached a terminal state, don't poll again
return p.resp, nil
resp = p.resp
return
}
resp, err := p.op.Poll(ctx)
ctx, endSpan := StartSpan(ctx, fmt.Sprintf("%s.Poll", shortenTypeName(reflect.TypeOf(*p).Name())), p.tracer, nil)
defer func() { endSpan(err) }()
resp, err = p.op.Poll(ctx)
if err != nil {
return nil, err
return
}
p.resp = resp
return p.resp, nil
return
}
// Done returns true if the LRO has reached a terminal state.
@ -284,31 +313,40 @@ func (p *Poller[T]) Done() bool {
// If the LRO completed successfully, a populated instance of T is returned.
// If the LRO failed or was canceled, an *azcore.ResponseError error is returned.
// Calling this on an LRO in a non-terminal state will return an error.
func (p *Poller[T]) Result(ctx context.Context) (T, error) {
func (p *Poller[T]) Result(ctx context.Context) (res T, err error) {
if !p.Done() {
return *new(T), errors.New("poller is in a non-terminal state")
err = errors.New("poller is in a non-terminal state")
return
}
if p.done {
// the result has already been retrieved, return the cached value
if p.err != nil {
return *new(T), p.err
err = p.err
return
}
return *p.result, nil
res = *p.result
return
}
err := p.op.Result(ctx, p.result)
ctx, endSpan := StartSpan(ctx, fmt.Sprintf("%s.Result", shortenTypeName(reflect.TypeOf(*p).Name())), p.tracer, nil)
defer func() { endSpan(err) }()
err = p.op.Result(ctx, p.result)
var respErr *exported.ResponseError
if errors.As(err, &respErr) {
// the LRO failed. record the error
p.err = err
} else if err != nil {
// the call to Result failed, don't cache anything in this case
return *new(T), err
return
}
p.done = true
if p.err != nil {
return *new(T), p.err
err = p.err
return
}
return *p.result, nil
res = *p.result
return
}
// ResumeToken returns a value representing the poller that can be used to resume
@ -325,3 +363,22 @@ func (p *Poller[T]) ResumeToken() (string, error) {
}
return tk, err
}
// extracts the type name from the string returned from reflect.Value.Name()
func shortenTypeName(s string) string {
// the value is formatted as follows
// Poller[module/Package.Type].Method
// we want to shorten the generic type parameter string to Type
// anything we don't recognize will be left as-is
begin := strings.Index(s, "[")
end := strings.Index(s, "]")
if begin == -1 || end == -1 {
return s
}
typeName := s[begin+1 : end]
if i := strings.LastIndex(typeName, "."); i > -1 {
typeName = typeName[i+1:]
}
return s[:begin+1] + typeName + s[end:]
}

View file

@ -9,18 +9,14 @@ package runtime
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"encoding/xml"
"fmt"
"io"
"mime/multipart"
"net/url"
"os"
"path"
"reflect"
"strings"
"time"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared"
@ -29,14 +25,14 @@ import (
// Base64Encoding is usesd to specify which base-64 encoder/decoder to use when
// encoding/decoding a slice of bytes to/from a string.
type Base64Encoding int
type Base64Encoding = exported.Base64Encoding
const (
// Base64StdFormat uses base64.StdEncoding for encoding and decoding payloads.
Base64StdFormat Base64Encoding = 0
Base64StdFormat Base64Encoding = exported.Base64StdFormat
// Base64URLFormat uses base64.RawURLEncoding for encoding and decoding payloads.
Base64URLFormat Base64Encoding = 1
Base64URLFormat Base64Encoding = exported.Base64URLFormat
)
// NewRequest creates a new policy.Request with the specified input.
@ -93,10 +89,7 @@ func JoinPaths(root string, paths ...string) string {
// EncodeByteArray will base-64 encode the byte slice v.
func EncodeByteArray(v []byte, format Base64Encoding) string {
if format == Base64URLFormat {
return base64.RawURLEncoding.EncodeToString(v)
}
return base64.StdEncoding.EncodeToString(v)
return exported.EncodeByteArray(v, format)
}
// MarshalAsByteArray will base-64 encode the byte slice v, then calls SetBody.
@ -109,9 +102,6 @@ func MarshalAsByteArray(req *policy.Request, v []byte, format Base64Encoding) er
// MarshalAsJSON calls json.Marshal() to get the JSON encoding of v then calls SetBody.
func MarshalAsJSON(req *policy.Request, v interface{}) error {
if omit := os.Getenv("AZURE_SDK_GO_OMIT_READONLY"); omit == "true" {
v = cloneWithoutReadOnlyFields(v)
}
b, err := json.Marshal(v)
if err != nil {
return fmt.Errorf("error marshalling type %T: %s", v, err)
@ -183,80 +173,5 @@ func SkipBodyDownload(req *policy.Request) {
req.SetOperationValue(bodyDownloadPolicyOpValues{Skip: true})
}
// returns a clone of the object graph pointed to by v, omitting values of all read-only
// fields. if there are no read-only fields in the object graph, no clone is created.
func cloneWithoutReadOnlyFields(v interface{}) interface{} {
val := reflect.Indirect(reflect.ValueOf(v))
if val.Kind() != reflect.Struct {
// not a struct, skip
return v
}
// first walk the graph to find any R/O fields.
// if there aren't any, skip cloning the graph.
if !recursiveFindReadOnlyField(val) {
return v
}
return recursiveCloneWithoutReadOnlyFields(val)
}
// returns true if any field in the object graph of val contains the `azure:"ro"` tag value
func recursiveFindReadOnlyField(val reflect.Value) bool {
t := val.Type()
// iterate over the fields, looking for the "azure" tag.
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
aztag := field.Tag.Get("azure")
if azureTagIsReadOnly(aztag) {
return true
} else if reflect.Indirect(val.Field(i)).Kind() == reflect.Struct && recursiveFindReadOnlyField(reflect.Indirect(val.Field(i))) {
return true
}
}
return false
}
// clones the object graph of val. all non-R/O properties are copied to the clone
func recursiveCloneWithoutReadOnlyFields(val reflect.Value) interface{} {
t := val.Type()
clone := reflect.New(t)
// iterate over the fields, looking for the "azure" tag.
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
aztag := field.Tag.Get("azure")
if azureTagIsReadOnly(aztag) {
// omit from payload
continue
}
// clone field will receive the same value as the source field...
value := val.Field(i)
v := reflect.Indirect(value)
if v.IsValid() && v.Type() != reflect.TypeOf(time.Time{}) && v.Kind() == reflect.Struct {
// ...unless the source value is a struct, in which case we recurse to clone that struct.
// (We can't recursively clone time.Time because it contains unexported fields.)
c := recursiveCloneWithoutReadOnlyFields(v)
if field.Anonymous {
// NOTE: this does not handle the case of embedded fields of unexported struct types.
// this should be ok as we don't generate any code like this at present
value = reflect.Indirect(reflect.ValueOf(c))
} else {
value = reflect.ValueOf(c)
}
}
reflect.Indirect(clone).Field(i).Set(value)
}
return clone.Interface()
}
// returns true if the "azure" tag contains the option "ro"
func azureTagIsReadOnly(tag string) bool {
if tag == "" {
return false
}
parts := strings.Split(tag, ",")
for _, part := range parts {
if part == "ro" {
return true
}
}
return false
}
// CtxAPINameKey is used as a context key for adding/retrieving the API name.
type CtxAPINameKey = shared.CtxAPINameKey

View file

@ -8,13 +8,13 @@ package runtime
import (
"bytes"
"encoding/base64"
"encoding/json"
"encoding/xml"
"fmt"
"io"
"net/http"
azexported "github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported"
"github.com/Azure/azure-sdk-for-go/sdk/internal/exported"
)
@ -105,31 +105,5 @@ func removeBOM(resp *http.Response) error {
// DecodeByteArray will base-64 decode the provided string into v.
func DecodeByteArray(s string, v *[]byte, format Base64Encoding) error {
if len(s) == 0 {
return nil
}
payload := string(s)
if payload[0] == '"' {
// remove surrounding quotes
payload = payload[1 : len(payload)-1]
}
switch format {
case Base64StdFormat:
decoded, err := base64.StdEncoding.DecodeString(payload)
if err == nil {
*v = decoded
return nil
}
return err
case Base64URLFormat:
// use raw encoding as URL format should not contain any '=' characters
decoded, err := base64.RawURLEncoding.DecodeString(payload)
if err == nil {
*v = decoded
return nil
}
return err
default:
return fmt.Errorf("unrecognized byte array format: %d", format)
}
return azexported.DecodeByteArray(s, v, format)
}

View file

@ -11,6 +11,8 @@ import (
"net"
"net/http"
"time"
"golang.org/x/net/http2"
)
var defaultHTTPClient *http.Client
@ -24,6 +26,7 @@ func init() {
}),
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
@ -32,6 +35,13 @@ func init() {
Renegotiation: tls.RenegotiateFreelyAsClient,
},
}
// TODO: evaluate removing this once https://github.com/golang/go/issues/59690 has been fixed
if http2Transport, err := http2.ConfigureTransports(defaultTransport); err == nil {
// if the connection has been idle for 10 seconds, send a ping frame for a health check
http2Transport.ReadIdleTimeout = 10 * time.Second
// if there's no response to the ping within the timeout, the connection will be closed
http2Transport.PingTimeout = 5 * time.Second
}
defaultHTTPClient = &http.Client{
Transport: defaultTransport,
}

View file

@ -31,12 +31,12 @@ type Provider struct {
newTracerFn func(name, version string) Tracer
}
// NewTracer creates a new Tracer for the specified name and version.
// - name - the name of the tracer object, typically the fully qualified name of the service client
// - version - the version of the module in which the service client resides
func (p Provider) NewTracer(name, version string) (tracer Tracer) {
// NewTracer creates a new Tracer for the specified module name and version.
// - module - the fully qualified name of the module
// - version - the version of the module
func (p Provider) NewTracer(module, version string) (tracer Tracer) {
if p.newTracerFn != nil {
tracer = p.newTracerFn(name, version)
tracer = p.newTracerFn(module, version)
}
return
}
@ -45,21 +45,28 @@ func (p Provider) NewTracer(name, version string) (tracer Tracer) {
// TracerOptions contains the optional values when creating a Tracer.
type TracerOptions struct {
// for future expansion
// SpanFromContext contains the implementation for the Tracer.SpanFromContext method.
SpanFromContext func(context.Context) Span
}
// NewTracer creates a Tracer with the specified values.
// - newSpanFn is the underlying implementation for creating Span instances
// - options contains optional values; pass nil to accept the default value
func NewTracer(newSpanFn func(ctx context.Context, spanName string, options *SpanOptions) (context.Context, Span), options *TracerOptions) Tracer {
if options == nil {
options = &TracerOptions{}
}
return Tracer{
newSpanFn: newSpanFn,
newSpanFn: newSpanFn,
spanFromContextFn: options.SpanFromContext,
}
}
// Tracer is the factory that creates Span instances.
type Tracer struct {
newSpanFn func(ctx context.Context, spanName string, options *SpanOptions) (context.Context, Span)
attrs []Attribute
newSpanFn func(ctx context.Context, spanName string, options *SpanOptions) (context.Context, Span)
spanFromContextFn func(ctx context.Context) Span
}
// Start creates a new span and a context.Context that contains it.
@ -68,11 +75,37 @@ type Tracer struct {
// - options contains optional values for the span, pass nil to accept any defaults
func (t Tracer) Start(ctx context.Context, spanName string, options *SpanOptions) (context.Context, Span) {
if t.newSpanFn != nil {
return t.newSpanFn(ctx, spanName, options)
opts := SpanOptions{}
if options != nil {
opts = *options
}
opts.Attributes = append(opts.Attributes, t.attrs...)
return t.newSpanFn(ctx, spanName, &opts)
}
return ctx, Span{}
}
// SetAttributes sets attrs to be applied to each Span. If a key from attrs
// already exists for an attribute of the Span it will be overwritten with
// the value contained in attrs.
func (t *Tracer) SetAttributes(attrs ...Attribute) {
t.attrs = append(t.attrs, attrs...)
}
// Enabled returns true if this Tracer is capable of creating Spans.
func (t Tracer) Enabled() bool {
return t.newSpanFn != nil
}
// SpanFromContext returns the Span associated with the current context.
// If the provided context has no Span, false is returned.
func (t Tracer) SpanFromContext(ctx context.Context) Span {
if t.spanFromContextFn != nil {
return t.spanFromContextFn(ctx)
}
return Span{}
}
// SpanOptions contains optional settings for creating a span.
type SpanOptions struct {
// Kind indicates the kind of Span.
@ -97,9 +130,6 @@ type SpanImpl struct {
// AddEvent contains the implementation for the Span.AddEvent method.
AddEvent func(string, ...Attribute)
// AddError contains the implementation for the Span.AddError method.
AddError func(err error)
// SetStatus contains the implementation for the Span.SetStatus method.
SetStatus func(SpanStatus, string)
}
@ -140,13 +170,6 @@ func (s Span) AddEvent(name string, attrs ...Attribute) {
}
}
// AddError adds the specified error event to the span.
func (s Span) AddError(err error) {
if s.impl.AddError != nil {
s.impl.AddError(err)
}
}
// SetStatus sets the status on the span along with a description.
func (s Span) SetStatus(code SpanStatus, desc string) {
if s.impl.SetStatus != nil {

View file

@ -1,14 +0,0 @@
dist
/doc
/doc-staging
.yardoc
Gemfile.lock
/internal/awstesting/integration/smoke/**/importmarker__.go
/internal/awstesting/integration/smoke/_test/
/vendor
/private/model/cli/gen-api/gen-api
.gradle/
build/
.idea/
bin/
.vscode/

View file

@ -1,27 +0,0 @@
[run]
concurrency = 4
timeout = "1m"
issues-exit-code = 0
modules-download-mode = "readonly"
allow-parallel-runners = true
skip-dirs = ["internal/repotools"]
skip-dirs-use-default = true
skip-files = ["service/transcribestreaming/eventstream_test.go"]
[output]
format = "github-actions"
[linters-settings.cyclop]
skip-tests = false
[linters-settings.errcheck]
check-blank = true
[linters]
disable-all = true
enable = ["errcheck"]
fast = false
[issues]
exclude-use-default = false
# Refer config definitions at https://golangci-lint.run/usage/configuration/#config-file

View file

@ -1,31 +0,0 @@
language: go
sudo: true
dist: bionic
branches:
only:
- main
os:
- linux
- osx
# Travis doesn't work with windows and Go tip
#- windows
go:
- tip
matrix:
allow_failures:
- go: tip
before_install:
- if [ "$TRAVIS_OS_NAME" = "windows" ]; then choco install make; fi
- (cd /tmp/; go get golang.org/x/lint/golint)
env:
- EACHMODULE_CONCURRENCY=4
script:
- make ci-test-no-generate;

File diff suppressed because it is too large Load diff

View file

@ -1,4 +0,0 @@
## Code of Conduct
This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
opensource-codeofconduct@amazon.com with any additional questions or comments.

View file

@ -1,177 +0,0 @@
# Contributing to the AWS SDK for Go
Thank you for your interest in contributing to the AWS SDK for Go!
We work hard to provide a high-quality and useful SDK, and we greatly value
feedback and contributions from our community. Whether it's a bug report,
new feature, correction, or additional documentation, we welcome your issues
and pull requests. Please read through this document before submitting any
[issues] or [pull requests][pr] to ensure we have all the necessary information to
effectively respond to your bug report or contribution.
Jump To:
* [Bug Reports](#bug-reports)
* [Feature Requests](#feature-requests)
* [Code Contributions](#code-contributions)
## How to contribute
*Before you send us a pull request, please be sure that:*
1. You're working from the latest source on the `main` branch.
2. You check existing open, and recently closed, pull requests to be sure
that someone else hasn't already addressed the problem.
3. You create an issue before working on a contribution that will take a
significant amount of your time.
*Creating a Pull Request*
1. Fork the repository.
2. In your fork, make your change in a branch that's based on this repo's `main` branch.
3. Commit the change to your fork, using a clear and descriptive commit message.
4. Create a pull request, answering any questions in the pull request form.
For contributions that will take a significant amount of time, open a new
issue to pitch your idea before you get started. Explain the problem and
describe the content you want to see added to the documentation. Let us know
if you'll write it yourself or if you'd like us to help. We'll discuss your
proposal with you and let you know whether we're likely to accept it.
## Bug Reports
You can file bug reports against the SDK on the [GitHub issues][issues] page.
If you are filing a report for a bug or regression in the SDK, it's extremely
helpful to provide as much information as possible when opening the original
issue. This helps us reproduce and investigate the possible bug without having
to wait for this extra information to be provided. Please read the following
guidelines prior to filing a bug report.
1. Search through existing [issues][] to ensure that your specific issue has
not yet been reported. If it is a common issue, it is likely there is
already a bug report for your problem.
2. Ensure that you have tested the latest version of the SDK. Although you
may have an issue against an older version of the SDK, we cannot provide
bug fixes for old versions. It's also possible that the bug may have been
fixed in the latest release.
3. Provide as much information about your environment, SDK version, and
relevant dependencies as possible. For example, let us know what version
of Go you are using, which and version of the operating system, and the
the environment your code is running in. e.g Container.
4. Provide a minimal test case that reproduces your issue or any error
information you related to your problem. We can provide feedback much
more quickly if we know what operations you are calling in the SDK. If
you cannot provide a full test case, provide as much code as you can
to help us diagnose the problem. Any relevant information should be provided
as well, like whether this is a persistent issue, or if it only occurs
some of the time.
## Feature Requests
Open an [issue][issues] with the following:
* A short, descriptive title. Ideally, other community members should be able
to get a good idea of the feature just from reading the title.
* A detailed description of the the proposed feature.
* Why it should be added to the SDK.
* If possible, example code to illustrate how it should work.
* Use Markdown to make the request easier to read;
* If you intend to implement this feature, indicate that you'd like to the issue to be assigned to you.
## Code Contributions
We are always happy to receive code and documentation contributions to the SDK.
Please be aware of the following notes prior to opening a pull request:
1. The SDK is released under the [Apache license][license]. Any code you submit
will be released under that license. For substantial contributions, we may
ask you to sign a [Contributor License Agreement (CLA)][cla].
2. If you would like to implement support for a significant feature that is not
yet available in the SDK, please talk to us beforehand to avoid any
duplication of effort.
3. Wherever possible, pull requests should contain tests as appropriate.
Bugfixes should contain tests that exercise the corrected behavior (i.e., the
test should fail without the bugfix and pass with it), and new features
should be accompanied by tests exercising the feature.
4. Pull requests that contain failing tests will not be merged until the test
failures are addressed. Pull requests that cause a significant drop in the
SDK's test coverage percentage are unlikely to be merged until tests have
been added.
5. The JSON files under the SDK's `models` folder are sourced from outside the SDK.
Such as `models/apis/ec2/2016-11-15/api.json`. We will not accept pull requests
directly on these models. If you discover an issue with the models please
create a [GitHub issue][issues] describing the issue.
### Testing
To run the tests locally, running the `make unit` command will `go get` the
SDK's testing dependencies, and run vet, link and unit tests for the SDK.
```
make unit
```
Standard go testing functionality is supported as well. To test SDK code that
is tagged with `codegen` you'll need to set the build tag in the go test
command. The `make unit` command will do this automatically.
```
go test -tags codegen ./private/...
```
See the `Makefile` for additional testing tags that can be used in testing.
To test on multiple platform the SDK includes several DockerFiles under the
`awstesting/sandbox` folder, and associated make recipes to to execute
unit testing within environments configured for specific Go versions.
```
make sandbox-test-go18
```
To run all sandbox environments use the following make recipe
```
# Optionally update the Go tip that will be used during the batch testing
make update-aws-golang-tip
# Run all SDK tests for supported Go versions in sandboxes
make sandbox-test
```
In addition the sandbox environment include make recipes for interactive modes
so you can run command within the Docker container and context of the SDK.
```
make sandbox-go18
```
### Changelog Documents
You can see all release changes in the `CHANGELOG.md` file at the root of the
repository. The release notes added to this file will contain service client
updates, and major SDK changes. When submitting a pull request please include an entry in `CHANGELOG_PENDING.md` under the appropriate changelog type so your changelog entry is included on the following release.
#### Changelog Types
* `SDK Features` - For major additive features, internal changes that have
outward impact, or updates to the SDK foundations. This will result in a minor
version change.
* `SDK Enhancements` - For minor additive features or incremental sized changes.
This will result in a patch version change.
* `SDK Bugs` - For minor changes that resolve an issue. This will result in a
patch version change.
[issues]: https://github.com/aws/aws-sdk-go-v2/issues
[pr]: https://github.com/aws/aws-sdk-go-v2/pulls
[license]: http://aws.amazon.com/apache2.0/
[cla]: http://en.wikipedia.org/wiki/Contributor_License_Agreement
[releasenotes]: https://github.com/aws/aws-sdk-go-v2/releases

View file

@ -1,15 +0,0 @@
Open Discussions
---
The following issues are currently open for community feedback.
All discourse must adhere to the [Code of Conduct] policy.
* [Refactoring API Client Paginators](https://github.com/aws/aws-sdk-go-v2/issues/439)
* [Refactoring API Client Waiters](https://github.com/aws/aws-sdk-go-v2/issues/442)
* [Refactoring API Client Enums and Types to Discrete Packages](https://github.com/aws/aws-sdk-go-v2/issues/445)
* [SDK Modularization](https://github.com/aws/aws-sdk-go-v2/issues/444)
Past Discussions
---
The issues listed here are for documentation purposes, and is used to capture issues and their associated discussions.
[Code of Conduct]: https://github.com/aws/aws-sdk-go-v2/blob/main/CODE_OF_CONDUCT.md

View file

@ -1,538 +0,0 @@
# Lint rules to ignore
LINTIGNORESINGLEFIGHT='internal/sync/singleflight/singleflight.go:.+error should be the last type'
LINT_IGNORE_S3MANAGER_INPUT='feature/s3/manager/upload.go:.+struct field SSEKMSKeyId should be SSEKMSKeyID'
# Names of these are tied to endpoint rules and they're internal so ignore them
LINT_IGNORE_AWSRULESFN_ARN='internal/endpoints/awsrulesfn/arn.go'
LINT_IGNORE_AWSRULESFN_PARTITION='internal/endpoints/awsrulesfn/partition.go'
UNIT_TEST_TAGS=
BUILD_TAGS=-tags "example,codegen,integration,ec2env,perftest"
SMITHY_GO_SRC ?= $(shell pwd)/../smithy-go
SDK_MIN_GO_VERSION ?= 1.19
EACHMODULE_FAILFAST ?= true
EACHMODULE_FAILFAST_FLAG=-fail-fast=${EACHMODULE_FAILFAST}
EACHMODULE_CONCURRENCY ?= 1
EACHMODULE_CONCURRENCY_FLAG=-c ${EACHMODULE_CONCURRENCY}
EACHMODULE_SKIP ?=
EACHMODULE_SKIP_FLAG=-skip="${EACHMODULE_SKIP}"
EACHMODULE_FLAGS=${EACHMODULE_CONCURRENCY_FLAG} ${EACHMODULE_FAILFAST_FLAG} ${EACHMODULE_SKIP_FLAG}
# SDK's Core and client packages that are compatible with Go 1.9+.
SDK_CORE_PKGS=./aws/... ./internal/...
SDK_CLIENT_PKGS=./service/...
SDK_COMPA_PKGS=${SDK_CORE_PKGS} ${SDK_CLIENT_PKGS}
# SDK additional packages that are used for development of the SDK.
SDK_EXAMPLES_PKGS=
SDK_ALL_PKGS=${SDK_COMPA_PKGS} ${SDK_EXAMPLES_PKGS}
RUN_NONE=-run NONE
RUN_INTEG=-run '^TestInteg_'
CODEGEN_RESOURCES_PATH=$(shell pwd)/codegen/smithy-aws-go-codegen/src/main/resources/software/amazon/smithy/aws/go/codegen
CODEGEN_API_MODELS_PATH=$(shell pwd)/codegen/sdk-codegen/aws-models
ENDPOINTS_JSON=${CODEGEN_RESOURCES_PATH}/endpoints.json
ENDPOINT_PREFIX_JSON=${CODEGEN_RESOURCES_PATH}/endpoint-prefix.json
LICENSE_FILE=$(shell pwd)/LICENSE.txt
SMITHY_GO_VERSION ?=
PRE_RELEASE_VERSION ?=
RELEASE_MANIFEST_FILE ?=
RELEASE_CHGLOG_DESC_FILE ?=
REPOTOOLS_VERSION ?= latest
REPOTOOLS_MODULE = github.com/awslabs/aws-go-multi-module-repository-tools
REPOTOOLS_CMD_ANNOTATE_STABLE_GEN = ${REPOTOOLS_MODULE}/cmd/annotatestablegen@${REPOTOOLS_VERSION}
REPOTOOLS_CMD_MAKE_RELATIVE = ${REPOTOOLS_MODULE}/cmd/makerelative@${REPOTOOLS_VERSION}
REPOTOOLS_CMD_CALCULATE_RELEASE = ${REPOTOOLS_MODULE}/cmd/calculaterelease@${REPOTOOLS_VERSION}
REPOTOOLS_CMD_UPDATE_REQUIRES = ${REPOTOOLS_MODULE}/cmd/updaterequires@${REPOTOOLS_VERSION}
REPOTOOLS_CMD_UPDATE_MODULE_METADATA = ${REPOTOOLS_MODULE}/cmd/updatemodulemeta@${REPOTOOLS_VERSION}
REPOTOOLS_CMD_GENERATE_CHANGELOG = ${REPOTOOLS_MODULE}/cmd/generatechangelog@${REPOTOOLS_VERSION}
REPOTOOLS_CMD_CHANGELOG = ${REPOTOOLS_MODULE}/cmd/changelog@${REPOTOOLS_VERSION}
REPOTOOLS_CMD_TAG_RELEASE = ${REPOTOOLS_MODULE}/cmd/tagrelease@${REPOTOOLS_VERSION}
REPOTOOLS_CMD_EDIT_MODULE_DEPENDENCY = ${REPOTOOLS_MODULE}/cmd/editmoduledependency@${REPOTOOLS_VERSION}
REPOTOOLS_CALCULATE_RELEASE_VERBOSE ?= false
REPOTOOLS_CALCULATE_RELEASE_VERBOSE_FLAG=-v=${REPOTOOLS_CALCULATE_RELEASE_VERBOSE}
REPOTOOLS_CALCULATE_RELEASE_ADDITIONAL_ARGS ?=
ifneq ($(PRE_RELEASE_VERSION),)
REPOTOOLS_CALCULATE_RELEASE_ADDITIONAL_ARGS += -preview=${PRE_RELEASE_VERSION}
endif
.PHONY: all
all: generate unit
###################
# Code Generation #
###################
.PHONY: generate smithy-generate smithy-build smithy-build-% smithy-clean smithy-go-publish-local format \
gen-config-asserts gen-repo-mod-replace gen-mod-replace-smithy gen-mod-dropreplace-smithy-% gen-aws-ptrs tidy-modules-% \
add-module-license-files sync-models sync-endpoints-model sync-endpoints.json clone-v1-models gen-internal-codegen \
sync-api-models copy-attributevalue-feature min-go-version-% update-requires smithy-annotate-stable \
update-module-metadata download-modules-%
generate: smithy-generate update-requires gen-repo-mod-replace update-module-metadata smithy-annotate-stable \
gen-config-asserts gen-internal-codegen copy-attributevalue-feature gen-mod-dropreplace-smithy-. min-go-version-. \
tidy-modules-. add-module-license-files gen-aws-ptrs format
generate-tmpreplace-smithy: smithy-generate update-requires gen-repo-mod-replace update-module-metadata smithy-annotate-stable \
gen-config-asserts gen-internal-codegen copy-attributevalue-feature gen-mod-replace-smithy-. min-go-version-. \
tidy-modules-. add-module-license-files gen-aws-ptrs format gen-mod-dropreplace-smithy-. reset-sum
reset-sum:
find . -name go.sum -exec git checkout -- {} \;
smithy-generate:
cd codegen && ./gradlew clean build -Plog-tests && ./gradlew clean
smithy-build:
cd codegen && ./gradlew clean build -Plog-tests
smithy-build-%:
@# smithy-build- command that uses the pattern to define build filter that
@# the smithy API model service id starts with. Strips off the
@# "smithy-build-".
@#
@# e.g. smithy-build-com.amazonaws.rds
@# e.g. smithy-build-com.amazonaws.rds#AmazonRDSv19
cd codegen && \
SMITHY_GO_BUILD_API="$(subst smithy-build-,,$@)" ./gradlew clean build -Plog-tests
smithy-annotate-stable:
go run ${REPOTOOLS_CMD_ANNOTATE_STABLE_GEN}
smithy-clean:
cd codegen && ./gradlew clean
smithy-go-publish-local:
rm -rf /tmp/smithy-go-local
git clone https://github.com/aws/smithy-go /tmp/smithy-go-local
make -C /tmp/smithy-go-local smithy-clean smithy-publish-local
format:
gofmt -w -s .
gen-config-asserts:
@echo "Generating SDK config package implementor assertions"
cd config \
&& go mod tidy \
&& go generate
gen-internal-codegen:
@echo "Generating internal/codegen"
cd internal/codegen \
&& go mod tidy \
&& go generate
gen-repo-mod-replace:
@echo "Generating go.mod replace for repo modules"
go run ${REPOTOOLS_CMD_MAKE_RELATIVE}
gen-mod-replace-smithy-%:
@# gen-mod-replace-smithy- command that uses the pattern to define build filter that
@# for modules to add replace to. Strips off the "gen-mod-replace-smithy-".
@#
@# SMITHY_GO_SRC environment variable is the path to add replace to
@#
@# e.g. gen-mod-replace-smithy-service_ssooidc
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst gen-mod-replace-smithy-,,$@)) ${EACHMODULE_FLAGS} \
"go mod edit -replace github.com/aws/smithy-go=${SMITHY_GO_SRC}"
gen-mod-dropreplace-smithy-%:
@# gen-mod-dropreplace-smithy- command that uses the pattern to define build filter that
@# for modules to add replace to. Strips off the "gen-mod-dropreplace-smithy-".
@#
@# e.g. gen-mod-dropreplace-smithy-service_ssooidc
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst gen-mod-dropreplace-smithy-,,$@)) ${EACHMODULE_FLAGS} \
"go mod edit -dropreplace github.com/aws/smithy-go"
gen-aws-ptrs:
cd aws && go generate
tidy-modules-%:
@# tidy command that uses the pattern to define the root path that the
@# module testing will start from. Strips off the "tidy-modules-" and
@# replaces all "_" with "/".
@#
@# e.g. tidy-modules-internal_protocoltest
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst tidy-modules-,,$@)) ${EACHMODULE_FLAGS} \
"go mod tidy"
download-modules-%:
@# download command that uses the pattern to define the root path that the
@# module testing will start from. Strips off the "download-modules-" and
@# replaces all "_" with "/".
@#
@# e.g. download-modules-internal_protocoltest
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst download-modules-,,$@)) ${EACHMODULE_FLAGS} \
"go mod download all"
add-module-license-files:
cd internal/repotools/cmd/eachmodule && \
go run . -skip-root \
"cp $(LICENSE_FILE) ."
sync-models: sync-endpoints-model sync-api-models
sync-endpoints-model: sync-endpoints.json
sync-endpoints.json:
[[ ! -z "${ENDPOINTS_MODEL}" ]] && cp ${ENDPOINTS_MODEL} ${ENDPOINTS_JSON} || echo "ENDPOINTS_MODEL not set, must not be empty"
clone-v1-models:
rm -rf /tmp/aws-sdk-go-model-sync
git clone https://github.com/aws/aws-sdk-go.git --depth 1 /tmp/aws-sdk-go-model-sync
sync-api-models:
cd internal/repotools/cmd/syncAPIModels && \
go run . \
-m ${API_MODELS} \
-o ${CODEGEN_API_MODELS_PATH}
copy-attributevalue-feature:
cd ./feature/dynamodbstreams/attributevalue && \
find . -name "*.go" | grep -v "doc.go" | xargs -I % rm % && \
find ../../dynamodb/attributevalue -name "*.go" | grep -v "doc.go" | xargs -I % cp % . && \
ls *.go | grep -v "convert.go" | grep -v "doc.go" | \
xargs -I % sed -i.bk -E 's:github.com/aws/aws-sdk-go-v2/(service|feature)/dynamodb:github.com/aws/aws-sdk-go-v2/\1/dynamodbstreams:g' % && \
ls *.go | grep -v "convert.go" | grep -v "doc.go" | \
xargs -I % sed -i.bk 's:DynamoDB:DynamoDBStreams:g' % && \
ls *.go | grep -v "doc.go" | \
xargs -I % sed -i.bk 's:dynamodb\.:dynamodbstreams.:g' % && \
sed -i.bk 's:streams\.:ddbtypes.:g' "convert.go" && \
sed -i.bk 's:ddb\.:streams.:g' "convert.go" && \
sed -i.bk 's:ddbtypes\.:ddb.:g' "convert.go" &&\
sed -i.bk 's:Streams::g' "convert.go" && \
rm -rf ./*.bk && \
go mod tidy && \
gofmt -w -s . && \
go test .
min-go-version-%:
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst min-go-version-,,$@)) ${EACHMODULE_FLAGS} \
"go mod edit -go=${SDK_MIN_GO_VERSION}"
update-requires:
go run ${REPOTOOLS_CMD_UPDATE_REQUIRES}
update-module-metadata:
go run ${REPOTOOLS_CMD_UPDATE_MODULE_METADATA}
################
# Unit Testing #
################
.PHONY: unit unit-race unit-test unit-race-test unit-race-modules-% unit-modules-% build build-modules-% \
go-build-modules-% test test-race-modules-% test-modules-% cachedep cachedep-modules-% api-diff-modules-%
unit: lint unit-modules-.
unit-race: lint unit-race-modules-.
unit-test: test-modules-.
unit-race-test: test-race-modules-.
unit-race-modules-%:
@# unit command that uses the pattern to define the root path that the
@# module testing will start from. Strips off the "unit-race-modules-" and
@# replaces all "_" with "/".
@#
@# e.g. unit-race-modules-internal_protocoltest
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst unit-race-modules-,,$@)) ${EACHMODULE_FLAGS} \
"go vet ${BUILD_TAGS} --all ./..." \
"go test ${BUILD_TAGS} ${RUN_NONE} ./..." \
"go test -timeout=1m ${UNIT_TEST_TAGS} -race -cpu=4 ./..."
unit-modules-%:
@# unit command that uses the pattern to define the root path that the
@# module testing will start from. Strips off the "unit-modules-" and
@# replaces all "_" with "/".
@#
@# e.g. unit-modules-internal_protocoltest
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst unit-modules-,,$@)) ${EACHMODULE_FLAGS} \
"go vet ${BUILD_TAGS} --all ./..." \
"go test ${BUILD_TAGS} ${RUN_NONE} ./..." \
"go test -timeout=1m ${UNIT_TEST_TAGS} ./..."
build: build-modules-.
build-modules-%:
@# build command that uses the pattern to define the root path that the
@# module testing will start from. Strips off the "build-modules-" and
@# replaces all "_" with "/".
@#
@# e.g. build-modules-internal_protocoltest
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst build-modules-,,$@)) ${EACHMODULE_FLAGS} \
"go test ${BUILD_TAGS} ${RUN_NONE} ./..."
go-build-modules-%:
@# build command that uses the pattern to define the root path that the
@# module testing will start from. Strips off the "build-modules-" and
@# replaces all "_" with "/".
@#
@# Validates that all modules in the repo have buildable Go files.
@#
@# e.g. go-build-modules-internal_protocoltest
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst go-build-modules-,,$@)) ${EACHMODULE_FLAGS} \
"go build ${BUILD_TAGS} ./..."
test: test-modules-.
test-race-modules-%:
@# Test command that uses the pattern to define the root path that the
@# module testing will start from. Strips off the "test-race-modules-" and
@# replaces all "_" with "/".
@#
@# e.g. test-race-modules-internal_protocoltest
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst test-race-modules-,,$@)) ${EACHMODULE_FLAGS} \
"go test -timeout=1m ${UNIT_TEST_TAGS} -race -cpu=4 ./..."
test-modules-%:
@# Test command that uses the pattern to define the root path that the
@# module testing will start from. Strips off the "test-modules-" and
@# replaces all "_" with "/".
@#
@# e.g. test-modules-internal_protocoltest
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst test-modules-,,$@)) ${EACHMODULE_FLAGS} \
"go test -timeout=1m ${UNIT_TEST_TAGS} ./..."
cachedep: cachedep-modules-.
cachedep-modules-%:
@# build command that uses the pattern to define the root path that the
@# module caching will start from. Strips off the "cachedep-modules-" and
@# replaces all "_" with "/".
@#
@# e.g. cachedep-modules-internal_protocoltest
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst cachedep-modules-,,$@)) ${EACHMODULE_FLAGS} \
"go mod download"
api-diff-modules-%:
@# Command that uses the pattern to define the root path that the
@# module testing will start from. Strips off the "api-diff-modules-" and
@# replaces all "_" with "/".
@#
@# Requires golang.org/x/exp/cmd/gorelease to be available in the GOPATH.
@#
@# e.g. api-diff-modules-internal_protocoltest
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst api-diff-modules-,,$@)) \
-fail-fast=true \
-c 1 \
-skip="internal/repotools" \
"$$(go env GOPATH)/bin/gorelease"
##############
# CI Testing #
##############
.PHONY: ci-test ci-test-no-generate ci-test-generate-validate
ci-test: generate unit-race ci-test-generate-validate
ci-test-no-generate: unit-race
ci-test-generate-validate:
@echo "CI test validate no generated code changes"
git update-index --assume-unchanged go.mod go.sum
git add . -A
gitstatus=`git diff --cached --ignore-space-change`; \
echo "$$gitstatus"; \
if [ "$$gitstatus" != "" ] && [ "$$gitstatus" != "skipping validation" ]; then echo "$$gitstatus"; exit 1; fi
git update-index --no-assume-unchanged go.mod go.sum
ci-lint: ci-lint-.
ci-lint-%:
@# Run golangci-lint command that uses the pattern to define the root path that the
@# module check will start from. Strips off the "ci-lint-" and
@# replaces all "_" with "/".
@#
@# e.g. ci-lint-internal_protocoltest
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst ci-lint-,,$@)) \
-fail-fast=false \
-c 1 \
-skip="internal/repotools" \
"golangci-lint run"
ci-lint-install:
@# Installs golangci-lint at GoPATH.
@# This should be used to run golangci-lint locally.
@#
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
#######################
# Integration Testing #
#######################
.PHONY: integration integ-modules-% cleanup-integ-buckets
integration: integ-modules-service
integ-modules-%:
@# integration command that uses the pattern to define the root path that
@# the module testing will start from. Strips off the "integ-modules-" and
@# replaces all "_" with "/".
@#
@# e.g. test-modules-service_dynamodb
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst integ-modules-,,$@)) ${EACHMODULE_FLAGS} \
"go test -timeout=10m -tags "integration" -v ${RUN_INTEG} -count 1 ./..."
cleanup-integ-buckets:
@echo "Cleaning up SDK integration resources"
go run -tags "integration" ./internal/awstesting/cmd/bucket_cleanup/main.go "aws-sdk-go-integration"
##############
# Benchmarks #
##############
.PHONY: bench bench-modules-%
bench: bench-modules-.
bench-modules-%:
@# benchmark command that uses the pattern to define the root path that
@# the module testing will start from. Strips off the "bench-modules-" and
@# replaces all "_" with "/".
@#
@# e.g. bench-modules-service_dynamodb
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst bench-modules-,,$@)) ${EACHMODULE_FLAGS} \
"go test -timeout=10m -bench . --benchmem ${BUILD_TAGS} ${RUN_NONE} ./..."
#####################
# Release Process #
#####################
.PHONY: preview-release pre-release-validation release
ls-changes:
go run ${REPOTOOLS_CMD_CHANGELOG} ls
preview-release:
go run ${REPOTOOLS_CMD_CALCULATE_RELEASE} ${REPOTOOLS_CALCULATE_RELEASE_VERBOSE_FLAG} ${REPOTOOLS_CALCULATE_RELEASE_ADDITIONAL_ARGS}
pre-release-validation:
@if [[ -z "${RELEASE_MANIFEST_FILE}" ]]; then \
echo "RELEASE_MANIFEST_FILE is required to specify the file to write the release manifest" && false; \
fi
@if [[ -z "${RELEASE_CHGLOG_DESC_FILE}" ]]; then \
echo "RELEASE_CHGLOG_DESC_FILE is required to specify the file to write the release notes" && false; \
fi
release: pre-release-validation
go run ${REPOTOOLS_CMD_CALCULATE_RELEASE} -o ${RELEASE_MANIFEST_FILE} ${REPOTOOLS_CALCULATE_RELEASE_VERBOSE_FLAG} ${REPOTOOLS_CALCULATE_RELEASE_ADDITIONAL_ARGS}
go run ${REPOTOOLS_CMD_UPDATE_REQUIRES} -release ${RELEASE_MANIFEST_FILE}
go run ${REPOTOOLS_CMD_UPDATE_MODULE_METADATA} -release ${RELEASE_MANIFEST_FILE}
go run ${REPOTOOLS_CMD_GENERATE_CHANGELOG} -release ${RELEASE_MANIFEST_FILE} -o ${RELEASE_CHGLOG_DESC_FILE}
go run ${REPOTOOLS_CMD_CHANGELOG} rm -all
go run ${REPOTOOLS_CMD_TAG_RELEASE} -release ${RELEASE_MANIFEST_FILE}
##############
# Repo Tools #
##############
.PHONY: install-repotools
install-repotools:
go install ${REPOTOOLS_MODULE}/cmd/changelog@${REPOTOOLS_VERSION}
set-smithy-go-version:
@if [[ -z "${SMITHY_GO_VERSION}" ]]; then \
echo "SMITHY_GO_VERSION is required to update SDK's smithy-go module dependency version" && false; \
fi
go run ${REPOTOOLS_CMD_EDIT_MODULE_DEPENDENCY} -s "github.com/aws/smithy-go" -v "${SMITHY_GO_VERSION}"
##################
# Linting/Verify #
##################
.PHONY: verify lint vet vet-modules-% sdkv1check
verify: lint vet sdkv1check
lint:
@echo "go lint SDK and vendor packages"
@lint=`golint ./...`; \
dolint=`echo "$$lint" | grep -E -v \
-e ${LINT_IGNORE_S3MANAGER_INPUT} \
-e ${LINTIGNORESINGLEFIGHT} \
-e ${LINT_IGNORE_AWSRULESFN_ARN} \
-e ${LINT_IGNORE_AWSRULESFN_PARTITION}`; \
echo "$$dolint"; \
if [ "$$dolint" != "" ]; then exit 1; fi
vet: vet-modules-.
vet-modules-%:
cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst vet-modules-,,$@)) ${EACHMODULE_FLAGS} \
"go vet ${BUILD_TAGS} --all ./..."
sdkv1check:
@echo "Checking for usage of AWS SDK for Go v1"
@sdkv1usage=`go list -test -f '''{{ if not .Standard }}{{ range $$_, $$name := .Imports }} * {{ $$.ImportPath }} -> {{ $$name }}{{ print "\n" }}{{ end }}{{ range $$_, $$name := .TestImports }} *: {{ $$.ImportPath }} -> {{ $$name }}{{ print "\n" }}{{ end }}{{ end}}''' ./... | sort -u | grep '''/aws-sdk-go/'''`; \
echo "$$sdkv1usage"; \
if [ "$$sdkv1usage" != "" ]; then exit 1; fi
list-deps: list-deps-.
list-deps-%:
@# command that uses the pattern to define the root path that the
@# module testing will start from. Strips off the "list-deps-" and
@# replaces all "_" with "/".
@#
@# Trim output to only include stdout for list of dependencies only.
@# make list-deps 2>&-
@#
@# e.g. list-deps-internal_protocoltest
@cd ./internal/repotools/cmd/eachmodule \
&& go run . -p $(subst _,/,$(subst list-deps-,,$@)) ${EACHMODULE_FLAGS} \
"go list -m all | grep -v 'github.com/aws/aws-sdk-go-v2'" | sort -u
###################
# Sandbox Testing #
###################
.PHONY: sandbox-tests sandbox-build-% sandbox-run-% sandbox-test-% update-aws-golang-tip
sandbox-tests: sandbox-test-go1.15 sandbox-test-go1.16 sandbox-test-go1.17 sandbox-test-go1.18 sandbox-test-go1.19 sandbox-test-go1.20 sandbox-test-gotip
sandbox-build-%:
@# sandbox-build-go1.17
@# sandbox-build-gotip
@if [ $@ == sandbox-build-gotip ]; then\
docker build \
-f ./internal/awstesting/sandbox/Dockerfile.test.gotip \
-t "aws-sdk-go-$(subst sandbox-build-,,$@)" . ;\
else\
docker build \
--build-arg GO_VERSION=$(subst sandbox-build-go,,$@) \
-f ./internal/awstesting/sandbox/Dockerfile.test.goversion \
-t "aws-sdk-go-$(subst sandbox-build-,,$@)" . ;\
fi
sandbox-run-%: sandbox-build-%
@# sandbox-run-go1.17
@# sandbox-run-gotip
docker run -i -t "aws-sdk-go-$(subst sandbox-run-,,$@)" bash
sandbox-test-%: sandbox-build-%
@# sandbox-test-go1.17
@# sandbox-test-gotip
docker run -t "aws-sdk-go-$(subst sandbox-test-,,$@)"
update-aws-golang-tip:
docker build --no-cache=true -f ./internal/awstesting/sandbox/Dockerfile.golang-tip -t "aws-golang:tip" .

View file

@ -1,165 +0,0 @@
# AWS SDK for Go v2
[![Go Build status](https://github.com/aws/aws-sdk-go-v2/actions/workflows/go.yml/badge.svg?branch=main)](https://github.com/aws/aws-sdk-go-v2/actions/workflows/go.yml)[![Codegen Build status](https://github.com/aws/aws-sdk-go-v2/actions/workflows/codegen.yml/badge.svg?branch=main)](https://github.com/aws/aws-sdk-go-v2/actions/workflows/codegen.yml) [![SDK Documentation](https://img.shields.io/badge/SDK-Documentation-blue)](https://aws.github.io/aws-sdk-go-v2/docs/) [![Migration Guide](https://img.shields.io/badge/Migration-Guide-blue)](https://aws.github.io/aws-sdk-go-v2/docs/migrating/) [![API Reference](https://img.shields.io/badge/api-reference-blue.svg)](https://pkg.go.dev/mod/github.com/aws/aws-sdk-go-v2) [![Apache V2 License](https://img.shields.io/badge/license-Apache%20V2-blue.svg)](https://github.com/aws/aws-sdk-go-v2/blob/main/LICENSE.txt)
`aws-sdk-go-v2` is the v2 AWS SDK for the Go programming language.
The v2 SDK requires a minimum version of `Go 1.19`.
Check out the [release notes](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) for information about the latest bug
fixes, updates, and features added to the SDK.
Jump To:
* [Getting Started](#getting-started)
* [Getting Help](#getting-help)
* [Contributing](#feedback-and-contributing)
* [More Resources](#resources)
## Maintenance and support for SDK major versions
For information about maintenance and support for SDK major versions and their underlying dependencies, see the
following in the AWS SDKs and Tools Shared Configuration and Credentials Reference Guide:
* [AWS SDKs and Tools Maintenance Policy](https://docs.aws.amazon.com/credref/latest/refdocs/maint-policy.html)
* [AWS SDKs and Tools Version Support Matrix](https://docs.aws.amazon.com/credref/latest/refdocs/version-support-matrix.html)
### Go version support policy
The v2 SDK follows the upstream [release policy](https://go.dev/doc/devel/release#policy)
with an additional six months of support for the most recently deprecated
language version.
**AWS reserves the right to drop support for unsupported Go versions earlier to
address critical security issues.**
## Getting started
To get started working with the SDK setup your project for Go modules, and retrieve the SDK dependencies with `go get`.
This example shows how you can use the v2 SDK to make an API request using the SDK's [Amazon DynamoDB] client.
###### Initialize Project
```sh
$ mkdir ~/helloaws
$ cd ~/helloaws
$ go mod init helloaws
```
###### Add SDK Dependencies
```sh
$ go get github.com/aws/aws-sdk-go-v2/aws
$ go get github.com/aws/aws-sdk-go-v2/config
$ go get github.com/aws/aws-sdk-go-v2/service/dynamodb
```
###### Write Code
In your preferred editor add the following content to `main.go`
```go
package main
import (
"context"
"fmt"
"log"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
)
func main() {
// Using the SDK's default configuration, loading additional config
// and credentials values from the environment variables, shared
// credentials, and shared configuration files
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-west-2"))
if err != nil {
log.Fatalf("unable to load SDK config, %v", err)
}
// Using the Config value, create the DynamoDB client
svc := dynamodb.NewFromConfig(cfg)
// Build the request with its input parameters
resp, err := svc.ListTables(context.TODO(), &dynamodb.ListTablesInput{
Limit: aws.Int32(5),
})
if err != nil {
log.Fatalf("failed to list tables, %v", err)
}
fmt.Println("Tables:")
for _, tableName := range resp.TableNames {
fmt.Println(tableName)
}
}
```
###### Compile and Execute
```sh
$ go run .
Tables:
tableOne
tableTwo
```
## Getting Help
Please use these community resources for getting help. We use the GitHub issues
for tracking bugs and feature requests.
* Ask us a [question](https://github.com/aws/aws-sdk-go-v2/discussions/new?category=q-a) or open a [discussion](https://github.com/aws/aws-sdk-go-v2/discussions/new?category=general).
* If you think you may have found a bug, please open an [issue](https://github.com/aws/aws-sdk-go-v2/issues/new/choose).
* Open a support ticket with [AWS Support](http://docs.aws.amazon.com/awssupport/latest/user/getting-started.html).
This SDK implements AWS service APIs. For general issues regarding the AWS services and their limitations, you may also take a look at the [Amazon Web Services Discussion Forums](https://forums.aws.amazon.com/).
### Opening Issues
If you encounter a bug with the AWS SDK for Go we would like to hear about it.
Search the [existing issues][Issues] and see
if others are also experiencing the same issue before opening a new issue. Please
include the version of AWS SDK for Go, Go language, and OS youre using. Please
also include reproduction case when appropriate.
The GitHub issues are intended for bug reports and feature requests. For help
and questions with using AWS SDK for Go please make use of the resources listed
in the [Getting Help](#getting-help) section.
Keeping the list of open issues lean will help us respond in a timely manner.
## Feedback and contributing
The v2 SDK will use GitHub [Issues] to track feature requests and issues with the SDK. In addition, we'll use GitHub [Projects] to track large tasks spanning multiple pull requests, such as refactoring the SDK's internal request lifecycle. You can provide feedback to us in several ways.
**GitHub issues**. To provide feedback or report bugs, file GitHub [Issues] on the SDK. This is the preferred mechanism to give feedback so that other users can engage in the conversation, +1 issues, etc. Issues you open will be evaluated, and included in our roadmap for the GA launch.
**Contributing**. You can open pull requests for fixes or additions to the AWS SDK for Go 2.0. All pull requests must be submitted under the Apache 2.0 license and will be reviewed by an SDK team member before being merged in. Accompanying unit tests, where possible, are appreciated.
## Resources
[SDK Developer Guide](https://aws.github.io/aws-sdk-go-v2/docs/) - Use this document to learn how to get started and
use the AWS SDK for Go V2.
[SDK Migration Guide](https://aws.github.io/aws-sdk-go-v2/docs/migrating/) - Use this document to learn how to migrate to V2 from the AWS SDK for Go.
[SDK API Reference Documentation](https://pkg.go.dev/mod/github.com/aws/aws-sdk-go-v2) - Use this
document to look up all API operation input and output parameters for AWS
services supported by the SDK. The API reference also includes documentation of
the SDK, and examples how to using the SDK, service client API operations, and
API operation require parameters.
[Service Documentation](https://aws.amazon.com/documentation/) - Use this
documentation to learn how to interface with AWS services. These guides are
great for getting started with a service, or when looking for more
information about a service. While this document is not required for coding,
services may supply helpful samples to look out for.
[Forum](https://forums.aws.amazon.com/forum.jspa?forumID=293) - Ask questions, get help, and give feedback
[Issues] - Report issues, submit pull requests, and get involved
(see [Apache 2.0 License][license])
[Dep]: https://github.com/golang/dep
[Issues]: https://github.com/aws/aws-sdk-go-v2/issues
[Projects]: https://github.com/aws/aws-sdk-go-v2/projects
[CHANGELOG]: https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md
[Amazon DynamoDB]: https://aws.amazon.com/dynamodb/
[design]: https://github.com/aws/aws-sdk-go-v2/blob/main/DESIGN.md
[license]: http://aws.amazon.com/apache2.0/

View file

@ -3,4 +3,4 @@
package aws
// goModuleVersion is the tagged release for this module
const goModuleVersion = "1.22.1"
const goModuleVersion = "1.22.2"

View file

@ -38,6 +38,7 @@ var RequiredSignedHeaders = Rules{
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": struct{}{},
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": struct{}{},
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": struct{}{},
"X-Amz-Expected-Bucket-Owner": struct{}{},
"X-Amz-Grant-Full-control": struct{}{},
"X-Amz-Grant-Read": struct{}{},
"X-Amz-Grant-Read-Acp": struct{}{},

View file

@ -1,12 +0,0 @@
version: 0.2
phases:
build:
commands:
- echo Build started on `date`
- export GOPATH=/go
- export SDK_CODEBUILD_ROOT=`pwd`
- make ci-test-no-generate
post_build:
commands:
- echo Build completed on `date`

View file

@ -1,58 +0,0 @@
#!/bin/bash
# looks for (and modreplaces if existing) a smithy-go branch matching the
# current branch name
#
# the loop will unfurl -*s off of the branch, e.g. sdk branch
# 'feat-foo-bar-baz' will match any of the following (in order):
# - feat-foo-bar-baz
# - feat-foo-bar
# - feat-foo
if [ -z "$SMITHY_GO_REPOSITORY" ]; then
SMITHY_GO_REPOSITORY=aws/smithy-go
fi
if [ -z "$RUNNER_TMPDIR" ]; then
echo env RUNNER_TMPDIR is required
exit 1
fi
branch=$(git branch --show-current)
if [ "$branch" == main ]; then
echo aws-sdk-go-v2 is on branch main, stop
exit 0
fi
# For PR workflows, only the triggering ref is checked out, which in isolation
# is not recognized as a branch by git. Use the specific workflow env instead.
if [ -z "$branch" ]; then
branch=$GITHUB_HEAD_REF
fi
if [ -n "$GIT_PAT" ]; then
repository=https://$GIT_PAT@github.com/$SMITHY_GO_REPOSITORY
else
repository=https://github.com/$SMITHY_GO_REPOSITORY
fi
echo on branch \"$branch\"
while [ -n "$branch" ] && [[ "$branch" == *-* ]]; do
echo looking for "$branch"...
git ls-remote --exit-code --heads "$repository" refs/heads/"$branch"
if [ "$?" == 0 ]; then
echo found "$branch"
matched_branch=$branch
break
fi
branch=${branch%-*}
done
if [ -z "$matched_branch" ]; then
echo found no matching smithy-go branch, stop
exit 0
fi
git clone -b "$matched_branch" "$repository" "$RUNNER_TMPDIR"/smithy-go
SMITHY_GO_SRC=$RUNNER_TMPDIR/smithy-go make gen-mod-replace-smithy-.

View file

@ -1,3 +1,30 @@
# v1.25.0 (2023-11-14)
* **Feature**: Add support for dynamic auth token from file and EKS container host in absolute/relative URIs in the HTTP credential provider.
* **Dependency Update**: Updated to the latest SDK module versions
# v1.24.0 (2023-11-13)
* **Feature**: Replace the legacy config parser with a modern, less-strict implementation. Parsing failures within a section will now simply ignore the invalid line rather than silently drop the entire section.
* **Dependency Update**: Updated to the latest SDK module versions
# v1.23.0 (2023-11-09.2)
* **Feature**: BREAKFIX: In order to support subproperty parsing, invalid property definitions must not be ignored
* **Dependency Update**: Updated to the latest SDK module versions
# v1.22.3 (2023-11-09)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.22.2 (2023-11-07)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.22.1 (2023-11-06)
* No change notes available for this release.
# v1.22.0 (2023-11-02)
* **Feature**: Add env and shared config settings for disabling IMDSv1 fallback.

View file

@ -165,7 +165,7 @@ func (cs configs) ResolveConfig(f func(configs []interface{}) error) error {
// or the custom data will be ignored by the resolvers and config loaders.
//
// cfg, err := config.LoadDefaultConfig( context.TODO(),
// WithSharedConfigProfile("test-profile"),
// config.WithSharedConfigProfile("test-profile"),
// )
// if err != nil {
// panic(fmt.Sprintf("failed loading config, %v", err))

View file

@ -3,4 +3,4 @@
package config
// goModuleVersion is the tagged release for this module
const goModuleVersion = "1.22.0"
const goModuleVersion = "1.25.0"

View file

@ -3,7 +3,10 @@ package config
import (
"context"
"fmt"
"io/ioutil"
"net"
"net/url"
"os"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
@ -21,11 +24,33 @@ import (
const (
// valid credential source values
credSourceEc2Metadata = "Ec2InstanceMetadata"
credSourceEnvironment = "Environment"
credSourceECSContainer = "EcsContainer"
credSourceEc2Metadata = "Ec2InstanceMetadata"
credSourceEnvironment = "Environment"
credSourceECSContainer = "EcsContainer"
httpProviderAuthFileEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE"
)
// direct representation of the IPv4 address for the ECS container
// "169.254.170.2"
var ecsContainerIPv4 net.IP = []byte{
169, 254, 170, 2,
}
// direct representation of the IPv4 address for the EKS container
// "169.254.170.23"
var eksContainerIPv4 net.IP = []byte{
169, 254, 170, 23,
}
// direct representation of the IPv6 address for the EKS container
// "fd00:ec2::23"
var eksContainerIPv6 net.IP = []byte{
0xFD, 0, 0xE, 0xC2,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0x23,
}
var (
ecsContainerEndpoint = "http://169.254.170.2" // not constant to allow for swapping during unit-testing
)
@ -222,6 +247,36 @@ func processCredentials(ctx context.Context, cfg *aws.Config, sharedConfig *Shar
return nil
}
// isAllowedHost allows host to be loopback or known ECS/EKS container IPs
//
// host can either be an IP address OR an unresolved hostname - resolution will
// be automatically performed in the latter case
func isAllowedHost(host string) (bool, error) {
if ip := net.ParseIP(host); ip != nil {
return isIPAllowed(ip), nil
}
addrs, err := lookupHostFn(host)
if err != nil {
return false, err
}
for _, addr := range addrs {
if ip := net.ParseIP(addr); ip == nil || !isIPAllowed(ip) {
return false, nil
}
}
return true, nil
}
func isIPAllowed(ip net.IP) bool {
return ip.IsLoopback() ||
ip.Equal(ecsContainerIPv4) ||
ip.Equal(eksContainerIPv4) ||
ip.Equal(eksContainerIPv6)
}
func resolveLocalHTTPCredProvider(ctx context.Context, cfg *aws.Config, endpointURL, authToken string, configs configs) error {
var resolveErr error
@ -232,10 +287,12 @@ func resolveLocalHTTPCredProvider(ctx context.Context, cfg *aws.Config, endpoint
host := parsed.Hostname()
if len(host) == 0 {
resolveErr = fmt.Errorf("unable to parse host from local HTTP cred provider URL")
} else if isLoopback, loopbackErr := isLoopbackHost(host); loopbackErr != nil {
resolveErr = fmt.Errorf("failed to resolve host %q, %v", host, loopbackErr)
} else if !isLoopback {
resolveErr = fmt.Errorf("invalid endpoint host, %q, only loopback hosts are allowed", host)
} else if parsed.Scheme == "http" {
if isAllowedHost, allowHostErr := isAllowedHost(host); allowHostErr != nil {
resolveErr = fmt.Errorf("failed to resolve host %q, %v", host, allowHostErr)
} else if !isAllowedHost {
resolveErr = fmt.Errorf("invalid endpoint host, %q, only loopback/ecs/eks hosts are allowed", host)
}
}
}
@ -252,6 +309,16 @@ func resolveHTTPCredProvider(ctx context.Context, cfg *aws.Config, url, authToke
if len(authToken) != 0 {
options.AuthorizationToken = authToken
}
if authFilePath := os.Getenv(httpProviderAuthFileEnvVar); authFilePath != "" {
options.AuthorizationTokenProvider = endpointcreds.TokenProviderFunc(func() (string, error) {
var contents []byte
var err error
if contents, err = ioutil.ReadFile(authFilePath); err != nil {
return "", fmt.Errorf("failed to read authorization token from %v: %v", authFilePath, err)
}
return string(contents), nil
})
}
options.APIOptions = cfg.APIOptions
if cfg.Retryer != nil {
options.Retryer = cfg.Retryer()

View file

@ -1,3 +1,11 @@
# v1.16.0 (2023-11-14)
* **Feature**: Add support for dynamic auth token from file and EKS container host in absolute/relative URIs in the HTTP credential provider.
# v1.15.2 (2023-11-09)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.15.1 (2023-11-02)
* **Dependency Update**: Updated to the latest SDK module versions

View file

@ -36,6 +36,7 @@ import (
"context"
"fmt"
"net/http"
"strings"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/credentials/endpointcreds/internal/client"
@ -81,7 +82,37 @@ type Options struct {
// Optional authorization token value if set will be used as the value of
// the Authorization header of the endpoint credential request.
//
// When constructed from environment, the provider will use the value of
// AWS_CONTAINER_AUTHORIZATION_TOKEN environment variable as the token
//
// Will be overridden if AuthorizationTokenProvider is configured
AuthorizationToken string
// Optional auth provider func to dynamically load the auth token from a file
// everytime a credential is retrieved
//
// When constructed from environment, the provider will read and use the content
// of the file pointed to by AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE environment variable
// as the auth token everytime credentials are retrieved
//
// Will override AuthorizationToken if configured
AuthorizationTokenProvider AuthTokenProvider
}
// AuthTokenProvider defines an interface to dynamically load a value to be passed
// for the Authorization header of a credentials request.
type AuthTokenProvider interface {
GetToken() (string, error)
}
// TokenProviderFunc is a func type implementing AuthTokenProvider interface
// and enables customizing token provider behavior
type TokenProviderFunc func() (string, error)
// GetToken func retrieves auth token according to TokenProviderFunc implementation
func (p TokenProviderFunc) GetToken() (string, error) {
return p()
}
// New returns a credentials Provider for retrieving AWS credentials
@ -132,5 +163,30 @@ func (p *Provider) Retrieve(ctx context.Context) (aws.Credentials, error) {
}
func (p *Provider) getCredentials(ctx context.Context) (*client.GetCredentialsOutput, error) {
return p.client.GetCredentials(ctx, &client.GetCredentialsInput{AuthorizationToken: p.options.AuthorizationToken})
authToken, err := p.resolveAuthToken()
if err != nil {
return nil, fmt.Errorf("resolve auth token: %v", err)
}
return p.client.GetCredentials(ctx, &client.GetCredentialsInput{
AuthorizationToken: authToken,
})
}
func (p *Provider) resolveAuthToken() (string, error) {
authToken := p.options.AuthorizationToken
var err error
if p.options.AuthorizationTokenProvider != nil {
authToken, err = p.options.AuthorizationTokenProvider.GetToken()
if err != nil {
return "", err
}
}
if strings.ContainsAny(authToken, "\r\n") {
return "", fmt.Errorf("authorization token contains invalid newline sequence")
}
return authToken, nil
}

View file

@ -3,4 +3,4 @@
package credentials
// goModuleVersion is the tagged release for this module
const goModuleVersion = "1.15.1"
const goModuleVersion = "1.16.0"

View file

@ -1,58 +0,0 @@
// Package sdk is the official AWS SDK v2 for the Go programming language.
//
// aws-sdk-go-v2 is the the v2 of the AWS SDK for the Go programming language.
//
// # Getting started
//
// The best way to get started working with the SDK is to use `go get` to add the
// SDK and desired service clients to your Go dependencies explicitly.
//
// go get github.com/aws/aws-sdk-go-v2
// go get github.com/aws/aws-sdk-go-v2/config
// go get github.com/aws/aws-sdk-go-v2/service/dynamodb
//
// # Hello AWS
//
// This example shows how you can use the v2 SDK to make an API request using the
// SDK's Amazon DynamoDB client.
//
// package main
//
// import (
// "context"
// "fmt"
// "log"
//
// "github.com/aws/aws-sdk-go-v2/aws"
// "github.com/aws/aws-sdk-go-v2/config"
// "github.com/aws/aws-sdk-go-v2/service/dynamodb"
// )
//
// func main() {
// // Using the SDK's default configuration, loading additional config
// // and credentials values from the environment variables, shared
// // credentials, and shared configuration files
// cfg, err := config.LoadDefaultConfig(context.TODO(),
// config.WithRegion("us-west-2"),
// )
// if err != nil {
// log.Fatalf("unable to load SDK config, %v", err)
// }
//
// // Using the Config value, create the DynamoDB client
// svc := dynamodb.NewFromConfig(cfg)
//
// // Build the request with its input parameters
// resp, err := svc.ListTables(context.TODO(), &dynamodb.ListTablesInput{
// Limit: aws.Int32(5),
// })
// if err != nil {
// log.Fatalf("failed to list tables, %v", err)
// }
//
// fmt.Println("Tables:")
// for _, tableName := range resp.TableNames {
// fmt.Println(tableName)
// }
// }
package sdk

View file

@ -1,3 +1,7 @@
# v1.14.3 (2023-11-09)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.14.2 (2023-11-02)
* No change notes available for this release.

View file

@ -3,4 +3,4 @@
package imds
// goModuleVersion is the tagged release for this module
const goModuleVersion = "1.14.2"
const goModuleVersion = "1.14.3"

View file

@ -1,3 +1,27 @@
# v1.13.7 (2023-11-14)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.6 (2023-11-13)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.5 (2023-11-09.2)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.4 (2023-11-09)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.3 (2023-11-07)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.2 (2023-11-06)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.13.1 (2023-11-02)
* **Dependency Update**: Updated to the latest SDK module versions

View file

@ -3,4 +3,4 @@
package manager
// goModuleVersion is the tagged release for this module
const goModuleVersion = "1.13.1"
const goModuleVersion = "1.13.7"

View file

@ -1,3 +1,7 @@
# v1.2.2 (2023-11-09)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.2.1 (2023-11-01)
* **Dependency Update**: Updated to the latest SDK module versions

View file

@ -3,4 +3,4 @@
package configsources
// goModuleVersion is the tagged release for this module
const goModuleVersion = "1.2.1"
const goModuleVersion = "1.2.2"

View file

@ -1,3 +1,7 @@
# v2.5.2 (2023-11-09)
* **Dependency Update**: Updated to the latest SDK module versions
# v2.5.1 (2023-11-01)
* **Dependency Update**: Updated to the latest SDK module versions

View file

@ -3,4 +3,4 @@
package endpoints
// goModuleVersion is the tagged release for this module
const goModuleVersion = "2.5.1"
const goModuleVersion = "2.5.2"

View file

@ -1,3 +1,19 @@
# v1.7.0 (2023-11-13)
* **Feature**: Replace the legacy config parser with a modern, less-strict implementation. Parsing failures within a section will now simply ignore the invalid line rather than silently drop the entire section.
# v1.6.0 (2023-11-09.2)
* **Feature**: BREAKFIX: In order to support subproperty parsing, invalid property definitions must not be ignored
# v1.5.2 (2023-11-09)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.5.1 (2023-11-07)
* **Bug Fix**: Fix subproperty performance regression
# v1.5.0 (2023-11-01)
* **Feature**: Adds support for configured endpoints via environment variables and the AWS shared configuration file.

View file

@ -1,120 +0,0 @@
package ini
// ASTKind represents different states in the parse table
// and the type of AST that is being constructed
type ASTKind int
// ASTKind* is used in the parse table to transition between
// the different states
const (
ASTKindNone = ASTKind(iota)
ASTKindStart
ASTKindExpr
ASTKindEqualExpr
ASTKindStatement
ASTKindSkipStatement
ASTKindExprStatement
ASTKindSectionStatement
ASTKindNestedSectionStatement
ASTKindCompletedNestedSectionStatement
ASTKindCommentStatement
ASTKindCompletedSectionStatement
)
func (k ASTKind) String() string {
switch k {
case ASTKindNone:
return "none"
case ASTKindStart:
return "start"
case ASTKindExpr:
return "expr"
case ASTKindStatement:
return "stmt"
case ASTKindSectionStatement:
return "section_stmt"
case ASTKindExprStatement:
return "expr_stmt"
case ASTKindCommentStatement:
return "comment"
case ASTKindNestedSectionStatement:
return "nested_section_stmt"
case ASTKindCompletedSectionStatement:
return "completed_stmt"
case ASTKindSkipStatement:
return "skip"
default:
return ""
}
}
// AST interface allows us to determine what kind of node we
// are on and casting may not need to be necessary.
//
// The root is always the first node in Children
type AST struct {
Kind ASTKind
Root Token
RootToken bool
Children []AST
}
func newAST(kind ASTKind, root AST, children ...AST) AST {
return AST{
Kind: kind,
Children: append([]AST{root}, children...),
}
}
func newASTWithRootToken(kind ASTKind, root Token, children ...AST) AST {
return AST{
Kind: kind,
Root: root,
RootToken: true,
Children: children,
}
}
// AppendChild will append to the list of children an AST has.
func (a *AST) AppendChild(child AST) {
a.Children = append(a.Children, child)
}
// GetRoot will return the root AST which can be the first entry
// in the children list or a token.
func (a *AST) GetRoot() AST {
if a.RootToken {
return *a
}
if len(a.Children) == 0 {
return AST{}
}
return a.Children[0]
}
// GetChildren will return the current AST's list of children
func (a *AST) GetChildren() []AST {
if len(a.Children) == 0 {
return []AST{}
}
if a.RootToken {
return a.Children
}
return a.Children[1:]
}
// SetChildren will set and override all children of the AST.
func (a *AST) SetChildren(children []AST) {
if a.RootToken {
a.Children = children
} else {
a.Children = append(a.Children[:1], children...)
}
}
// Start is used to indicate the starting state of the parse table.
var Start = newAST(ASTKindStart, AST{})

View file

@ -1,11 +0,0 @@
package ini
var commaRunes = []rune(",")
func isComma(b rune) bool {
return b == ','
}
func newCommaToken() Token {
return newToken(TokenComma, commaRunes, NoneType)
}

View file

@ -1,35 +0,0 @@
package ini
// isComment will return whether or not the next byte(s) is a
// comment.
func isComment(b []rune) bool {
if len(b) == 0 {
return false
}
switch b[0] {
case ';':
return true
case '#':
return true
}
return false
}
// newCommentToken will create a comment token and
// return how many bytes were read.
func newCommentToken(b []rune) (Token, int, error) {
i := 0
for ; i < len(b); i++ {
if b[i] == '\n' {
break
}
if len(b)-i > 2 && b[i] == '\r' && b[i+1] == '\n' {
break
}
}
return newToken(TokenComment, b[:i], NoneType), i, nil
}

View file

@ -1,6 +0,0 @@
package ini
import (
// internal/ini module was carved out of this module
_ "github.com/aws/aws-sdk-go-v2"
)

View file

@ -1,43 +0,0 @@
// Package ini is an LL(1) parser for configuration files.
//
// Example:
// sections, err := ini.OpenFile("/path/to/file")
// if err != nil {
// panic(err)
// }
//
// profile := "foo"
// section, ok := sections.GetSection(profile)
// if !ok {
// fmt.Printf("section %q could not be found", profile)
// }
//
// Below is the BNF that describes this parser
//
// Grammar:
// stmt -> section | stmt'
// stmt' -> epsilon | expr
// expr -> value (stmt)* | equal_expr (stmt)*
// equal_expr -> value ( ':' | '=' ) equal_expr'
// equal_expr' -> number | string | quoted_string
// quoted_string -> " quoted_string'
// quoted_string' -> string quoted_string_end
// quoted_string_end -> "
//
// section -> [ section'
// section' -> section_value section_close
// section_value -> number | string_subset | boolean | quoted_string_subset
// quoted_string_subset -> " quoted_string_subset'
// quoted_string_subset' -> string_subset quoted_string_end
// quoted_string_subset -> "
// section_close -> ]
//
// value -> number | string_subset | boolean
// string -> ? UTF-8 Code-Points except '\n' (U+000A) and '\r\n' (U+000D U+000A) ?
// string_subset -> ? Code-points excepted by <string> grammar except ':' (U+003A), '=' (U+003D), '[' (U+005B), and ']' (U+005D) ?
//
// SkipState will skip (NL WS)+
//
// comment -> # comment' | ; comment'
// comment' -> epsilon | value
package ini

View file

@ -1,4 +0,0 @@
package ini
// emptyToken is used to satisfy the Token interface
var emptyToken = newToken(TokenNone, []rune{}, NoneType)

View file

@ -1,24 +0,0 @@
package ini
// newExpression will return an expression AST.
// Expr represents an expression
//
// grammar:
// expr -> string | number
func newExpression(tok Token) AST {
return newASTWithRootToken(ASTKindExpr, tok)
}
func newEqualExpr(left AST, tok Token) AST {
return newASTWithRootToken(ASTKindEqualExpr, tok, left)
}
// EqualExprKey will return a LHS value in the equal expr
func EqualExprKey(ast AST) string {
children := ast.GetChildren()
if len(children) == 0 || ast.Kind != ASTKindEqualExpr {
return ""
}
return string(children[0].Root.Raw())
}

View file

@ -3,4 +3,4 @@
package ini
// goModuleVersion is the tagged release for this module
const goModuleVersion = "1.5.0"
const goModuleVersion = "1.7.0"

View file

@ -1,13 +1,26 @@
// Package ini implements parsing of the AWS shared config file.
//
// Example:
// sections, err := ini.OpenFile("/path/to/file")
// if err != nil {
// panic(err)
// }
//
// profile := "foo"
// section, ok := sections.GetSection(profile)
// if !ok {
// fmt.Printf("section %q could not be found", profile)
// }
package ini
import (
"fmt"
"io"
"os"
"strings"
)
// OpenFile takes a path to a given file, and will open and parse
// that file.
// OpenFile parses shared config from the given file path.
func OpenFile(path string) (sections Sections, err error) {
f, oerr := os.Open(path)
if oerr != nil {
@ -26,33 +39,18 @@ func OpenFile(path string) (sections Sections, err error) {
return Parse(f, path)
}
// Parse will parse the given file using the shared config
// visitor.
func Parse(f io.Reader, path string) (Sections, error) {
tree, err := ParseAST(f)
// Parse parses shared config from the given reader.
func Parse(r io.Reader, path string) (Sections, error) {
contents, err := io.ReadAll(r)
if err != nil {
return Sections{}, err
return Sections{}, fmt.Errorf("read all: %v", err)
}
v := NewDefaultVisitor(path)
if err = Walk(tree, v); err != nil {
return Sections{}, err
}
return v.Sections, nil
}
// ParseBytes will parse the given bytes and return the parsed sections.
func ParseBytes(b []byte) (Sections, error) {
tree, err := ParseASTBytes(b)
lines := strings.Split(string(contents), "\n")
tokens, err := tokenize(lines)
if err != nil {
return Sections{}, err
return Sections{}, fmt.Errorf("tokenize: %v", err)
}
v := NewDefaultVisitor("")
if err = Walk(tree, v); err != nil {
return Sections{}, err
}
return v.Sections, nil
return parse(tokens, path), nil
}

View file

@ -1,161 +0,0 @@
package ini
import (
"bytes"
"io"
"io/ioutil"
)
// TokenType represents the various different tokens types
type TokenType int
func (t TokenType) String() string {
switch t {
case TokenNone:
return "none"
case TokenLit:
return "literal"
case TokenSep:
return "sep"
case TokenOp:
return "op"
case TokenWS:
return "ws"
case TokenNL:
return "newline"
case TokenComment:
return "comment"
case TokenComma:
return "comma"
default:
return ""
}
}
// TokenType enums
const (
TokenNone = TokenType(iota)
TokenLit
TokenSep
TokenComma
TokenOp
TokenWS
TokenNL
TokenComment
)
type iniLexer struct{}
// Tokenize will return a list of tokens during lexical analysis of the
// io.Reader.
func (l *iniLexer) Tokenize(r io.Reader) ([]Token, error) {
b, err := ioutil.ReadAll(r)
if err != nil {
return nil, &UnableToReadFile{Err: err}
}
return l.tokenize(b)
}
func (l *iniLexer) tokenize(b []byte) ([]Token, error) {
runes := bytes.Runes(b)
var err error
n := 0
tokenAmount := countTokens(runes)
tokens := make([]Token, tokenAmount)
count := 0
for len(runes) > 0 && count < tokenAmount {
switch {
case isSubProperty(runes):
tokens[count], n, err = newLitToken(runes)
case isWhitespace(runes[0]):
tokens[count], n, err = newWSToken(runes)
case isComma(runes[0]):
tokens[count], n = newCommaToken(), 1
case isComment(runes):
tokens[count], n, err = newCommentToken(runes)
case isNewline(runes):
tokens[count], n, err = newNewlineToken(runes)
case isSep(runes):
tokens[count], n, err = newSepToken(runes)
case isOp(runes):
tokens[count], n, err = newOpToken(runes)
default:
tokens[count], n, err = newLitToken(runes)
}
if err != nil {
return nil, err
}
count++
runes = runes[n:]
}
return tokens[:count], nil
}
func countTokens(runes []rune) int {
count, n := 0, 0
var err error
for len(runes) > 0 {
switch {
case isSubProperty(runes):
_, n, err = newLitToken(runes)
case isWhitespace(runes[0]):
_, n, err = newWSToken(runes)
case isComma(runes[0]):
_, n = newCommaToken(), 1
case isComment(runes):
_, n, err = newCommentToken(runes)
case isNewline(runes):
_, n, err = newNewlineToken(runes)
case isSep(runes):
_, n, err = newSepToken(runes)
case isOp(runes):
_, n, err = newOpToken(runes)
default:
_, n, err = newLitToken(runes)
}
if err != nil {
return 0
}
count++
runes = runes[n:]
}
return count + 1
}
// Token indicates a metadata about a given value.
type Token struct {
t TokenType
ValueType ValueType
base int
raw []rune
}
var emptyValue = Value{}
func newToken(t TokenType, raw []rune, v ValueType) Token {
return Token{
t: t,
raw: raw,
ValueType: v,
}
}
// Raw return the raw runes that were consumed
func (tok Token) Raw() []rune {
return tok.raw
}
// Type returns the token type
func (tok Token) Type() TokenType {
return tok.t
}

View file

@ -1,349 +0,0 @@
package ini
import (
"fmt"
"io"
)
// ParseState represents the current state of the parser.
type ParseState uint
// State enums for the parse table
const (
InvalidState ParseState = iota
// stmt -> value stmt'
StatementState
// stmt' -> MarkComplete | op stmt
StatementPrimeState
// value -> number | string | boolean | quoted_string
ValueState
// section -> [ section'
OpenScopeState
// section' -> value section_close
SectionState
// section_close -> ]
CloseScopeState
// SkipState will skip (NL WS)+
SkipState
// SkipTokenState will skip any token and push the previous
// state onto the stack.
SkipTokenState
// comment -> # comment' | ; comment'
// comment' -> MarkComplete | value
CommentState
// MarkComplete state will complete statements and move that
// to the completed AST list
MarkCompleteState
// TerminalState signifies that the tokens have been fully parsed
TerminalState
)
// parseTable is a state machine to dictate the grammar above.
var parseTable = map[ASTKind]map[TokenType]ParseState{
ASTKindStart: {
TokenLit: StatementState,
TokenSep: OpenScopeState,
TokenWS: SkipTokenState,
TokenNL: SkipTokenState,
TokenComment: CommentState,
TokenNone: TerminalState,
},
ASTKindCommentStatement: {
TokenLit: StatementState,
TokenSep: OpenScopeState,
TokenWS: SkipTokenState,
TokenNL: SkipTokenState,
TokenComment: CommentState,
TokenNone: MarkCompleteState,
},
ASTKindExpr: {
TokenOp: StatementPrimeState,
TokenLit: ValueState,
TokenSep: OpenScopeState,
TokenWS: ValueState,
TokenNL: SkipState,
TokenComment: CommentState,
TokenNone: MarkCompleteState,
},
ASTKindEqualExpr: {
TokenLit: ValueState,
TokenSep: ValueState,
TokenOp: ValueState,
TokenWS: SkipTokenState,
TokenNL: SkipState,
},
ASTKindStatement: {
TokenLit: SectionState,
TokenSep: CloseScopeState,
TokenWS: SkipTokenState,
TokenNL: SkipTokenState,
TokenComment: CommentState,
TokenNone: MarkCompleteState,
},
ASTKindExprStatement: {
TokenLit: ValueState,
TokenSep: ValueState,
TokenOp: ValueState,
TokenWS: ValueState,
TokenNL: MarkCompleteState,
TokenComment: CommentState,
TokenNone: TerminalState,
TokenComma: SkipState,
},
ASTKindSectionStatement: {
TokenLit: SectionState,
TokenOp: SectionState,
TokenSep: CloseScopeState,
TokenWS: SectionState,
TokenNL: SkipTokenState,
},
ASTKindCompletedSectionStatement: {
TokenWS: SkipTokenState,
TokenNL: SkipTokenState,
TokenLit: StatementState,
TokenSep: OpenScopeState,
TokenComment: CommentState,
TokenNone: MarkCompleteState,
},
ASTKindSkipStatement: {
TokenLit: StatementState,
TokenSep: OpenScopeState,
TokenWS: SkipTokenState,
TokenNL: SkipTokenState,
TokenComment: CommentState,
TokenNone: TerminalState,
},
}
// ParseAST will parse input from an io.Reader using
// an LL(1) parser.
func ParseAST(r io.Reader) ([]AST, error) {
lexer := iniLexer{}
tokens, err := lexer.Tokenize(r)
if err != nil {
return []AST{}, err
}
return parse(tokens)
}
// ParseASTBytes will parse input from a byte slice using
// an LL(1) parser.
func ParseASTBytes(b []byte) ([]AST, error) {
lexer := iniLexer{}
tokens, err := lexer.tokenize(b)
if err != nil {
return []AST{}, err
}
return parse(tokens)
}
func parse(tokens []Token) ([]AST, error) {
start := Start
stack := newParseStack(3, len(tokens))
stack.Push(start)
s := newSkipper()
loop:
for stack.Len() > 0 {
k := stack.Pop()
var tok Token
if len(tokens) == 0 {
// this occurs when all the tokens have been processed
// but reduction of what's left on the stack needs to
// occur.
tok = emptyToken
} else {
tok = tokens[0]
}
step := parseTable[k.Kind][tok.Type()]
if s.ShouldSkip(tok) {
// being in a skip state with no tokens will break out of
// the parse loop since there is nothing left to process.
if len(tokens) == 0 {
break loop
}
// if should skip is true, we skip the tokens until should skip is set to false.
step = SkipTokenState
}
switch step {
case TerminalState:
// Finished parsing. Push what should be the last
// statement to the stack. If there is anything left
// on the stack, an error in parsing has occurred.
if k.Kind != ASTKindStart {
stack.MarkComplete(k)
}
break loop
case SkipTokenState:
// When skipping a token, the previous state was popped off the stack.
// To maintain the correct state, the previous state will be pushed
// onto the stack.
stack.Push(k)
case StatementState:
if k.Kind != ASTKindStart {
stack.MarkComplete(k)
}
expr := newExpression(tok)
stack.Push(expr)
case StatementPrimeState:
if tok.Type() != TokenOp {
stack.MarkComplete(k)
continue
}
if k.Kind != ASTKindExpr {
return nil, NewParseError(
fmt.Sprintf("invalid expression: expected Expr type, but found %T type", k),
)
}
k = trimSpaces(k)
expr := newEqualExpr(k, tok)
stack.Push(expr)
case ValueState:
// ValueState requires the previous state to either be an equal expression
// or an expression statement.
switch k.Kind {
case ASTKindEqualExpr:
// assigning a value to some key
k.AppendChild(newExpression(tok))
stack.Push(newExprStatement(k))
case ASTKindExpr:
k.Root.raw = append(k.Root.raw, tok.Raw()...)
stack.Push(k)
case ASTKindExprStatement:
root := k.GetRoot()
children := root.GetChildren()
if len(children) == 0 {
return nil, NewParseError(
fmt.Sprintf("invalid expression: AST contains no children %s", k.Kind),
)
}
rhs := children[len(children)-1]
if rhs.Root.ValueType != QuotedStringType {
rhs.Root.ValueType = StringType
rhs.Root.raw = append(rhs.Root.raw, tok.Raw()...)
}
children[len(children)-1] = rhs
root.SetChildren(children)
stack.Push(k)
}
case OpenScopeState:
if !runeCompare(tok.Raw(), openBrace) {
return nil, NewParseError("expected '['")
}
// If OpenScopeState is not at the start, we must mark the previous ast as complete
//
// for example: if previous ast was a skip statement;
// we should mark it as complete before we create a new statement
if k.Kind != ASTKindStart {
stack.MarkComplete(k)
}
stmt := newStatement()
stack.Push(stmt)
case CloseScopeState:
if !runeCompare(tok.Raw(), closeBrace) {
return nil, NewParseError("expected ']'")
}
k = trimSpaces(k)
stack.Push(newCompletedSectionStatement(k))
case SectionState:
var stmt AST
switch k.Kind {
case ASTKindStatement:
// If there are multiple literals inside of a scope declaration,
// then the current token's raw value will be appended to the Name.
//
// This handles cases like [ profile default ]
//
// k will represent a SectionStatement with the children representing
// the label of the section
stmt = newSectionStatement(tok)
case ASTKindSectionStatement:
k.Root.raw = append(k.Root.raw, tok.Raw()...)
stmt = k
default:
return nil, NewParseError(
fmt.Sprintf("invalid statement: expected statement: %v", k.Kind),
)
}
stack.Push(stmt)
case MarkCompleteState:
if k.Kind != ASTKindStart {
stack.MarkComplete(k)
}
if stack.Len() == 0 {
stack.Push(start)
}
case SkipState:
stack.Push(newSkipStatement(k))
s.Skip()
case CommentState:
if k.Kind == ASTKindStart {
stack.Push(k)
} else {
stack.MarkComplete(k)
}
stmt := newCommentStatement(tok)
stack.Push(stmt)
default:
return nil, NewParseError(
fmt.Sprintf("invalid state with ASTKind %v and TokenType %v",
k.Kind, tok.Type()))
}
if len(tokens) > 0 {
tokens = tokens[1:]
}
}
// this occurs when a statement has not been completed
if stack.top > 1 {
return nil, NewParseError(fmt.Sprintf("incomplete ini expression"))
}
// returns a sublist which exludes the start symbol
return stack.List(), nil
}
// trimSpaces will trim spaces on the left and right hand side of
// the literal.
func trimSpaces(k AST) AST {
// trim left hand side of spaces
for i := 0; i < len(k.Root.raw); i++ {
if !isWhitespace(k.Root.raw[i]) {
break
}
k.Root.raw = k.Root.raw[1:]
i--
}
// trim right hand side of spaces
for i := len(k.Root.raw) - 1; i >= 0; i-- {
if !isWhitespace(k.Root.raw[i]) {
break
}
k.Root.raw = k.Root.raw[:len(k.Root.raw)-1]
}
return k
}

View file

@ -1,267 +0,0 @@
package ini
import (
"fmt"
"strconv"
"strings"
"unicode"
)
var (
runesTrue = []rune("true")
runesFalse = []rune("false")
)
// isCaselessLitValue is a caseless value comparison, assumes want is already lower-cased for efficiency.
func isCaselessLitValue(want, have []rune) bool {
if len(have) < len(want) {
return false
}
for i := 0; i < len(want); i++ {
if want[i] != unicode.ToLower(have[i]) {
return false
}
}
return true
}
func isValid(b []rune) (bool, int, error) {
if len(b) == 0 {
// TODO: should probably return an error
return false, 0, nil
}
return isValidRune(b[0]), 1, nil
}
func isValidRune(r rune) bool {
return r != ':' && r != '=' && r != '[' && r != ']' && r != ' ' && r != '\n'
}
// ValueType is an enum that will signify what type
// the Value is
type ValueType int
func (v ValueType) String() string {
switch v {
case NoneType:
return "NONE"
case StringType:
return "STRING"
}
return ""
}
// ValueType enums
const (
NoneType = ValueType(iota)
StringType
QuotedStringType
)
// Value is a union container
type Value struct {
Type ValueType
raw []rune
str string
mp map[string]string
}
func newValue(t ValueType, base int, raw []rune) (Value, error) {
v := Value{
Type: t,
raw: raw,
}
switch t {
case StringType:
v.str = string(raw)
if isSubProperty(raw) {
v.mp = v.MapValue()
}
case QuotedStringType:
v.str = string(raw[1 : len(raw)-1])
}
return v, nil
}
// NewStringValue returns a Value type generated using a string input.
func NewStringValue(str string) (Value, error) {
return newValue(StringType, 10, []rune(str))
}
func (v Value) String() string {
switch v.Type {
case StringType:
return fmt.Sprintf("string: %s", string(v.raw))
case QuotedStringType:
return fmt.Sprintf("quoted string: %s", string(v.raw))
default:
return "union not set"
}
}
func newLitToken(b []rune) (Token, int, error) {
n := 0
var err error
token := Token{}
if b[0] == '"' {
n, err = getStringValue(b)
if err != nil {
return token, n, err
}
token = newToken(TokenLit, b[:n], QuotedStringType)
} else if isSubProperty(b) {
offset := 0
end, err := getSubProperty(b, offset)
if err != nil {
return token, n, err
}
token = newToken(TokenLit, b[offset:end], StringType)
n = end
} else {
n, err = getValue(b)
token = newToken(TokenLit, b[:n], StringType)
}
return token, n, err
}
func isSubProperty(runes []rune) bool {
// needs at least
// (1) newline (2) whitespace (3) literal
if len(runes) < 3 {
return false
}
// must have an equal expression
split := strings.FieldsFunc(string(runes), func(r rune) bool {
return isOp([]rune{r})
})
if len(split) < 2 {
return false
}
// must start with a new line
if !isNewline(runes) {
return false
}
_, n, err := newNewlineToken(runes)
if err != nil {
return false
}
// whitespace must follow newline
return isWhitespace(runes[n])
}
// getSubProperty pulls all subproperties and terminates when
// it hits a newline that is not the start of another subproperty.
// offset allows for removal of leading newline and whitespace
// characters
func getSubProperty(runes []rune, offset int) (int, error) {
for idx, val := range runes[offset:] {
if val == '\n' && !isSubProperty(runes[offset+idx:]) {
return offset + idx, nil
}
}
return 0, fmt.Errorf("no sub property")
}
// MapValue returns a map value for sub properties
func (v Value) MapValue() map[string]string {
newlineParts := strings.Split(string(v.raw), "\n")
mp := make(map[string]string)
for _, part := range newlineParts {
operandParts := strings.Split(part, "=")
if len(operandParts) < 2 {
continue
}
key := strings.TrimSpace(operandParts[0])
val := strings.TrimSpace(operandParts[1])
mp[key] = val
}
return mp
}
// IntValue returns an integer value
func (v Value) IntValue() (int64, bool) {
i, err := strconv.ParseInt(string(v.raw), 0, 64)
if err != nil {
return 0, false
}
return i, true
}
// FloatValue returns a float value
func (v Value) FloatValue() (float64, bool) {
f, err := strconv.ParseFloat(string(v.raw), 64)
if err != nil {
return 0, false
}
return f, true
}
// BoolValue returns a bool value
func (v Value) BoolValue() (bool, bool) {
// we don't use ParseBool as it recognizes more than what we've
// historically supported
if isCaselessLitValue(runesTrue, v.raw) {
return true, true
} else if isCaselessLitValue(runesFalse, v.raw) {
return false, true
}
return false, false
}
func isTrimmable(r rune) bool {
switch r {
case '\n', ' ':
return true
}
return false
}
// StringValue returns the string value
func (v Value) StringValue() string {
switch v.Type {
case StringType:
return strings.TrimFunc(string(v.raw), isTrimmable)
case QuotedStringType:
// preserve all characters in the quotes
return string(removeEscapedCharacters(v.raw[1 : len(v.raw)-1]))
default:
return strings.TrimFunc(string(v.raw), isTrimmable)
}
}
func contains(runes []rune, c rune) bool {
for i := 0; i < len(runes); i++ {
if runes[i] == c {
return true
}
}
return false
}
func runeCompare(v1 []rune, v2 []rune) bool {
if len(v1) != len(v2) {
return false
}
for i := 0; i < len(v1); i++ {
if v1[i] != v2[i] {
return false
}
}
return true
}

View file

@ -1,30 +0,0 @@
package ini
func isNewline(b []rune) bool {
if len(b) == 0 {
return false
}
if b[0] == '\n' {
return true
}
if len(b) < 2 {
return false
}
return b[0] == '\r' && b[1] == '\n'
}
func newNewlineToken(b []rune) (Token, int, error) {
i := 1
if b[0] == '\r' && isNewline(b[1:]) {
i++
}
if !isNewline([]rune(b[:i])) {
return emptyToken, 0, NewParseError("invalid new line token")
}
return newToken(TokenNL, b[:i], NoneType), i, nil
}

View file

@ -1,39 +0,0 @@
package ini
import (
"fmt"
)
var (
equalOp = []rune("=")
equalColonOp = []rune(":")
)
func isOp(b []rune) bool {
if len(b) == 0 {
return false
}
switch b[0] {
case '=':
return true
case ':':
return true
default:
return false
}
}
func newOpToken(b []rune) (Token, int, error) {
tok := Token{}
switch b[0] {
case '=':
tok = newToken(TokenOp, equalOp, NoneType)
case ':':
tok = newToken(TokenOp, equalColonOp, NoneType)
default:
return tok, 0, NewParseError(fmt.Sprintf("unexpected op type, %v", b[0]))
}
return tok, 1, nil
}

View file

@ -0,0 +1,109 @@
package ini
import (
"fmt"
"strings"
)
func parse(tokens []lineToken, path string) Sections {
parser := &parser{
path: path,
sections: NewSections(),
}
parser.parse(tokens)
return parser.sections
}
type parser struct {
csection, ckey string // current state
path string // source file path
sections Sections // parse result
}
func (p *parser) parse(tokens []lineToken) {
for _, otok := range tokens {
switch tok := otok.(type) {
case *lineTokenProfile:
p.handleProfile(tok)
case *lineTokenProperty:
p.handleProperty(tok)
case *lineTokenSubProperty:
p.handleSubProperty(tok)
case *lineTokenContinuation:
p.handleContinuation(tok)
}
}
}
func (p *parser) handleProfile(tok *lineTokenProfile) {
name := tok.Name
if tok.Type != "" {
name = fmt.Sprintf("%s %s", tok.Type, tok.Name)
}
p.ckey = ""
p.csection = name
if _, ok := p.sections.container[name]; !ok {
p.sections.container[name] = NewSection(name)
}
}
func (p *parser) handleProperty(tok *lineTokenProperty) {
if p.csection == "" {
return // LEGACY: don't error on "global" properties
}
p.ckey = tok.Key
if _, ok := p.sections.container[p.csection].values[tok.Key]; ok {
section := p.sections.container[p.csection]
section.Logs = append(p.sections.container[p.csection].Logs,
fmt.Sprintf(
"For profile: %v, overriding %v value, with a %v value found in a duplicate profile defined later in the same file %v. \n",
p.csection, tok.Key, tok.Key, p.path,
),
)
p.sections.container[p.csection] = section
}
p.sections.container[p.csection].values[tok.Key] = Value{
str: tok.Value,
}
p.sections.container[p.csection].SourceFile[tok.Key] = p.path
}
func (p *parser) handleSubProperty(tok *lineTokenSubProperty) {
if p.csection == "" {
return // LEGACY: don't error on "global" properties
}
if p.ckey == "" || p.sections.container[p.csection].values[p.ckey].str != "" {
// This is an "orphaned" subproperty, either because it's at
// the beginning of a section or because the last property's
// value isn't empty. Either way we're lenient here and
// "promote" this to a normal property.
p.handleProperty(&lineTokenProperty{
Key: tok.Key,
Value: strings.TrimSpace(trimComment(tok.Value)),
})
return
}
if p.sections.container[p.csection].values[p.ckey].mp == nil {
p.sections.container[p.csection].values[p.ckey] = Value{
mp: map[string]string{},
}
}
p.sections.container[p.csection].values[p.ckey].mp[tok.Key] = tok.Value
}
func (p *parser) handleContinuation(tok *lineTokenContinuation) {
if p.ckey == "" {
return
}
value, _ := p.sections.container[p.csection].values[p.ckey]
if value.str != "" && value.mp == nil {
value.str = fmt.Sprintf("%s\n%s", value.str, tok.Value)
}
p.sections.container[p.csection].values[p.ckey] = value
}

View file

@ -1,19 +0,0 @@
package ini
// ParseError is an error which is returned during any part of
// the parsing process.
type ParseError struct {
msg string
}
// NewParseError will return a new ParseError where message
// is the description of the error.
func NewParseError(message string) *ParseError {
return &ParseError{
msg: message,
}
}
func (err *ParseError) Error() string {
return err.msg
}

View file

@ -1,60 +0,0 @@
package ini
import (
"bytes"
"fmt"
)
// ParseStack is a stack that contains a container, the stack portion,
// and the list which is the list of ASTs that have been successfully
// parsed.
type ParseStack struct {
top int
container []AST
list []AST
index int
}
func newParseStack(sizeContainer, sizeList int) ParseStack {
return ParseStack{
container: make([]AST, sizeContainer),
list: make([]AST, sizeList),
}
}
// Pop will return and truncate the last container element.
func (s *ParseStack) Pop() AST {
s.top--
return s.container[s.top]
}
// Push will add the new AST to the container
func (s *ParseStack) Push(ast AST) {
s.container[s.top] = ast
s.top++
}
// MarkComplete will append the AST to the list of completed statements
func (s *ParseStack) MarkComplete(ast AST) {
s.list[s.index] = ast
s.index++
}
// List will return the completed statements
func (s ParseStack) List() []AST {
return s.list[:s.index]
}
// Len will return the length of the container
func (s *ParseStack) Len() int {
return s.top
}
func (s ParseStack) String() string {
buf := bytes.Buffer{}
for i, node := range s.list {
buf.WriteString(fmt.Sprintf("%d: %v\n", i+1, node))
}
return buf.String()
}

View file

@ -0,0 +1,157 @@
package ini
import (
"sort"
)
// Sections is a map of Section structures that represent
// a configuration.
type Sections struct {
container map[string]Section
}
// NewSections returns empty ini Sections
func NewSections() Sections {
return Sections{
container: make(map[string]Section, 0),
}
}
// GetSection will return section p. If section p does not exist,
// false will be returned in the second parameter.
func (t Sections) GetSection(p string) (Section, bool) {
v, ok := t.container[p]
return v, ok
}
// HasSection denotes if Sections consist of a section with
// provided name.
func (t Sections) HasSection(p string) bool {
_, ok := t.container[p]
return ok
}
// SetSection sets a section value for provided section name.
func (t Sections) SetSection(p string, v Section) Sections {
t.container[p] = v
return t
}
// DeleteSection deletes a section entry/value for provided section name./
func (t Sections) DeleteSection(p string) {
delete(t.container, p)
}
// values represents a map of union values.
type values map[string]Value
// List will return a list of all sections that were successfully
// parsed.
func (t Sections) List() []string {
keys := make([]string, len(t.container))
i := 0
for k := range t.container {
keys[i] = k
i++
}
sort.Strings(keys)
return keys
}
// Section contains a name and values. This represent
// a sectioned entry in a configuration file.
type Section struct {
// Name is the Section profile name
Name string
// values are the values within parsed profile
values values
// Errors is the list of errors
Errors []error
// Logs is the list of logs
Logs []string
// SourceFile is the INI Source file from where this section
// was retrieved. They key is the property, value is the
// source file the property was retrieved from.
SourceFile map[string]string
}
// NewSection returns an initialize section for the name
func NewSection(name string) Section {
return Section{
Name: name,
values: values{},
SourceFile: map[string]string{},
}
}
// List will return a list of all
// services in values
func (t Section) List() []string {
keys := make([]string, len(t.values))
i := 0
for k := range t.values {
keys[i] = k
i++
}
sort.Strings(keys)
return keys
}
// UpdateSourceFile updates source file for a property to provided filepath.
func (t Section) UpdateSourceFile(property string, filepath string) {
t.SourceFile[property] = filepath
}
// UpdateValue updates value for a provided key with provided value
func (t Section) UpdateValue(k string, v Value) error {
t.values[k] = v
return nil
}
// Has will return whether or not an entry exists in a given section
func (t Section) Has(k string) bool {
_, ok := t.values[k]
return ok
}
// ValueType will returned what type the union is set to. If
// k was not found, the NoneType will be returned.
func (t Section) ValueType(k string) (ValueType, bool) {
v, ok := t.values[k]
return v.Type, ok
}
// Bool returns a bool value at k
func (t Section) Bool(k string) (bool, bool) {
return t.values[k].BoolValue()
}
// Int returns an integer value at k
func (t Section) Int(k string) (int64, bool) {
return t.values[k].IntValue()
}
// Map returns a map value at k
func (t Section) Map(k string) map[string]string {
return t.values[k].MapValue()
}
// Float64 returns a float value at k
func (t Section) Float64(k string) (float64, bool) {
return t.values[k].FloatValue()
}
// String returns the string value at k
func (t Section) String(k string) string {
_, ok := t.values[k]
if !ok {
return ""
}
return t.values[k].StringValue()
}

View file

@ -1,41 +0,0 @@
package ini
import (
"fmt"
)
var (
emptyRunes = []rune{}
)
func isSep(b []rune) bool {
if len(b) == 0 {
return false
}
switch b[0] {
case '[', ']':
return true
default:
return false
}
}
var (
openBrace = []rune("[")
closeBrace = []rune("]")
)
func newSepToken(b []rune) (Token, int, error) {
tok := Token{}
switch b[0] {
case '[':
tok = newToken(TokenSep, openBrace, NoneType)
case ']':
tok = newToken(TokenSep, closeBrace, NoneType)
default:
return tok, 0, NewParseError(fmt.Sprintf("unexpected sep type, %v", b[0]))
}
return tok, 1, nil
}

View file

@ -1,45 +0,0 @@
package ini
// skipper is used to skip certain blocks of an ini file.
// Currently skipper is used to skip nested blocks of ini
// files. See example below
//
// [ foo ]
// nested = ; this section will be skipped
// a=b
// c=d
// bar=baz ; this will be included
type skipper struct {
shouldSkip bool
TokenSet bool
prevTok Token
}
func newSkipper() skipper {
return skipper{
prevTok: emptyToken,
}
}
func (s *skipper) ShouldSkip(tok Token) bool {
// should skip state will be modified only if previous token was new line (NL);
// and the current token is not WhiteSpace (WS).
if s.shouldSkip &&
s.prevTok.Type() == TokenNL &&
tok.Type() != TokenWS {
s.Continue()
return false
}
s.prevTok = tok
return s.shouldSkip
}
func (s *skipper) Skip() {
s.shouldSkip = true
}
func (s *skipper) Continue() {
s.shouldSkip = false
s.prevTok = emptyToken
}

View file

@ -1,35 +0,0 @@
package ini
// Statement is an empty AST mostly used for transitioning states.
func newStatement() AST {
return newAST(ASTKindStatement, AST{})
}
// SectionStatement represents a section AST
func newSectionStatement(tok Token) AST {
return newASTWithRootToken(ASTKindSectionStatement, tok)
}
// ExprStatement represents a completed expression AST
func newExprStatement(ast AST) AST {
return newAST(ASTKindExprStatement, ast)
}
// CommentStatement represents a comment in the ini defintion.
//
// grammar:
// comment -> #comment' | ;comment'
// comment' -> epsilon | value
func newCommentStatement(tok Token) AST {
return newAST(ASTKindCommentStatement, newExpression(tok))
}
// CompletedSectionStatement represents a completed section
func newCompletedSectionStatement(ast AST) AST {
return newAST(ASTKindCompletedSectionStatement, ast)
}
// SkipStatement is used to skip whole statements
func newSkipStatement(ast AST) AST {
return newAST(ASTKindSkipStatement, ast)
}

View file

@ -0,0 +1,83 @@
package ini
import "strings"
func trimComment(v string) string {
rest, _, _ := strings.Cut(v, "#")
rest, _, _ = strings.Cut(rest, ";")
return rest
}
// assumes no surrounding comment
func splitProperty(s string) (string, string, bool) {
equalsi := strings.Index(s, "=")
coloni := strings.Index(s, ":") // LEGACY: also supported for property assignment
sep := "="
if equalsi == -1 || coloni != -1 && coloni < equalsi {
sep = ":"
}
k, v, ok := strings.Cut(s, sep)
if !ok {
return "", "", false
}
return strings.TrimSpace(k), strings.TrimSpace(v), true
}
// assumes no surrounding comment, whitespace, or profile brackets
func splitProfile(s string) (string, string) {
var first int
for i, r := range s {
if isLineSpace(r) {
if first == 0 {
first = i
}
} else {
if first != 0 {
return s[:first], s[i:]
}
}
}
if first == 0 {
return "", s // type component is effectively blank
}
return "", ""
}
func isLineSpace(r rune) bool {
return r == ' ' || r == '\t'
}
func unquote(s string) string {
if isSingleQuoted(s) || isDoubleQuoted(s) {
return s[1 : len(s)-1]
}
return s
}
// applies various legacy conversions to property values:
// - remote wrapping single/doublequotes
// - expand escaped quote and newline sequences
func legacyStrconv(s string) string {
s = unquote(s)
s = strings.ReplaceAll(s, `\"`, `"`)
s = strings.ReplaceAll(s, `\'`, `'`)
s = strings.ReplaceAll(s, `\n`, "\n")
return s
}
func isSingleQuoted(s string) bool {
return hasAffixes(s, "'", "'")
}
func isDoubleQuoted(s string) bool {
return hasAffixes(s, `"`, `"`)
}
func isBracketed(s string) bool {
return hasAffixes(s, "[", "]")
}
func hasAffixes(s, left, right string) bool {
return strings.HasPrefix(s, left) && strings.HasSuffix(s, right)
}

View file

@ -0,0 +1,32 @@
package ini
type lineToken interface {
isLineToken()
}
type lineTokenProfile struct {
Type string
Name string
}
func (*lineTokenProfile) isLineToken() {}
type lineTokenProperty struct {
Key string
Value string
}
func (*lineTokenProperty) isLineToken() {}
type lineTokenContinuation struct {
Value string
}
func (*lineTokenContinuation) isLineToken() {}
type lineTokenSubProperty struct {
Key string
Value string
}
func (*lineTokenSubProperty) isLineToken() {}

View file

@ -0,0 +1,91 @@
package ini
import (
"strings"
)
func tokenize(lines []string) ([]lineToken, error) {
tokens := make([]lineToken, 0, len(lines))
for _, line := range lines {
if len(strings.TrimSpace(line)) == 0 || isLineComment(line) {
continue
}
if tok := asProfile(line); tok != nil {
tokens = append(tokens, tok)
} else if tok := asProperty(line); tok != nil {
tokens = append(tokens, tok)
} else if tok := asSubProperty(line); tok != nil {
tokens = append(tokens, tok)
} else if tok := asContinuation(line); tok != nil {
tokens = append(tokens, tok)
} // unrecognized tokens are effectively ignored
}
return tokens, nil
}
func isLineComment(line string) bool {
trimmed := strings.TrimLeft(line, " \t")
return strings.HasPrefix(trimmed, "#") || strings.HasPrefix(trimmed, ";")
}
func asProfile(line string) *lineTokenProfile { // " [ type name ] ; comment"
trimmed := strings.TrimSpace(trimComment(line)) // "[ type name ]"
if !isBracketed(trimmed) {
return nil
}
trimmed = trimmed[1 : len(trimmed)-1] // " type name " (or just " name ")
trimmed = strings.TrimSpace(trimmed) // "type name" / "name"
typ, name := splitProfile(trimmed)
return &lineTokenProfile{
Type: typ,
Name: name,
}
}
func asProperty(line string) *lineTokenProperty {
if isLineSpace(rune(line[0])) {
return nil
}
trimmed := strings.TrimRight(trimComment(line), " \t")
k, v, ok := splitProperty(trimmed)
if !ok {
return nil
}
return &lineTokenProperty{
Key: strings.ToLower(k), // LEGACY: normalize key case
Value: legacyStrconv(v), // LEGACY: see func docs
}
}
func asSubProperty(line string) *lineTokenSubProperty {
if !isLineSpace(rune(line[0])) {
return nil
}
// comments on sub-properties are included in the value
trimmed := strings.TrimLeft(line, " \t")
k, v, ok := splitProperty(trimmed)
if !ok {
return nil
}
return &lineTokenSubProperty{ // same LEGACY constraints as in normal property
Key: strings.ToLower(k),
Value: legacyStrconv(v),
}
}
func asContinuation(line string) *lineTokenContinuation {
if !isLineSpace(rune(line[0])) {
return nil
}
// includes comments like sub-properties
trimmed := strings.TrimLeft(line, " \t")
return &lineTokenContinuation{
Value: trimmed,
}
}

View file

@ -0,0 +1,104 @@
package ini
import (
"fmt"
"strconv"
"strings"
)
// ValueType is an enum that will signify what type
// the Value is
type ValueType int
func (v ValueType) String() string {
switch v {
case NoneType:
return "NONE"
case StringType:
return "STRING"
}
return ""
}
// ValueType enums
const (
NoneType = ValueType(iota)
StringType
QuotedStringType
)
// Value is a union container
type Value struct {
Type ValueType
str string
mp map[string]string
}
// NewStringValue returns a Value type generated using a string input.
func NewStringValue(str string) (Value, error) {
return Value{str: str}, nil
}
func (v Value) String() string {
switch v.Type {
case StringType:
return fmt.Sprintf("string: %s", string(v.str))
case QuotedStringType:
return fmt.Sprintf("quoted string: %s", string(v.str))
default:
return "union not set"
}
}
// MapValue returns a map value for sub properties
func (v Value) MapValue() map[string]string {
newlineParts := strings.Split(string(v.str), "\n")
mp := make(map[string]string)
for _, part := range newlineParts {
operandParts := strings.Split(part, "=")
if len(operandParts) < 2 {
continue
}
key := strings.TrimSpace(operandParts[0])
val := strings.TrimSpace(operandParts[1])
mp[key] = val
}
return mp
}
// IntValue returns an integer value
func (v Value) IntValue() (int64, bool) {
i, err := strconv.ParseInt(string(v.str), 0, 64)
if err != nil {
return 0, false
}
return i, true
}
// FloatValue returns a float value
func (v Value) FloatValue() (float64, bool) {
f, err := strconv.ParseFloat(string(v.str), 64)
if err != nil {
return 0, false
}
return f, true
}
// BoolValue returns a bool value
func (v Value) BoolValue() (bool, bool) {
// we don't use ParseBool as it recognizes more than what we've
// historically supported
if strings.EqualFold(v.str, "true") {
return true, true
} else if strings.EqualFold(v.str, "false") {
return false, true
}
return false, false
}
// StringValue returns the string value
func (v Value) StringValue() string {
return v.str
}

View file

@ -1,123 +0,0 @@
package ini
import (
"fmt"
)
// getStringValue will return a quoted string and the amount
// of bytes read
//
// an error will be returned if the string is not properly formatted
func getStringValue(b []rune) (int, error) {
if b[0] != '"' {
return 0, NewParseError("strings must start with '\"'")
}
endQuote := false
i := 1
for ; i < len(b) && !endQuote; i++ {
if escaped := isEscaped(b[:i], b[i]); b[i] == '"' && !escaped {
endQuote = true
break
} else if escaped {
/*c, err := getEscapedByte(b[i])
if err != nil {
return 0, err
}
b[i-1] = c
b = append(b[:i], b[i+1:]...)
i--*/
continue
}
}
if !endQuote {
return 0, NewParseError("missing '\"' in string value")
}
return i + 1, nil
}
func getValue(b []rune) (int, error) {
i := 0
for i < len(b) {
if isNewline(b[i:]) {
break
}
if isOp(b[i:]) {
break
}
valid, n, err := isValid(b[i:])
if err != nil {
return 0, err
}
if !valid {
break
}
i += n
}
return i, nil
}
// isEscaped will return whether or not the character is an escaped
// character.
func isEscaped(value []rune, b rune) bool {
if len(value) == 0 {
return false
}
switch b {
case '\'': // single quote
case '"': // quote
case 'n': // newline
case 't': // tab
case '\\': // backslash
default:
return false
}
return value[len(value)-1] == '\\'
}
func getEscapedByte(b rune) (rune, error) {
switch b {
case '\'': // single quote
return '\'', nil
case '"': // quote
return '"', nil
case 'n': // newline
return '\n', nil
case 't': // table
return '\t', nil
case '\\': // backslash
return '\\', nil
default:
return b, NewParseError(fmt.Sprintf("invalid escaped character %c", b))
}
}
func removeEscapedCharacters(b []rune) []rune {
for i := 0; i < len(b); i++ {
if isEscaped(b[:i], b[i]) {
c, err := getEscapedByte(b[i])
if err != nil {
return b
}
b[i-1] = c
b = append(b[:i], b[i+1:]...)
i--
}
}
return b
}

View file

@ -1,288 +0,0 @@
package ini
import (
"fmt"
"sort"
"strings"
)
// Visitor is an interface used by walkers that will
// traverse an array of ASTs.
type Visitor interface {
VisitExpr(AST) error
VisitStatement(AST) error
}
// DefaultVisitor is used to visit statements and expressions
// and ensure that they are both of the correct format.
// In addition, upon visiting this will build sections and populate
// the Sections field which can be used to retrieve profile
// configuration.
type DefaultVisitor struct {
// scope is the profile which is being visited
scope string
// path is the file path which the visitor is visiting
path string
// Sections defines list of the profile section
Sections Sections
}
// NewDefaultVisitor returns a DefaultVisitor. It takes in a filepath
// which points to the file it is visiting.
func NewDefaultVisitor(filepath string) *DefaultVisitor {
return &DefaultVisitor{
Sections: Sections{
container: map[string]Section{},
},
path: filepath,
}
}
// VisitExpr visits expressions...
func (v *DefaultVisitor) VisitExpr(expr AST) error {
t := v.Sections.container[v.scope]
if t.values == nil {
t.values = values{}
}
if t.SourceFile == nil {
t.SourceFile = make(map[string]string, 0)
}
switch expr.Kind {
case ASTKindExprStatement:
opExpr := expr.GetRoot()
switch opExpr.Kind {
case ASTKindEqualExpr:
children := opExpr.GetChildren()
if len(children) <= 1 {
return NewParseError("unexpected token type")
}
rhs := children[1]
// The right-hand value side the equality expression is allowed to contain '[', ']', ':', '=' in the values.
// If the token is not either a literal or one of the token types that identifies those four additional
// tokens then error.
if !(rhs.Root.Type() == TokenLit || rhs.Root.Type() == TokenOp || rhs.Root.Type() == TokenSep) {
return NewParseError("unexpected token type")
}
key := EqualExprKey(opExpr)
val, err := newValue(rhs.Root.ValueType, rhs.Root.base, rhs.Root.Raw())
if err != nil {
return err
}
// lower case key to standardize
k := strings.ToLower(key)
// identify if the section already had this key, append log on section
if t.Has(k) {
t.Logs = append(t.Logs,
fmt.Sprintf("For profile: %v, overriding %v value, "+
"with a %v value found in a duplicate profile defined later in the same file %v. \n",
t.Name, k, k, v.path))
}
// assign the value
t.values[k] = val
// update the source file path for region
t.SourceFile[k] = v.path
default:
return NewParseError(fmt.Sprintf("unsupported expression %v", expr))
}
default:
return NewParseError(fmt.Sprintf("unsupported expression %v", expr))
}
v.Sections.container[v.scope] = t
return nil
}
// VisitStatement visits statements...
func (v *DefaultVisitor) VisitStatement(stmt AST) error {
switch stmt.Kind {
case ASTKindCompletedSectionStatement:
child := stmt.GetRoot()
if child.Kind != ASTKindSectionStatement {
return NewParseError(fmt.Sprintf("unsupported child statement: %T", child))
}
name := string(child.Root.Raw())
// trim start and end space
name = strings.TrimSpace(name)
// if has prefix "profile " + [ws+] + "profile-name",
// we standardize by removing the [ws+] between prefix and profile-name.
if strings.HasPrefix(name, "profile ") {
names := strings.SplitN(name, " ", 2)
name = names[0] + " " + strings.TrimLeft(names[1], " ")
}
// attach profile name on section
if !v.Sections.HasSection(name) {
v.Sections.container[name] = NewSection(name)
}
v.scope = name
default:
return NewParseError(fmt.Sprintf("unsupported statement: %s", stmt.Kind))
}
return nil
}
// Sections is a map of Section structures that represent
// a configuration.
type Sections struct {
container map[string]Section
}
// NewSections returns empty ini Sections
func NewSections() Sections {
return Sections{
container: make(map[string]Section, 0),
}
}
// GetSection will return section p. If section p does not exist,
// false will be returned in the second parameter.
func (t Sections) GetSection(p string) (Section, bool) {
v, ok := t.container[p]
return v, ok
}
// HasSection denotes if Sections consist of a section with
// provided name.
func (t Sections) HasSection(p string) bool {
_, ok := t.container[p]
return ok
}
// SetSection sets a section value for provided section name.
func (t Sections) SetSection(p string, v Section) Sections {
t.container[p] = v
return t
}
// DeleteSection deletes a section entry/value for provided section name./
func (t Sections) DeleteSection(p string) {
delete(t.container, p)
}
// values represents a map of union values.
type values map[string]Value
// List will return a list of all sections that were successfully
// parsed.
func (t Sections) List() []string {
keys := make([]string, len(t.container))
i := 0
for k := range t.container {
keys[i] = k
i++
}
sort.Strings(keys)
return keys
}
// Section contains a name and values. This represent
// a sectioned entry in a configuration file.
type Section struct {
// Name is the Section profile name
Name string
// values are the values within parsed profile
values values
// Errors is the list of errors
Errors []error
// Logs is the list of logs
Logs []string
// SourceFile is the INI Source file from where this section
// was retrieved. They key is the property, value is the
// source file the property was retrieved from.
SourceFile map[string]string
}
// NewSection returns an initialize section for the name
func NewSection(name string) Section {
return Section{
Name: name,
values: values{},
SourceFile: map[string]string{},
}
}
// List will return a list of all
// services in values
func (t Section) List() []string {
keys := make([]string, len(t.values))
i := 0
for k := range t.values {
keys[i] = k
i++
}
sort.Strings(keys)
return keys
}
// UpdateSourceFile updates source file for a property to provided filepath.
func (t Section) UpdateSourceFile(property string, filepath string) {
t.SourceFile[property] = filepath
}
// UpdateValue updates value for a provided key with provided value
func (t Section) UpdateValue(k string, v Value) error {
t.values[k] = v
return nil
}
// Has will return whether or not an entry exists in a given section
func (t Section) Has(k string) bool {
_, ok := t.values[k]
return ok
}
// ValueType will returned what type the union is set to. If
// k was not found, the NoneType will be returned.
func (t Section) ValueType(k string) (ValueType, bool) {
v, ok := t.values[k]
return v.Type, ok
}
// Bool returns a bool value at k
func (t Section) Bool(k string) (bool, bool) {
return t.values[k].BoolValue()
}
// Int returns an integer value at k
func (t Section) Int(k string) (int64, bool) {
return t.values[k].IntValue()
}
// Map returns a map value at k
func (t Section) Map(k string) map[string]string {
return t.values[k].MapValue()
}
// Float64 returns a float value at k
func (t Section) Float64(k string) (float64, bool) {
return t.values[k].FloatValue()
}
// String returns the string value at k
func (t Section) String(k string) string {
_, ok := t.values[k]
if !ok {
return ""
}
return t.values[k].StringValue()
}

View file

@ -1,25 +0,0 @@
package ini
// Walk will traverse the AST using the v, the Visitor.
func Walk(tree []AST, v Visitor) error {
for _, node := range tree {
switch node.Kind {
case ASTKindExpr,
ASTKindExprStatement:
if err := v.VisitExpr(node); err != nil {
return err
}
case ASTKindStatement,
ASTKindCompletedSectionStatement,
ASTKindNestedSectionStatement,
ASTKindCompletedNestedSectionStatement:
if err := v.VisitStatement(node); err != nil {
return err
}
}
}
return nil
}

View file

@ -1,24 +0,0 @@
package ini
import (
"unicode"
)
// isWhitespace will return whether or not the character is
// a whitespace character.
//
// Whitespace is defined as a space or tab.
func isWhitespace(c rune) bool {
return unicode.IsSpace(c) && c != '\n' && c != '\r'
}
func newWSToken(b []rune) (Token, int, error) {
i := 0
for ; i < len(b); i++ {
if !isWhitespace(b[i]) {
break
}
}
return newToken(TokenWS, b[:i], NoneType), i, nil
}

View file

@ -1,3 +1,7 @@
# v1.2.2 (2023-11-09)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.2.1 (2023-11-01)
* **Dependency Update**: Updated to the latest SDK module versions

View file

@ -3,4 +3,4 @@
package v4a
// goModuleVersion is the tagged release for this module
const goModuleVersion = "1.2.1"
const goModuleVersion = "1.2.2"

View file

@ -1,39 +0,0 @@
#!/usr/bin/env bash
PROJECT_DIR=""
SDK_SOURCE_DIR=$(cd `dirname $0` && pwd)
usage() {
echo "Usage: $0 [-s SDK_SOURCE_DIR] [-d PROJECT_DIR]" 1>&2
exit 1
}
while getopts "hs:d:" options; do
case "${options}" in
s)
SDK_SOURCE_DIR=${OPTARG}
if [ "$SDK_SOURCE_DIR" == "" ]; then
echo "path to SDK source directory is required" || exit
usage
fi
;;
d)
PROJECT_DIR=${OPTARG}
;;
h)
usage
;;
*)
usage
;;
esac
done
if [ "$PROJECT_DIR" != "" ]; then
cd "$PROJECT_DIR" || exit
fi
go mod graph | awk '{print $1}' | cut -d '@' -f 1 | sort | uniq | grep "github.com/aws/aws-sdk-go-v2" | while read x; do
repPath=${x/github.com\/aws\/aws-sdk-go-v2/${SDK_SOURCE_DIR}}
echo -replace $x=$repPath
done | xargs go mod edit

View file

@ -1,78 +0,0 @@
[dependencies]
"github.com/aws/aws-sdk-go" = "v1.44.28"
"github.com/aws/smithy-go" = "v1.16.0"
"github.com/google/go-cmp" = "v0.5.8"
"github.com/jmespath/go-jmespath" = "v0.4.0"
"golang.org/x/net" = "v0.1.0"
[modules]
[modules."."]
metadata_package = "aws"
[modules.codegen]
no_tag = true
[modules."example/service/dynamodb/createTable"]
no_tag = true
[modules."example/service/dynamodb/scanItems"]
no_tag = true
[modules."example/service/s3/listObjects"]
no_tag = true
[modules."example/service/s3/usingPrivateLink"]
no_tag = true
[modules."feature/ec2/imds/internal/configtesting"]
no_tag = true
[modules."internal/codegen"]
no_tag = true
[modules."internal/configsources/configtesting"]
no_tag = true
[modules."internal/protocoltest/awsrestjson"]
no_tag = true
[modules."internal/protocoltest/ec2query"]
no_tag = true
[modules."internal/protocoltest/jsonrpc"]
no_tag = true
[modules."internal/protocoltest/jsonrpc10"]
no_tag = true
[modules."internal/protocoltest/query"]
no_tag = true
[modules."internal/protocoltest/restxml"]
no_tag = true
[modules."internal/protocoltest/restxmlwithnamespace"]
no_tag = true
[modules."internal/repotools"]
no_tag = true
[modules."internal/repotools/changes"]
no_tag = true
[modules."service/internal/benchmark"]
no_tag = true
[modules."service/internal/integrationtest"]
no_tag = true
[modules."service/kinesis/internal/testing"]
no_tag = true
[modules."service/s3/internal/configtesting"]
no_tag = true
[modules."service/transcribestreaming/internal/testing"]
no_tag = true

View file

@ -1,3 +1,7 @@
# v1.2.2 (2023-11-09)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.2.1 (2023-11-01)
* **Dependency Update**: Updated to the latest SDK module versions

View file

@ -3,4 +3,4 @@
package checksum
// goModuleVersion is the tagged release for this module
const goModuleVersion = "1.2.1"
const goModuleVersion = "1.2.2"

View file

@ -1,3 +1,7 @@
# v1.10.2 (2023-11-09)
* **Dependency Update**: Updated to the latest SDK module versions
# v1.10.1 (2023-11-01)
* **Dependency Update**: Updated to the latest SDK module versions

View file

@ -3,4 +3,4 @@
package presignedurl
// goModuleVersion is the tagged release for this module
const goModuleVersion = "1.10.1"
const goModuleVersion = "1.10.2"

Some files were not shown because too many files have changed in this diff Show more