diff --git a/go.mod b/go.mod index 65a439f1e7..1692f44441 100644 --- a/go.mod +++ b/go.mod @@ -11,15 +11,15 @@ require ( github.com/grpc-ecosystem/grpc-health-probe v0.4.53 github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2 github.com/mikefarah/yq/v3 v3.0.0-20201202084205-8846255d1c37 - github.com/onsi/ginkgo/v2 v2.32.0 + github.com/onsi/ginkgo/v2 v2.32.1 github.com/openshift/api v0.0.0-20260204104751-e09e5a4ebcd0 github.com/openshift/library-go v0.0.0-20260204111611-b7d4fa0e292a github.com/operator-framework/api v0.45.0 github.com/operator-framework/operator-lifecycle-manager v0.0.0-00010101000000-000000000000 github.com/operator-framework/operator-registry v1.73.0 - github.com/sirupsen/logrus v1.9.4 + github.com/sirupsen/logrus v1.10.0 github.com/spf13/cobra v1.10.2 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.0 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af gopkg.in/yaml.v2 v2.4.0 @@ -199,24 +199,24 @@ require ( go.opentelemetry.io/otel/trace v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.podman.io/common v0.68.1 // indirect - go.podman.io/image/v5 v5.41.0 // indirect + go.podman.io/image/v5 v5.41.1 // indirect go.podman.io/storage v1.64.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect - golang.org/x/crypto v0.54.0 // indirect + golang.org/x/crypto v0.55.0 // indirect golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect golang.org/x/lint v0.0.0-20241112194109-818c5a804067 // indirect golang.org/x/mod v0.38.0 // indirect - golang.org/x/net v0.57.0 // indirect + golang.org/x/net v0.58.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/term v0.45.0 // indirect - golang.org/x/text v0.40.0 // indirect + golang.org/x/text v0.41.0 // indirect golang.org/x/time v0.15.0 // indirect - golang.org/x/tools v0.47.0 // indirect + golang.org/x/tools v0.48.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect diff --git a/go.sum b/go.sum index 891c2b7491..467fc96305 100644 --- a/go.sum +++ b/go.sum @@ -413,8 +413,8 @@ github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JX github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.32.0 h1:Hw7s2pVrQo/8Yz5N77qdnpHaoc+c6cC9WIV1Jce+J6E= -github.com/onsi/ginkgo/v2 v2.32.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= +github.com/onsi/ginkgo/v2 v2.32.1 h1:6tlvcDm/3sE8lGJbZ4+d4mO3RLy24/tQWOFzVSQNIfw= +github.com/onsi/ginkgo/v2 v2.32.1/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I= github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -488,8 +488,8 @@ github.com/sigstore/protobuf-specs v0.5.1/go.mod h1:DRBzpFuE+LnvQMN10/dU6nBeKwVL github.com/sigstore/sigstore v1.10.8 h1:1Mgkxvkw4AXMfIP1DOjc6kw0GkUgA8pGVpveN/EfOq4= github.com/sigstore/sigstore v1.10.8/go.mod h1:f9+B/4iaYimvUkySyb2mvc73n3RLqNn24grHZM/ET8M= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= -github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/sirupsen/logrus v1.10.0 h1:T8MxJJXVZkfcC5zSRMRAg2F8+lxjmUCGGWPzFxO+Msc= +github.com/sirupsen/logrus v1.10.0/go.mod h1:FXZFonkDAnFozmO+5hGAFvB0Yg9/j2SIhA/QuIkP180= github.com/smallstep/pkcs7 v0.2.1 h1:6Kfzr/QizdIuB6LSv8y1LJdZ3aPSfTNhTLqAx9CTLfA= github.com/smallstep/pkcs7 v0.2.1/go.mod h1:RcXHsMfL+BzH8tRhmrF1NkkpebKpq3JEM66cOFxanf0= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -517,8 +517,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -526,8 +526,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.0 h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8uI= +github.com/stretchr/testify v1.12.0/go.mod h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw= github.com/tidwall/btree v1.8.1 h1:27ehoXvm5AG/g+1VxLS1SD3vRhp/H7LuEfwNvddEdmA= github.com/tidwall/btree v1.8.1/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -629,8 +629,8 @@ go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpu go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.podman.io/common v0.68.1 h1:y8NoHLidghpgHtWtwB9+pN7cHhIDe33g1sB4RFCRvKI= go.podman.io/common v0.68.1/go.mod h1:zVzufHkRpLueF6NW6N+fAs1C2METdzYcfD9zuw+oJKA= -go.podman.io/image/v5 v5.41.0 h1:xOan4jlwbT4R5qhe3ruDAGJLnOC2z0eR61WhZjHbe4E= -go.podman.io/image/v5 v5.41.0/go.mod h1:wfgAlfczPK4ZZw/wn/Av2iJfb3z9Vv+RKWgjHNShzZk= +go.podman.io/image/v5 v5.41.1 h1:iPrhIt7/aNfRBMuzoxbMUVpQmR7Q75ahnrzkIC4JWLQ= +go.podman.io/image/v5 v5.41.1/go.mod h1:wfgAlfczPK4ZZw/wn/Av2iJfb3z9Vv+RKWgjHNShzZk= go.podman.io/storage v1.64.0 h1:ryHCZO+Zl0ZG7OTHF+Qc4C0zLNuoe9W35zgE7mh2pYw= go.podman.io/storage v1.64.0/go.mod h1:ft0DzCRMZs1yn0rszmLYXEEwgd7IIc66JRwSRPUDwwc= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -658,8 +658,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= -golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= -golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= +golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M= +golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= @@ -700,8 +700,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= -golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= @@ -772,8 +772,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= -golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= @@ -794,8 +794,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= -golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= diff --git a/staging/operator-lifecycle-manager/go.mod b/staging/operator-lifecycle-manager/go.mod index 98e35c39b6..928bd52a78 100644 --- a/staging/operator-lifecycle-manager/go.mod +++ b/staging/operator-lifecycle-manager/go.mod @@ -16,7 +16,7 @@ require ( github.com/itchyny/gojq v0.12.19 github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2 github.com/mitchellh/hashstructure/v2 v2.0.2 - github.com/onsi/ginkgo/v2 v2.32.0 + github.com/onsi/ginkgo/v2 v2.32.1 github.com/onsi/gomega v1.42.1 github.com/openshift/api v0.0.0-20260204104751-e09e5a4ebcd0 github.com/openshift/client-go v0.0.0-20260108185524-48f4ccfc4e13 @@ -28,14 +28,14 @@ require ( github.com/prometheus/client_golang v1.24.1 github.com/prometheus/client_model v0.6.2 github.com/prometheus/common v0.70.1 - github.com/sirupsen/logrus v1.9.4 + github.com/sirupsen/logrus v1.10.0 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 - github.com/stretchr/testify v1.11.1 - go.podman.io/image/v5 v5.41.0 + github.com/stretchr/testify v1.12.0 + go.podman.io/image/v5 v5.41.1 go.uber.org/mock v0.6.0 go.yaml.in/yaml/v3 v3.0.5 - golang.org/x/net v0.57.0 + golang.org/x/net v0.58.0 golang.org/x/sync v0.22.0 golang.org/x/time v0.15.0 google.golang.org/grpc v1.83.0 @@ -174,14 +174,14 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - golang.org/x/crypto v0.54.0 // indirect + golang.org/x/crypto v0.55.0 // indirect golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect golang.org/x/mod v0.38.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/term v0.45.0 // indirect - golang.org/x/text v0.40.0 // indirect - golang.org/x/tools v0.47.0 // indirect + golang.org/x/text v0.41.0 // indirect + golang.org/x/tools v0.48.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto v0.0.0-20260203192932-546029d2fa20 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect diff --git a/staging/operator-lifecycle-manager/go.sum b/staging/operator-lifecycle-manager/go.sum index 990f012b63..52476815d8 100644 --- a/staging/operator-lifecycle-manager/go.sum +++ b/staging/operator-lifecycle-manager/go.sum @@ -324,8 +324,8 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.32.0 h1:Hw7s2pVrQo/8Yz5N77qdnpHaoc+c6cC9WIV1Jce+J6E= -github.com/onsi/ginkgo/v2 v2.32.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= +github.com/onsi/ginkgo/v2 v2.32.1 h1:6tlvcDm/3sE8lGJbZ4+d4mO3RLy24/tQWOFzVSQNIfw= +github.com/onsi/ginkgo/v2 v2.32.1/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I= github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -385,8 +385,8 @@ github.com/sigstore/protobuf-specs v0.5.1 h1:/5OPaNuolRJmQfeZLayJGFXMpsRJEdgC6ah github.com/sigstore/protobuf-specs v0.5.1/go.mod h1:DRBzpFuE+LnvQMN10/dU6nBeKwVLGEQ6o2FovN2Rats= github.com/sigstore/sigstore v1.10.8 h1:1Mgkxvkw4AXMfIP1DOjc6kw0GkUgA8pGVpveN/EfOq4= github.com/sigstore/sigstore v1.10.8/go.mod h1:f9+B/4iaYimvUkySyb2mvc73n3RLqNn24grHZM/ET8M= -github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= -github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/sirupsen/logrus v1.10.0 h1:T8MxJJXVZkfcC5zSRMRAg2F8+lxjmUCGGWPzFxO+Msc= +github.com/sirupsen/logrus v1.10.0/go.mod h1:FXZFonkDAnFozmO+5hGAFvB0Yg9/j2SIhA/QuIkP180= github.com/smallstep/pkcs7 v0.2.1 h1:6Kfzr/QizdIuB6LSv8y1LJdZ3aPSfTNhTLqAx9CTLfA= github.com/smallstep/pkcs7 v0.2.1/go.mod h1:RcXHsMfL+BzH8tRhmrF1NkkpebKpq3JEM66cOFxanf0= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= @@ -401,14 +401,14 @@ github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= +github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.0 h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8uI= +github.com/stretchr/testify v1.12.0/go.mod h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw= github.com/tidwall/btree v1.8.1 h1:27ehoXvm5AG/g+1VxLS1SD3vRhp/H7LuEfwNvddEdmA= github.com/tidwall/btree v1.8.1/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -503,8 +503,8 @@ go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpu go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.podman.io/common v0.68.1 h1:y8NoHLidghpgHtWtwB9+pN7cHhIDe33g1sB4RFCRvKI= go.podman.io/common v0.68.1/go.mod h1:zVzufHkRpLueF6NW6N+fAs1C2METdzYcfD9zuw+oJKA= -go.podman.io/image/v5 v5.41.0 h1:xOan4jlwbT4R5qhe3ruDAGJLnOC2z0eR61WhZjHbe4E= -go.podman.io/image/v5 v5.41.0/go.mod h1:wfgAlfczPK4ZZw/wn/Av2iJfb3z9Vv+RKWgjHNShzZk= +go.podman.io/image/v5 v5.41.1 h1:iPrhIt7/aNfRBMuzoxbMUVpQmR7Q75ahnrzkIC4JWLQ= +go.podman.io/image/v5 v5.41.1/go.mod h1:wfgAlfczPK4ZZw/wn/Av2iJfb3z9Vv+RKWgjHNShzZk= go.podman.io/storage v1.64.0 h1:ryHCZO+Zl0ZG7OTHF+Qc4C0zLNuoe9W35zgE7mh2pYw= go.podman.io/storage v1.64.0/go.mod h1:ft0DzCRMZs1yn0rszmLYXEEwgd7IIc66JRwSRPUDwwc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -523,8 +523,8 @@ go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= -golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= +golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M= +golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= @@ -544,8 +544,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= -golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= @@ -567,8 +567,8 @@ golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= -golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -579,8 +579,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= -golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go index 5b54dbe246..a5dd7602aa 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go @@ -2539,7 +2539,10 @@ func (a *Operator) transitionCSVState(in v1alpha1.ClusterServiceVersion) (out *v // If there is a succeeded replacement, mark this for deletion next := a.isBeingReplaced(out, a.csvSet(out.GetNamespace(), v1alpha1.CSVPhaseAny)) // Get the newest CSV in the replacement chain if fail forward upgrades are enabled. - if operatorGroup.UpgradeStrategy() == operatorsv1.UpgradeStrategyUnsafeFailForward { + // next can be nil (e.g. the replacement was deleted, or a self-referencing + // spec.replaces is ignored by the finder); fall through to the + // "no replacement CSV found" error below instead of dereferencing it. + if next != nil && operatorGroup.UpgradeStrategy() == operatorsv1.UpgradeStrategyUnsafeFailForward { csvs, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(next.GetNamespace()).List(labels.Everything()) if err != nil { syncError = err diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator_test.go b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator_test.go index a1451d7457..b169bb2188 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator_test.go @@ -3464,6 +3464,71 @@ func TestTransitionCSVFailForward(t *testing.T) { }, }, }, + { + // A CSV left in Replacing by a pre-cycle-guard OLM whose spec.replaces + // names itself: with the self-reference guards, isBeingReplaced returns + // nil, and the fail-forward chain walk must not dereference it. + name: "FailForwardEnabled/SelfReferencingCSV/NoPanicReplacementNotFound", + initial: initial{ + csvs: []*v1alpha1.ClusterServiceVersion{ + csvWithAnnotations(csv("csv1", + namespace, + "1.0.0", + "csv1", + installStrategy("csv1-dep1", nil, nil), + []*apiextensionsv1.CustomResourceDefinition{crd("c1", "v1", "g1")}, + []*apiextensionsv1.CustomResourceDefinition{}, + v1alpha1.CSVPhaseReplacing, + ), addAnnotations(defaultTemplateAnnotations, map[string]string{})), + }, + clientObjs: []runtime.Object{ + func() *operatorsv1.OperatorGroup { + og := defaultOperatorGroup.DeepCopy() + og.Spec.UpgradeStrategy = operatorsv1.UpgradeStrategyUnsafeFailForward + return og + }(), + }, + }, + expected: expected{ + csvStates: map[string]csvState{ + "csv1": {exists: true, phase: v1alpha1.CSVPhaseReplacing}, + }, + err: map[string]error{ + "csv1": fmt.Errorf("marked as replacement, but no replacement CSV found in cluster"), + }, + }, + }, + { + name: "FailForwardDisabled/SelfReferencingCSV/ReplacementNotFound", + initial: initial{ + csvs: []*v1alpha1.ClusterServiceVersion{ + csvWithAnnotations(csv("csv1", + namespace, + "1.0.0", + "csv1", + installStrategy("csv1-dep1", nil, nil), + []*apiextensionsv1.CustomResourceDefinition{crd("c1", "v1", "g1")}, + []*apiextensionsv1.CustomResourceDefinition{}, + v1alpha1.CSVPhaseReplacing, + ), addAnnotations(defaultTemplateAnnotations, map[string]string{})), + }, + clientObjs: []runtime.Object{ + func() *operatorsv1.OperatorGroup { + og := defaultOperatorGroup.DeepCopy() + og.Spec.UpgradeStrategy = operatorsv1.UpgradeStrategyDefault + return og + }(), + }, + }, + expected: expected{ + csvStates: map[string]csvState{ + "csv1": {exists: true, phase: v1alpha1.CSVPhaseReplacing}, + }, + err: map[string]error{ + "csv1": fmt.Errorf("marked as replacement, but no replacement CSV found in cluster"), + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/grpc.go b/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/grpc.go index 024e298691..182dc4288e 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/grpc.go +++ b/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/grpc.go @@ -337,15 +337,15 @@ func (c *GrpcRegistryReconciler) EnsureRegistryServer(logger *logrus.Entry, cata } if err := c.ensurePod(logger, source, sa, defaultPodSecurityConfig, overwritePod); err != nil { logger.WithError(err).Error("error ensuring registry server: could not ensure registry pod") - return pkgerrors.Wrapf(err, "error ensuring pod: %s", pod.GetName()) + return pkgerrors.Wrapf(err, "error ensuring pod: %s", pod.GetGenerateName()) } if err := c.ensureUpdatePod(logger, sa, defaultPodSecurityConfig, source); err != nil { - logger.WithError(err).Error("error ensuring registry server: could not ensure update pod") if _, ok := err.(UpdateNotReadyErr); ok { - logger.WithError(err).Error("error ensuring registry server: ensure update pod error is not of type UpdateNotReadyErr") + logger.WithError(err).Debug("registry server update pod not yet ready") return err } - return pkgerrors.Wrapf(err, "error ensuring updated catalog source pod: %s", pod.GetName()) + logger.WithError(err).Error("error ensuring registry server: could not ensure update pod") + return pkgerrors.Wrapf(err, "error ensuring update pod for catalog source %s", source.GetName()) } service, err := source.Service() diff --git a/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/grpc_test.go b/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/grpc_test.go index d528ea5b90..89b1719ce0 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/grpc_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/grpc_test.go @@ -64,6 +64,16 @@ func grpcCatalogSourceWithAnnotations(annotations map[string]string) *v1alpha1.C return catsrc } +func grpcCatalogSourceWithPolling() *v1alpha1.CatalogSource { + catsrc := validGrpcCatalogSource("image", "") + catsrc.Spec.UpdateStrategy = &v1alpha1.UpdateStrategy{ + RegistryPoll: &v1alpha1.RegistryPoll{ + Interval: &metav1.Duration{Duration: 1 * time.Minute}, + }, + } + return catsrc +} + func grpcCatalogSourceWithName(name string) *v1alpha1.CatalogSource { catsrc := validGrpcCatalogSource("image", "") catsrc.SetName(name) @@ -387,6 +397,28 @@ func TestGrpcRegistryReconciler(t *testing.T) { }, }, }, + { + testName: "Grpc/PollingEnabled/UpdatePodNotReady/ReturnsUpdateNotReadyErr", + in: in{ + cluster: cluster{ + k8sObjs: append( + objectsForCatalogSource(t, grpcCatalogSourceWithPolling()), + &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "update-pod", + Namespace: testNamespace, + Labels: map[string]string{CatalogSourceUpdateKey: "img-catalog"}, + }, + Status: corev1.PodStatus{Phase: corev1.PodRunning}, + }, + ), + }, + catsrc: grpcCatalogSourceWithPolling(), + }, + out: out{ + err: UpdateNotReadyErr{catalogName: "img-catalog", podName: "update-pod"}, + }, + }, } for _, tt := range tests { t.Run(tt.testName, func(t *testing.T) { diff --git a/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go b/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go index 46081bbb4a..d10d5992e5 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go +++ b/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go @@ -180,7 +180,7 @@ func Pod(source *operatorsv1alpha1.CatalogSource, name, opmImg, utilImage, img s Port: 50051, }, }, - FailureThreshold: 10, + FailureThreshold: 15, PeriodSeconds: 10, TimeoutSeconds: 5, }, diff --git a/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler_test.go b/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler_test.go index c527842fb6..fc6187a684 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler_test.go @@ -38,7 +38,7 @@ func TestPodMemoryTarget(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ GenerateName: "test-", Namespace: "testns", - Labels: map[string]string{"olm.pod-spec-hash": "b5nr2RkG6B45bx5Cn63j7B5ybVGjvneXC0AGu7", "olm.managed": "true"}, + Labels: map[string]string{"olm.pod-spec-hash": "cBESV2Miwqi3TX1KELgXItd7j206e4q6b21BnP", "olm.managed": "true"}, Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}, }, Spec: corev1.PodSpec{ @@ -71,7 +71,7 @@ func TestPodMemoryTarget(t *testing.T) { Port: 50051, }, }, - FailureThreshold: 10, + FailureThreshold: 15, PeriodSeconds: 10, TimeoutSeconds: 5, }, @@ -110,7 +110,7 @@ func TestPodMemoryTarget(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ GenerateName: "test-", Namespace: "testns", - Labels: map[string]string{"olm.pod-spec-hash": "62b6r57eNVnmLZo0glbfT9JJHn98HcJzBhZN94", "olm.managed": "true"}, + Labels: map[string]string{"olm.pod-spec-hash": "22OBIOEoFxFjed27eJvvYzqL0TfdXsuapKoxsE", "olm.managed": "true"}, Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}, }, Spec: corev1.PodSpec{ @@ -144,7 +144,7 @@ func TestPodMemoryTarget(t *testing.T) { Port: 50051, }, }, - FailureThreshold: 10, + FailureThreshold: 15, PeriodSeconds: 10, TimeoutSeconds: 5, }, @@ -209,7 +209,7 @@ func TestPodExtractContent(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ GenerateName: "test-", Namespace: "testns", - Labels: map[string]string{"olm.pod-spec-hash": "b5nr2RkG6B45bx5Cn63j7B5ybVGjvneXC0AGu7", "olm.managed": "true"}, + Labels: map[string]string{"olm.pod-spec-hash": "cBESV2Miwqi3TX1KELgXItd7j206e4q6b21BnP", "olm.managed": "true"}, Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}, }, Spec: corev1.PodSpec{ @@ -242,7 +242,7 @@ func TestPodExtractContent(t *testing.T) { Port: 50051, }, }, - FailureThreshold: 10, + FailureThreshold: 15, PeriodSeconds: 10, TimeoutSeconds: 5, }, @@ -285,7 +285,7 @@ func TestPodExtractContent(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ GenerateName: "test-", Namespace: "testns", - Labels: map[string]string{"olm.pod-spec-hash": "7RONLG0R9WhWbcfGQvcwjexvGmZhN0Hyl16mHH", "olm.managed": "true"}, + Labels: map[string]string{"olm.pod-spec-hash": "30fzEky6rWPSaUHUtTN3mbl0SvE2PryXPIr090", "olm.managed": "true"}, Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}, }, Spec: corev1.PodSpec{ @@ -363,7 +363,7 @@ func TestPodExtractContent(t *testing.T) { Port: 50051, }, }, - FailureThreshold: 10, + FailureThreshold: 15, PeriodSeconds: 10, TimeoutSeconds: 5, }, @@ -406,7 +406,7 @@ func TestPodExtractContent(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ GenerateName: "test-", Namespace: "testns", - Labels: map[string]string{"olm.pod-spec-hash": "8GtO2FTxu9llB1icRoWQIinKIVmNcrtivDihGG", "olm.managed": "true"}, + Labels: map[string]string{"olm.pod-spec-hash": "57eMKS6v8du5evWOTeBJEFYx7r5udVxvhEQpLB", "olm.managed": "true"}, Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}, }, Spec: corev1.PodSpec{ @@ -486,7 +486,7 @@ func TestPodExtractContent(t *testing.T) { Port: 50051, }, }, - FailureThreshold: 10, + FailureThreshold: 15, PeriodSeconds: 10, TimeoutSeconds: 5, }, @@ -522,7 +522,7 @@ func TestPodExtractContent(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ GenerateName: "test-", Namespace: "testns", - Labels: map[string]string{"olm.pod-spec-hash": "8MrvWgiEdqXHHX9wiYJTEe8NgdSQYTVQtOnqml", "olm.managed": "true"}, + Labels: map[string]string{"olm.pod-spec-hash": "bfNQa9OTbkeciTpFoCgpKhNENGjVjY61PYAoYA", "olm.managed": "true"}, Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}, }, Spec: corev1.PodSpec{ @@ -555,7 +555,7 @@ func TestPodExtractContent(t *testing.T) { Port: 50051, }, }, - FailureThreshold: 10, + FailureThreshold: 15, PeriodSeconds: 10, TimeoutSeconds: 5, }, @@ -605,7 +605,7 @@ func TestPodExtractContent(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ GenerateName: "test-", Namespace: "testns", - Labels: map[string]string{"olm.pod-spec-hash": "30zwNEAtF3RcZ94bM4MzA64LwQ8ikpEyrQHTd6", "olm.managed": "true"}, + Labels: map[string]string{"olm.pod-spec-hash": "5CPn3qG7EdDS0mOX7AazM9Tt1RBtR6bjjMmmep", "olm.managed": "true"}, Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}, }, Spec: corev1.PodSpec{ @@ -687,7 +687,7 @@ func TestPodExtractContent(t *testing.T) { Port: 50051, }, }, - FailureThreshold: 10, + FailureThreshold: 15, PeriodSeconds: 10, TimeoutSeconds: 5, }, @@ -737,7 +737,7 @@ func TestPodExtractContent(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ GenerateName: "test-", Namespace: "testns", - Labels: map[string]string{"olm.pod-spec-hash": "G21gfJ9Iz3wyOmy5eBn7kFuqBv93zGJP5lFEM", "olm.managed": "true"}, + Labels: map[string]string{"olm.pod-spec-hash": "adSLBZw1o1geKStnSKaiQEubBnN4eqkHabCyHs", "olm.managed": "true"}, Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}, }, Spec: corev1.PodSpec{ @@ -821,7 +821,7 @@ func TestPodExtractContent(t *testing.T) { Port: 50051, }, }, - FailureThreshold: 10, + FailureThreshold: 15, PeriodSeconds: 10, TimeoutSeconds: 5, }, diff --git a/staging/operator-lifecycle-manager/pkg/lib/csv/replace_finder.go b/staging/operator-lifecycle-manager/pkg/lib/csv/replace_finder.go index 0fc4610ce0..b3cfae9ad3 100644 --- a/staging/operator-lifecycle-manager/pkg/lib/csv/replace_finder.go +++ b/staging/operator-lifecycle-manager/pkg/lib/csv/replace_finder.go @@ -41,6 +41,12 @@ func (r *replace) IsBeingReplaced(in *v1alpha1.ClusterServiceVersion, csvsInName continue } + // a CSV cannot replace itself + if csv.GetName() == in.GetName() { + r.logger.WithField("csv", in.GetName()).Debug("ignoring self-referencing spec.replaces") + continue + } + r.logger.Debugf("checking %s", csv.GetName()) if csv.Spec.Replaces == in.GetName() { @@ -63,6 +69,12 @@ func (r *replace) IsReplacing(in *v1alpha1.ClusterServiceVersion) *v1alpha1.Clus return nil } + // a CSV cannot replace itself + if in.Spec.Replaces == in.GetName() { + r.logger.WithField("csv", in.GetName()).Debug("ignoring self-referencing spec.replaces") + return nil + } + // using the client instead of a lister; missing an object because of a cache sync can cause upgrades to fail previous, err := r.client.OperatorsV1alpha1().ClusterServiceVersions(in.GetNamespace()).Get(context.TODO(), in.Spec.Replaces, metav1.GetOptions{}) if err != nil { @@ -79,12 +91,20 @@ func (r *replace) IsReplacing(in *v1alpha1.ClusterServiceVersion) *v1alpha1.Clus // If the corresponding ClusterServiceVersion is not found nil is returned. func (r *replace) GetFinalCSVInReplacing(in *v1alpha1.ClusterServiceVersion, csvsInNamespace map[string]*v1alpha1.ClusterServiceVersion) (replacedBy *v1alpha1.ClusterServiceVersion) { current := in + visited := map[string]struct{}{in.GetName(): {}} for { next := r.IsBeingReplaced(current, csvsInNamespace) if next == nil { break } + // a cycle in the replacement chain would loop forever + if _, ok := visited[next.GetName()]; ok { + r.logger.WithField("csv", next.GetName()).Debug("cycle detected in replacement chain") + break + } + visited[next.GetName()] = struct{}{} + replacedBy = next current = next } diff --git a/staging/operator-lifecycle-manager/pkg/lib/csv/replace_finder_test.go b/staging/operator-lifecycle-manager/pkg/lib/csv/replace_finder_test.go new file mode 100644 index 0000000000..5ce7692969 --- /dev/null +++ b/staging/operator-lifecycle-manager/pkg/lib/csv/replace_finder_test.go @@ -0,0 +1,71 @@ +package csv + +import ( + "testing" + + "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/operator-framework/api/pkg/operators/v1alpha1" + "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake" +) + +func newCSV(name, replaces string) *v1alpha1.ClusterServiceVersion { + return &v1alpha1.ClusterServiceVersion{ + ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "ns"}, + Spec: v1alpha1.ClusterServiceVersionSpec{Replaces: replaces}, + } +} + +func setOf(csvs ...*v1alpha1.ClusterServiceVersion) map[string]*v1alpha1.ClusterServiceVersion { + set := map[string]*v1alpha1.ClusterServiceVersion{} + for _, csv := range csvs { + set[csv.GetName()] = csv + } + return set +} + +func TestIsBeingReplacedIgnoresSelf(t *testing.T) { + finder := NewReplaceFinder(logrus.New(), fake.NewSimpleClientset()) + self := newCSV("a", "a") + if got := finder.IsBeingReplaced(self, setOf(self)); got != nil { + t.Fatalf("self-replacing CSV reported as being replaced by %q", got.GetName()) + } +} + +func TestIsReplacingIgnoresSelf(t *testing.T) { + self := newCSV("a", "a") + finder := NewReplaceFinder(logrus.New(), fake.NewSimpleClientset(self)) + if got := finder.IsReplacing(self); got != nil { + t.Fatalf("self-replacing CSV reported as replacing %q", got.GetName()) + } +} + +// Regression test for OCPBUGS-23954: these calls looped forever before the +// cycle guard. +func TestGetFinalCSVInReplacingTerminates(t *testing.T) { + finder := NewReplaceFinder(logrus.New(), fake.NewSimpleClientset()) + + // self-loop: a replaces a + self := newCSV("a", "a") + if got := finder.GetFinalCSVInReplacing(self, setOf(self)); got != nil { + t.Fatalf("self-loop: expected nil, got %q", got.GetName()) + } + + // two-CSV cycle: a replaces b, b replaces a + a := newCSV("a", "b") + b := newCSV("b", "a") + got := finder.GetFinalCSVInReplacing(a, setOf(a, b)) + if got == nil || got.GetName() != "b" { + t.Fatalf("two-CSV cycle: expected b, got %v", got) + } + + // linear chain: c replaces b replaces a, walk from a ends at c + a = newCSV("a", "") + b = newCSV("b", "a") + c := newCSV("c", "b") + got = finder.GetFinalCSVInReplacing(a, setOf(a, b, c)) + if got == nil || got.GetName() != "c" { + t.Fatalf("linear chain: expected c, got %v", got) + } +} diff --git a/vendor/github.com/onsi/ginkgo/v2/types/version.go b/vendor/github.com/onsi/ginkgo/v2/types/version.go index dc28324c1d..177baed9be 100644 --- a/vendor/github.com/onsi/ginkgo/v2/types/version.go +++ b/vendor/github.com/onsi/ginkgo/v2/types/version.go @@ -1,3 +1,3 @@ package types -const VERSION = "2.32.0" +const VERSION = "2.32.1" diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go index 5b54dbe246..a5dd7602aa 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go @@ -2539,7 +2539,10 @@ func (a *Operator) transitionCSVState(in v1alpha1.ClusterServiceVersion) (out *v // If there is a succeeded replacement, mark this for deletion next := a.isBeingReplaced(out, a.csvSet(out.GetNamespace(), v1alpha1.CSVPhaseAny)) // Get the newest CSV in the replacement chain if fail forward upgrades are enabled. - if operatorGroup.UpgradeStrategy() == operatorsv1.UpgradeStrategyUnsafeFailForward { + // next can be nil (e.g. the replacement was deleted, or a self-referencing + // spec.replaces is ignored by the finder); fall through to the + // "no replacement CSV found" error below instead of dereferencing it. + if next != nil && operatorGroup.UpgradeStrategy() == operatorsv1.UpgradeStrategyUnsafeFailForward { csvs, err := a.lister.OperatorsV1alpha1().ClusterServiceVersionLister().ClusterServiceVersions(next.GetNamespace()).List(labels.Everything()) if err != nil { syncError = err diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler/grpc.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler/grpc.go index 024e298691..182dc4288e 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler/grpc.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler/grpc.go @@ -337,15 +337,15 @@ func (c *GrpcRegistryReconciler) EnsureRegistryServer(logger *logrus.Entry, cata } if err := c.ensurePod(logger, source, sa, defaultPodSecurityConfig, overwritePod); err != nil { logger.WithError(err).Error("error ensuring registry server: could not ensure registry pod") - return pkgerrors.Wrapf(err, "error ensuring pod: %s", pod.GetName()) + return pkgerrors.Wrapf(err, "error ensuring pod: %s", pod.GetGenerateName()) } if err := c.ensureUpdatePod(logger, sa, defaultPodSecurityConfig, source); err != nil { - logger.WithError(err).Error("error ensuring registry server: could not ensure update pod") if _, ok := err.(UpdateNotReadyErr); ok { - logger.WithError(err).Error("error ensuring registry server: ensure update pod error is not of type UpdateNotReadyErr") + logger.WithError(err).Debug("registry server update pod not yet ready") return err } - return pkgerrors.Wrapf(err, "error ensuring updated catalog source pod: %s", pod.GetName()) + logger.WithError(err).Error("error ensuring registry server: could not ensure update pod") + return pkgerrors.Wrapf(err, "error ensuring update pod for catalog source %s", source.GetName()) } service, err := source.Service() diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go index 46081bbb4a..d10d5992e5 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go @@ -180,7 +180,7 @@ func Pod(source *operatorsv1alpha1.CatalogSource, name, opmImg, utilImage, img s Port: 50051, }, }, - FailureThreshold: 10, + FailureThreshold: 15, PeriodSeconds: 10, TimeoutSeconds: 5, }, diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/csv/replace_finder.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/csv/replace_finder.go index 0fc4610ce0..b3cfae9ad3 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/csv/replace_finder.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/lib/csv/replace_finder.go @@ -41,6 +41,12 @@ func (r *replace) IsBeingReplaced(in *v1alpha1.ClusterServiceVersion, csvsInName continue } + // a CSV cannot replace itself + if csv.GetName() == in.GetName() { + r.logger.WithField("csv", in.GetName()).Debug("ignoring self-referencing spec.replaces") + continue + } + r.logger.Debugf("checking %s", csv.GetName()) if csv.Spec.Replaces == in.GetName() { @@ -63,6 +69,12 @@ func (r *replace) IsReplacing(in *v1alpha1.ClusterServiceVersion) *v1alpha1.Clus return nil } + // a CSV cannot replace itself + if in.Spec.Replaces == in.GetName() { + r.logger.WithField("csv", in.GetName()).Debug("ignoring self-referencing spec.replaces") + return nil + } + // using the client instead of a lister; missing an object because of a cache sync can cause upgrades to fail previous, err := r.client.OperatorsV1alpha1().ClusterServiceVersions(in.GetNamespace()).Get(context.TODO(), in.Spec.Replaces, metav1.GetOptions{}) if err != nil { @@ -79,12 +91,20 @@ func (r *replace) IsReplacing(in *v1alpha1.ClusterServiceVersion) *v1alpha1.Clus // If the corresponding ClusterServiceVersion is not found nil is returned. func (r *replace) GetFinalCSVInReplacing(in *v1alpha1.ClusterServiceVersion, csvsInNamespace map[string]*v1alpha1.ClusterServiceVersion) (replacedBy *v1alpha1.ClusterServiceVersion) { current := in + visited := map[string]struct{}{in.GetName(): {}} for { next := r.IsBeingReplaced(current, csvsInNamespace) if next == nil { break } + // a cycle in the replacement chain would loop forever + if _, ok := visited[next.GetName()]; ok { + r.logger.WithField("csv", next.GetName()).Debug("cycle detected in replacement chain") + break + } + visited[next.GetName()] = struct{}{} + replacedBy = next current = next } diff --git a/vendor/github.com/sirupsen/logrus/.golangci.yml b/vendor/github.com/sirupsen/logrus/.golangci.yml index 792db36181..c9a840e0d7 100644 --- a/vendor/github.com/sirupsen/logrus/.golangci.yml +++ b/vendor/github.com/sirupsen/logrus/.golangci.yml @@ -1,12 +1,9 @@ version: "2" -run: - tests: false linters: enable: - asasalint - asciicheck - bidichk - - bodyclose - contextcheck - durationcheck - errchkjson @@ -22,46 +19,21 @@ linters: - nilerr - nilnesserr - noctx - - protogetter - reassign - recvcheck - - rowserrcheck - - spancheck - - sqlclosecheck - testifylint - unparam - - zerologlint - disable: - - prealloc - settings: - errcheck: - check-type-assertions: false - check-blank: false - lll: - line-length: 100 - tab-width: 4 - prealloc: - simple: false - range-loops: false - for-loops: false - whitespace: - multi-if: false - multi-func: false exclusions: - generated: lax presets: - - comments - - common-false-positives - legacy - std-error-handling - paths: - - third_party$ - - builtin$ - - examples$ -formatters: - exclusions: - generated: lax - paths: - - third_party$ - - builtin$ - - examples$ + rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - gosec + - musttag + - noctx # TODO: enable once we switch to Go 1.24+. + - linters: # TODO: remove once golangci-lint is updated with https://github.com/golangci/golangci-lint/pull/6584 + - gocheckcompilerdirectives + text: 'compiler directive unrecognized: //go:fix' diff --git a/vendor/github.com/sirupsen/logrus/.travis.yml b/vendor/github.com/sirupsen/logrus/.travis.yml deleted file mode 100644 index c1dbd5a3a3..0000000000 --- a/vendor/github.com/sirupsen/logrus/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: go -go_import_path: github.com/sirupsen/logrus -git: - depth: 1 -env: - - GO111MODULE=on -go: 1.15.x -os: linux -install: - - ./travis/install.sh -script: - - cd ci - - go run mage.go -v -w ../ crossBuild - - go run mage.go -v -w ../ lint - - go run mage.go -v -w ../ test diff --git a/vendor/github.com/sirupsen/logrus/CHANGELOG.md b/vendor/github.com/sirupsen/logrus/CHANGELOG.md index 098608ff4b..650f64988f 100644 --- a/vendor/github.com/sirupsen/logrus/CHANGELOG.md +++ b/vendor/github.com/sirupsen/logrus/CHANGELOG.md @@ -1,90 +1,238 @@ -# 1.8.1 +# Changelog + +All notable changes to this project will be documented in this file. + +## 1.10.0 + +Fixes: + + * Fix reentrant logging deadlocks in formatter paths. + * Fix race conditions in formatter and entry handling. + * Fix generic `Log`, `Logf`, `Logln`, and `LogFn` methods unexpectedly + panicking when called with `PanicLevel`. Use the corresponding `Panic` + methods when panic behavior is desired. + * Improve concurrency safety around formatter and hook access. + +Features: + + * Add `slog` hook for forwarding Logrus entries to `log/slog`. + * Add `slog.Handler` for forwarding `log/slog` records to a Logrus logger, + including levels, fields, groups, context, time, and optional caller + reporting. The hook and handler can also be combined to help migrate + between Logrus and `log/slog`. + * Add minimal, composable logging interfaces for each log level. This enables + consumers to depend on narrower interfaces, making it easier to substitute + or adapt logging implementations. + * Allow `Entry.Caller` to be set explicitly and preserve it across derived + entries, enabling custom caller detection without Logrus overwriting + caller information when `ReportCaller` is enabled. + +Changed: + + * Raise minimum supported Go version to 1.23. + * TextFormatter now renders `[]byte` values as raw/quoted strings instead of slice-of-ints. + * TextFormatter now uses distinct dimmed colors for debug and trace output. + * TextFormatter now automatically enables colors on Windows terminals with ANSI support, + matching the behavior on other platforms. + * `Entry.HasCaller` is now deprecated in favor of checking `Entry.Caller` directly. + * Deprecated `MutexWrap`, which was unintentionally exposed as public API. + It remains available as an alias for compatibility but should not be used + directly. + +Performance: + + * Significantly improve TextFormatter performance and reduce allocations. + * Optimize common Entry and Logger hot paths. + * Reduce allocations in caller reporting. + * ~17% lower geomean runtime and ~27% higher formatter throughput overall. + * Common enabled logging paths are ~30–44% faster. + * TextFormatter paths are up to ~40% faster, with allocation counts reduced + by 25–74% across the measured formatter cases. + + +## 1.9.4 + +Fixes: + + * Remove uses of deprecated `ioutil` package + +Features: + + * Add GNU/Hurd support + * Add WASI wasip1 support + +Code quality: + + * Update minimum supported Go version to 1.17 + * Documentation updates + + +## 1.9.3 + +Fixes: + + * Re-apply fix for potential denial of service in logrus.Writer() when logging >64KB single-line payloads without newlines (#1376) + * Fix panic in Writer + + +## 1.9.2 + +Fixes: + + * Revert Writer DoS fix (#1376) due to regression + + +## 1.9.1 + +Fixes: + + * Fix potential denial of service in logrus.Writer() when logging >64KB single-line payloads without newlines (#1376) + + +## 1.9.0 + +Fixes: + + * Multiple concurrency and race condition fixes + * Improve Windows terminal and ANSI handling + +Code quality: + + * Internal cleanups and modernization + + +## 1.8.3 + +Fixes: + + * Fix potential denial of service in logrus.Writer() when logging >64KB single-line payloads without newlines (#1376) + + +## 1.8.2 + +Features: + + * Add support for the logger private buffer pool (#1253) + +Fixes: + + * Fix race condition for SetFormatter and SetReportCaller + * Fix data race in hooks test package + +## 1.8.1 + Code quality: + * move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer * improve timestamp format documentation Fixes: + * fix race condition on logger hooks -# 1.8.0 +## 1.8.0 Correct versioning number replacing v1.7.1. -# 1.7.1 +## 1.7.1 Beware this release has introduced a new public API and its semver is therefore incorrect. Code quality: + * use go 1.15 in travis * use magefile as task runner Fixes: + * small fixes about new go 1.13 error formatting system * Fix for long time race condiction with mutating data hooks Features: + * build support for zos -# 1.7.0 +## 1.7.0 + Fixes: + * the dependency toward a windows terminal library has been removed Features: + * a new buffer pool management API has been added * a set of `Fn()` functions have been added -# 1.6.0 +## 1.6.0 + Fixes: + * end of line cleanup * revert the entry concurrency bug fix which leads to deadlock under some circumstances * update dependency on go-windows-terminal-sequences to fix a crash with go 1.14 Features: + * add an option to the `TextFormatter` to completely disable fields quoting -# 1.5.0 +## 1.5.0 + Code quality: + * add golangci linter run on travis Fixes: + * add mutex for hooks concurrent access on `Entry` data * caller function field for go1.14 * fix build issue for gopherjs target Feature: + * add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level * add a `DisableHTMLEscape` option in the `JSONFormatter` * add `ForceQuote` and `PadLevelText` options in the `TextFormatter` -# 1.4.2 +## 1.4.2 + * Fixes build break for plan9, nacl, solaris -# 1.4.1 + +## 1.4.1 + This new release introduces: + * Enhance TextFormatter to not print caller information when they are empty (#944) * Remove dependency on golang.org/x/crypto (#932, #943) Fixes: + * Fix Entry.WithContext method to return a copy of the initial entry (#941) -# 1.4.0 +## 1.4.0 + This new release introduces: + * Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848). * Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter` (#909, #911) * Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919). Fixes: + * Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893). * Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903) * Fix infinite recursion on unknown `Level.String()` (#907) * Fix race condition in `getCaller` (#916). -# 1.3.0 +## 1.3.0 + This new release introduces: + * Log, Logf, Logln functions for Logger and Entry that take a Level Fixes: + * Building prometheus node_exporter on AIX (#840) * Race condition in TextFormatter (#468) * Travis CI import path (#868) @@ -92,20 +240,26 @@ Fixes: * Pointer to func as field in JSONFormatter (#870) * Properly marshal Levels (#873) -# 1.2.0 +## 1.2.0 + This new release introduces: + * A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued * A new trace level named `Trace` whose level is below `Debug` * A configurable exit function to be called upon a Fatal trace * The `Level` object now implements `encoding.TextUnmarshaler` interface -# 1.1.1 +## 1.1.1 + This is a bug fix release. + * fix the build break on Solaris * don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized -# 1.1.0 +## 1.1.0 + This new release introduces: + * several fixes: * a fix for a race condition on entry formatting * proper cleanup of previously used entries before putting them back in the pool @@ -122,9 +276,10 @@ This new release introduces: * the field sort function is now configurable for text formatter * the CLICOLOR and CLICOLOR\_FORCE environment variable support in text formater -# 1.0.6 +## 1.0.6 This new release introduces: + * a new api WithTime which allows to easily force the time of the log entry which is mostly useful for logger wrapper * a fix reverting the immutability of the entry given as parameter to the hooks @@ -134,71 +289,71 @@ This new release introduces: * a new configuration of the textformatter to configure the name of the default keys * a new configuration of the text formatter to disable the level truncation -# 1.0.5 +## 1.0.5 * Fix hooks race (#707) * Fix panic deadlock (#695) -# 1.0.4 +## 1.0.4 * Fix race when adding hooks (#612) * Fix terminal check in AppEngine (#635) -# 1.0.3 +## 1.0.3 * Replace example files with testable examples -# 1.0.2 +## 1.0.2 * bug: quote non-string values in text formatter (#583) * Make (*Logger) SetLevel a public method -# 1.0.1 +## 1.0.1 * bug: fix escaping in text formatter (#575) -# 1.0.0 +## 1.0.0 * Officially changed name to lower-case * bug: colors on Windows 10 (#541) * bug: fix race in accessing level (#512) -# 0.11.5 +## 0.11.5 * feature: add writer and writerlevel to entry (#372) -# 0.11.4 +## 0.11.4 * bug: fix undefined variable on solaris (#493) -# 0.11.3 +## 0.11.3 * formatter: configure quoting of empty values (#484) * formatter: configure quoting character (default is `"`) (#484) * bug: fix not importing io correctly in non-linux environments (#481) -# 0.11.2 +## 0.11.2 * bug: fix windows terminal detection (#476) -# 0.11.1 +## 0.11.1 * bug: fix tty detection with custom out (#471) -# 0.11.0 +## 0.11.0 * performance: Use bufferpool to allocate (#370) * terminal: terminal detection for app-engine (#343) * feature: exit handler (#375) -# 0.10.0 +## 0.10.0 * feature: Add a test hook (#180) * feature: `ParseLevel` is now case-insensitive (#326) * feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308) * performance: avoid re-allocations on `WithFields` (#335) -# 0.9.0 +## 0.9.0 * logrus/text_formatter: don't emit empty msg * logrus/hooks/airbrake: move out of main repository @@ -210,25 +365,25 @@ This new release introduces: * logrus/core: support `WithError` on logger * logrus/core: Solaris support -# 0.8.7 +## 0.8.7 * logrus/core: fix possible race (#216) * logrus/doc: small typo fixes and doc improvements -# 0.8.6 +## 0.8.6 * hooks/raven: allow passing an initialized client -# 0.8.5 +## 0.8.5 * logrus/core: revert #208 -# 0.8.4 +## 0.8.4 * formatter/text: fix data race (#218) -# 0.8.3 +## 0.8.3 * logrus/core: fix entry log level (#208) * logrus/core: improve performance of text formatter by 40% @@ -236,24 +391,24 @@ This new release introduces: * logrus/core: add support for DragonflyBSD and NetBSD * formatter/text: print structs more verbosely -# 0.8.2 +## 0.8.2 * logrus: fix more Fatal family functions -# 0.8.1 +## 0.8.1 * logrus: fix not exiting on `Fatalf` and `Fatalln` -# 0.8.0 +## 0.8.0 * logrus: defaults to stderr instead of stdout * hooks/sentry: add special field for `*http.Request` * formatter/text: ignore Windows for colors -# 0.7.3 +## 0.7.3 * formatter/\*: allow configuration of timestamp layout -# 0.7.2 +## 0.7.2 * formatter/text: Add configuration option for time format (#158) diff --git a/vendor/github.com/sirupsen/logrus/README.md b/vendor/github.com/sirupsen/logrus/README.md index cc5dab7eb7..b2ff7affca 100644 --- a/vendor/github.com/sirupsen/logrus/README.md +++ b/vendor/github.com/sirupsen/logrus/README.md @@ -3,13 +3,10 @@ Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger. -**Logrus is in maintenance-mode.** We will not be introducing new features. It's -simply too hard to do in a way that won't break many people's projects, which is -the last thing you want from your Logging library (again...). - -This does not mean Logrus is dead. Logrus will continue to be maintained for -security, (backwards compatible) bug fixes, and performance (where we are -limited by the interface). +**Logrus is in maintenance mode.** The project focuses on security, bug fixes, +and performance improvements. New features are not planned, aside from changes +required to provide interoperability with other logging ecosystems (e.g., Go's +[log/slog](https://pkg.go.dev/log/slog)). I believe Logrus' biggest contribution is to have played a part in today's widespread use of structured logging in Golang. There doesn't seem to be a @@ -23,18 +20,6 @@ about structured logging in Go today. Check out, for example, [zap]: https://github.com/uber-go/zap [apex]: https://github.com/apex/log -**Seeing weird case-sensitive problems?** It's in the past been possible to -import Logrus as both upper- and lower-case. Due to the Go package environment, -this caused issues in the community and we needed a standard. Some environments -experienced problems with the upper-case variant, so the lower-case was decided. -Everything using `logrus` will need to use the lower-case: -`github.com/sirupsen/logrus`. Any package that isn't, should be changed. - -To fix Glide, see [these -comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437). -For an in-depth explanation of the casing issue, see [this -comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276). - Nicely color-coded in development (when a TTY is attached, otherwise just plain text): @@ -43,35 +28,27 @@ plain text): With `logrus.SetFormatter(&logrus.JSONFormatter{})`, for easy parsing by logstash or Splunk: -```text -{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the -ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} - -{"level":"warning","msg":"The group's number increased tremendously!", -"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"} - -{"animal":"walrus","level":"info","msg":"A giant walrus appears!", -"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"} - -{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.", -"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"} - -{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true, -"time":"2014-03-10 19:57:38.562543128 -0400 EDT"} +```json lines +{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} +{"level":"warning","msg":"The group's number increased tremendously!","number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"} +{"animal":"walrus","level":"info","msg":"A giant walrus appears!","size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"} +{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.","size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"} +{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,"time":"2014-03-10 19:57:38.562543128 -0400 EDT"} ``` With the default `logrus.SetFormatter(&logrus.TextFormatter{})` when a TTY is not attached, the output is compatible with the [logfmt](https://pkg.go.dev/github.com/kr/logfmt) format: -```text +```bash time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8 time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10 time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4 time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009 -time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true +time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" animal=orca err="It's over 9000!" number=100 omg=true size=9009 ``` + To ensure this behaviour even if a TTY is attached, set your formatter as follows: ```go @@ -88,29 +65,32 @@ If you wish to add the calling method as a field, instruct the logger via: ```go logrus.SetReportCaller(true) ``` + This adds the caller as 'method' like so: ```json -{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by", -"time":"2014-03-10 19:57:38.562543129 -0400 EDT"} +{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by","time":"2014-03-10 19:57:38.562543129 -0400 EDT"} ``` -```text +```bash time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin ``` + Note that this does add measurable overhead - the cost will depend on the version of Go, but is between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your environment via benchmarks: ```bash -go test -bench=.*CallerTracing +go test -bench=ReportCaller ``` #### Case-sensitivity -The organization's name was changed to lower-case--and this will not be changed -back. If you are getting import conflicts due to case sensitivity, please use -the lower-case import: `github.com/sirupsen/logrus`. +The organization's name was [changed to lower-case][1]. If you are getting import +conflicts due to case sensitivity, please use the lower-case import: +`github.com/sirupsen/logrus`. + +[1]: https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276 #### Example @@ -289,6 +269,7 @@ func init() { } } ``` + Note: Syslog hooks also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md). A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks) @@ -367,18 +348,21 @@ Splunk or Logstash. The built-in logging formatters are: -* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise - without colors. - * *Note:* to force colored output when there is no TTY, set the `ForceColors` +* [`logrus.TextFormatter`](https://pkg.go.dev/github.com/sirupsen/logrus#TextFormatter) + logs the event in colors if the logger output is a TTY, otherwise without colors. + * To force colored output when there is no TTY, set the `ForceColors` field to `true`. To force no colored output even if there is a TTY set the - `DisableColors` field to `true`. For Windows, see - [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable). + `DisableColors` field to `true`. + * On modern Windows terminals with ANSI (Virtual Terminal) support, TextFormatter + automatically enables colored output. + * If your environment does not support ANSI escape sequences, wrap the logger output + using [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable) + and set `ForceColors` (or `CLICOLOR_FORCE=1`) to enable colors through the wrapper. * When colors are enabled, levels are truncated to 4 characters by default. To disable truncation set the `DisableLevelTruncation` field to `true`. * When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text. - * All options are listed in the [generated docs](https://pkg.go.dev/github.com/sirupsen/logrus#TextFormatter). -* `logrus.JSONFormatter`. Logs fields as JSON. - * All options are listed in the [generated docs](https://pkg.go.dev/github.com/sirupsen/logrus#JSONFormatter). +* [`logrus.JSONFormatter`](https://pkg.go.dev/github.com/sirupsen/logrus#JSONFormatter) + logs fields as JSON. Third-party logging formatters: @@ -390,10 +374,12 @@ Third-party logging formatters: * [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure. * [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Save log to files. * [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added. +* [`easy-logrus-formatter`](https://github.com/WeiZhixiong/easy-logrus-formatter). Provide a user-friendly formatter for logrus. +* [`redactrus`](https://github.com/ibreakthecloud/redactrus). Redacts sensitive information like password, apikeys, email, etc. from logs. You can define your formatter by implementing the `Formatter` interface, requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a -`Fields` type (`map[string]interface{}`) with all your fields as well as the +`Fields` type (`map[string]any`) with all your fields as well as the default ones (see Entries section above): ```go @@ -516,4 +502,4 @@ Situations when locking is not needed include: 2) logger.Out is an os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allows multi-thread/multi-process writing) - (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/) + (Refer to ) diff --git a/vendor/github.com/sirupsen/logrus/alt_exit.go b/vendor/github.com/sirupsen/logrus/alt_exit.go index 8fd189e1cc..1c35cf81c4 100644 --- a/vendor/github.com/sirupsen/logrus/alt_exit.go +++ b/vendor/github.com/sirupsen/logrus/alt_exit.go @@ -57,7 +57,7 @@ func Exit(code int) { // // This method is useful when a caller wishes to use logrus to log a fatal // message but also needs to gracefully shutdown. An example usecase could be -// closing database connections, or sending a alert that the application is +// closing database connections, or sending an alert that the application is // closing. func RegisterExitHandler(handler func()) { handlers = append(handlers, handler) @@ -69,7 +69,7 @@ func RegisterExitHandler(handler func()) { // // This method is useful when a caller wishes to use logrus to log a fatal // message but also needs to gracefully shutdown. An example usecase could be -// closing database connections, or sending a alert that the application is +// closing database connections, or sending an alert that the application is // closing. func DeferExitHandler(handler func()) { handlers = append([]func(){handler}, handlers...) diff --git a/vendor/github.com/sirupsen/logrus/buffer_pool.go b/vendor/github.com/sirupsen/logrus/buffer_pool.go index c7787f77cb..6b562d8703 100644 --- a/vendor/github.com/sirupsen/logrus/buffer_pool.go +++ b/vendor/github.com/sirupsen/logrus/buffer_pool.go @@ -5,9 +5,13 @@ import ( "sync" ) -var ( - bufferPool BufferPool -) +var bufferPool BufferPool = &defaultPool{ + pool: &sync.Pool{ + New: func() any { + return new(bytes.Buffer) + }, + }, +} type BufferPool interface { Put(*bytes.Buffer) @@ -27,17 +31,7 @@ func (p *defaultPool) Get() *bytes.Buffer { } // SetBufferPool allows to replace the default logrus buffer pool -// to better meets the specific needs of an application. +// to better meet the specific needs of an application. func SetBufferPool(bp BufferPool) { bufferPool = bp } - -func init() { - SetBufferPool(&defaultPool{ - pool: &sync.Pool{ - New: func() interface{} { - return new(bytes.Buffer) - }, - }, - }) -} diff --git a/vendor/github.com/sirupsen/logrus/doc.go b/vendor/github.com/sirupsen/logrus/doc.go index da67aba06d..75186dc2d2 100644 --- a/vendor/github.com/sirupsen/logrus/doc.go +++ b/vendor/github.com/sirupsen/logrus/doc.go @@ -1,25 +1,25 @@ /* Package logrus is a structured logger for Go, completely API compatible with the standard library logger. - The simplest way to use Logrus is simply the package-level exported logger: - package main + package main - import ( - log "github.com/sirupsen/logrus" - ) + import ( + log "github.com/sirupsen/logrus" + ) - func main() { - log.WithFields(log.Fields{ - "animal": "walrus", - "number": 1, - "size": 10, - }).Info("A walrus appears") - } + func main() { + log.WithFields(log.Fields{ + "animal": "walrus", + "number": 1, + "size": 10, + }).Info("A walrus appears") + } Output: - time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 + + time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 For a full guide visit https://github.com/sirupsen/logrus */ diff --git a/vendor/github.com/sirupsen/logrus/entry.go b/vendor/github.com/sirupsen/logrus/entry.go index 71d796d0b1..1cb4b5204c 100644 --- a/vendor/github.com/sirupsen/logrus/entry.go +++ b/vendor/github.com/sirupsen/logrus/entry.go @@ -4,9 +4,11 @@ import ( "bytes" "context" "fmt" + "maps" "os" "reflect" "runtime" + "strconv" "strings" "sync" "time" @@ -17,8 +19,10 @@ var ( // qualified package name, cached at first use logrusPackage string - // Positions in the call stack when tracing to report the calling method - minimumCallerDepth int + // Positions in the call stack when tracing to report the calling method. + // + // Start at the bottom of the stack before the package-name cache is primed. + minimumCallerDepth = 1 // Used for caller information initialisation callerInitOnce sync.Once @@ -29,68 +33,116 @@ const ( knownLogrusFrames int = 4 ) -func init() { - // start at the bottom of the stack before the package-name cache is primed - minimumCallerDepth = 1 -} - // ErrorKey defines the key when adding errors using [WithError], [Logger.WithError]. var ErrorKey = "error" -// Entry is the final or intermediate Logrus logging entry. It contains all -// the fields passed with WithField{,s}. It's finally logged when Trace, Debug, -// Info, Warn, Error, Fatal or Panic is called on it. These objects can be -// reused and passed around as much as you wish to avoid field duplication. +// Entry represents a single log event. It may be either an intermediate +// entry (created via WithField(s), WithContext, etc.) or a final entry +// that is emitted when one of the level methods (Trace, Debug, Info, +// Warn, Error, Fatal, Panic) is called. // -//nolint:recvcheck // the methods of "Entry" use pointer receiver and non-pointer receiver. +// An Entry always belongs to a Logger. A nil Logger is invalid and will +// cause a panic when the entry is logged. Use [NewEntry] or Logger methods +// to construct entries. +// +// Entries are safe to reuse for adding fields and may be passed around +// to avoid field duplication. Each log operation operates on a copy +// of the Entry’s data to avoid mutation during formatting. +// +//nolint:recvcheck // Entry methods intentionally use both pointer and value receivers. type Entry struct { + // Logger is the Logger that owns this entry and is responsible for + // formatting, hooks, and output. It must not be nil. An Entry without + // a Logger is invalid and will panic when logged. Logger *Logger - // Contains all the fields set by the user. + // Data contains all user-defined fields attached to this entry. Data Fields - // Time at which the log entry was created + // Time is the timestamp for the log event. If zero when the entry is + // logged, it defaults to the current time. Time time.Time - // Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic - // This field will be set on entry firing and the value will be equal to the one in Logger struct field. + // Level is the severity of the log entry. It is set when the entry + // is fired and reflects the level used for that log call. Level Level - // Calling method, with package name + // Caller contains the calling method information. + // + // When [Logger.ReportCaller] is enabled, Caller is populated automatically at + // log time if it is nil. Hooks and formatters may inspect Caller. + // + // Applications generally should not modify Caller unless they intentionally + // want to provide custom caller information. Caller *runtime.Frame - // Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic + // Message is the log message supplied to one of the logging methods + // (Trace, Debug, Info, Warn, Error, Fatal, or Panic). It is set when + // the entry is logged. Message string - // When formatter is called in entry.log(), a Buffer may be set to entry + // Buffer is a reusable buffer provided to the formatter. It is set + // before formatting in the normal log path; when nil, formatters + // allocate their own. Buffer *bytes.Buffer - // Contains the context set by the user. Useful for hook processing etc. + // Context carries user-provided context for hooks and formatters. Context context.Context - // err may contain a field formatting error + // err contains internal field-formatting errors. err string } +// NewEntry creates a new [Entry] associated with the provided Logger. +// The logger must not be nil. Passing a nil logger results in a +// panic when a logging method (e.g., [Entry.Info], [Entry.Error], etc.) +// is called. func NewEntry(logger *Logger) *Entry { return &Entry{ Logger: logger, - // Default is three fields, plus one optional. Give a little extra room. - Data: make(Fields, 6), + // Reserve default predefined fields and a little extra room. + Data: make(Fields, defaultFields+3), } } +// Dup creates a copy of the entry for further modification. +// +// Data is cloned to avoid mutating the original entry. Other fields +// (Logger, Time, Context, etc.) are copied by value. func (entry *Entry) Dup() *Entry { - data := make(Fields, len(entry.Data)) - for k, v := range entry.Data { - data[k] = v + dup := entry.dup() + dup.Data = maps.Clone(entry.Data) + return dup +} + +// dup copies the entry fields shared by derived entries except Data, which +// callers must copy or initialize as appropriate for their use. +func (entry *Entry) dup() *Entry { + return &Entry{ + Logger: entry.Logger, + Time: entry.Time, + Caller: entry.Caller, + Context: entry.Context, + err: entry.err, } - return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err} } // Bytes returns the bytes representation of this entry from the formatter. func (entry *Entry) Bytes() ([]byte, error) { - return entry.Logger.Formatter.Format(entry) + // Snapshot the formatter under the lock to protect against concurrent + // SetFormatter calls, then release the lock before formatting. + // This avoids a data race and prevents a deadlock if Format() triggers + // reentrant logging (e.g., a field's MarshalJSON calls logrus). + // + // See: + // + // - https://github.com/sirupsen/logrus/issues/1440 + // - https://github.com/sirupsen/logrus/issues/1448 + entry.Logger.mu.Lock() + formatter := entry.Logger.Formatter + entry.Logger.mu.Unlock() + + return formatter.Format(entry) } // String returns the string representation from the reader and ultimately the @@ -107,63 +159,82 @@ func (entry *Entry) String() (string, error) { // WithError adds an error as single field (using the key defined in [ErrorKey]) // to the Entry. func (entry *Entry) WithError(err error) *Entry { - return entry.WithField(ErrorKey, err) + // Avoid reflection work in WithFields; we know the type is an error; + // copy the entry data and set the ErrorKey directly. + dup := entry.dup() + dup.Data = maps.Clone(entry.Data) + if dup.Data == nil { + dup.Data = make(Fields, 1) + } + dup.Data[ErrorKey] = err + return dup } // WithContext adds a context to the Entry. func (entry *Entry) WithContext(ctx context.Context) *Entry { - dataCopy := make(Fields, len(entry.Data)) - for k, v := range entry.Data { - dataCopy[k] = v - } - return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx} + dup := entry.dup() + dup.Data = maps.Clone(entry.Data) + dup.Context = ctx + return dup } // WithField adds a single field to the Entry. -func (entry *Entry) WithField(key string, value interface{}) *Entry { - return entry.WithFields(Fields{key: value}) +func (entry *Entry) WithField(key string, value any) *Entry { + dup := entry.dup() + dup.Data = maps.Clone(entry.Data) + if isInvalidField(value) { + if dup.err != "" { + dup.err += ", skipping unsupported field " + strconv.Quote(key) + } else { + dup.err = "skipping unsupported field " + strconv.Quote(key) + } + return dup + } + if dup.Data == nil { + dup.Data = make(Fields, 1) + } + dup.Data[key] = value + return dup } // WithFields adds a map of fields to the Entry. func (entry *Entry) WithFields(fields Fields) *Entry { - data := make(Fields, len(entry.Data)+len(fields)) - for k, v := range entry.Data { - data[k] = v - } - fieldErr := entry.err - for k, v := range fields { - isErrField := false - if t := reflect.TypeOf(v); t != nil { - switch { - case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func: - isErrField = true - } - } - if isErrField { - tmp := fmt.Sprintf("can not add field %q", k) - if fieldErr != "" { - fieldErr = entry.err + ", " + tmp + dup := entry.dup() + dup.Data = make(Fields, len(entry.Data)+len(fields)) + maps.Copy(dup.Data, entry.Data) + + for key, value := range fields { + if isInvalidField(value) { + if dup.err != "" { + dup.err += ", skipping unsupported field " + strconv.Quote(key) } else { - fieldErr = tmp + dup.err = "skipping unsupported field " + strconv.Quote(key) } } else { - data[k] = v + dup.Data[key] = value } } - return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context} + return dup +} + +func isInvalidField(v any) bool { + t := reflect.TypeOf(v) + if t == nil { + return false + } + return t.Kind() == reflect.Func || t.Kind() == reflect.Pointer && t.Elem().Kind() == reflect.Func } // WithTime overrides the time of the Entry. func (entry *Entry) WithTime(t time.Time) *Entry { - dataCopy := make(Fields, len(entry.Data)) - for k, v := range entry.Data { - dataCopy[k] = v - } - return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context} + dup := entry.dup() + dup.Data = maps.Clone(entry.Data) + dup.Time = t + return dup } // getPackageName reduces a fully qualified function name to the package name -// There really ought to be to be a better way... +// There really ought to be a better way... func getPackageName(f string) string { for { lastPeriod := strings.LastIndex(f, ".") @@ -186,7 +257,7 @@ func getCaller() *runtime.Frame { _ = runtime.Callers(0, pcs) // dynamic get the package name and the minimum caller depth - for i := 0; i < maximumCallerDepth; i++ { + for i := range maximumCallerDepth { funcName := runtime.FuncForPC(pcs[i]).Name() if strings.Contains(funcName, "getCaller") { logrusPackage = getPackageName(funcName) @@ -215,16 +286,47 @@ func getCaller() *runtime.Frame { return nil } -func (entry Entry) HasCaller() (has bool) { - return entry.Logger != nil && - entry.Logger.ReportCaller && - entry.Caller != nil +// HasCaller reports whether this Entry contains caller information. +// +// Caller may be set explicitly, or populated at log time when +// [Logger.ReportCaller] is enabled. +// +// Deprecated: use [Entry.Caller] != nil instead. +// +//go:fix inline +func (entry Entry) HasCaller() bool { + return entry.Caller != nil +} + +func (entry *Entry) logArgs(level Level, panicAfter bool, args ...any) { + entry.log(level, panicAfter, sprint(args...)) } -func (entry *Entry) log(level Level, msg string) { - var buffer *bytes.Buffer +func (entry *Entry) logf(level Level, panicAfter bool, format string, args ...any) { + entry.log(level, panicAfter, fmt.Sprintf(format, args...)) +} + +// logln uses Sprintln for multiple arguments to preserve Println-style +// spacing between args, then trims the trailing newline. +func (entry *Entry) logln(level Level, panicAfter bool, args ...any) { + if len(args) <= 1 { + entry.log(level, panicAfter, sprint(args...)) + return + } + msg := fmt.Sprintln(args...) + msg = msg[:len(msg)-1] // Trim the newline added by Sprintln; logging adds its own. + entry.log(level, panicAfter, msg) +} - newEntry := entry.Dup() +// log writes msg at level. If panicAfter is true, it panics with the fully +// populated entry after hooks and output have completed. +// +// The explicit flag keeps panic behavior limited to Panic, Panicf, and +// Panicln while avoiding a return value used only as the panic value. +// See #1283 and commits f96066e and 5f8c666. +func (entry *Entry) log(level Level, panicAfter bool, msg string) { + newEntry := entry.dup() + newEntry.Data = maps.Clone(entry.Data) if newEntry.Time.IsZero() { newEntry.Time = time.Now() @@ -233,17 +335,24 @@ func (entry *Entry) log(level Level, msg string) { newEntry.Level = level newEntry.Message = msg - newEntry.Logger.mu.Lock() - reportCaller := newEntry.Logger.ReportCaller + logger := newEntry.Logger + logger.mu.Lock() + reportCaller := logger.ReportCaller bufPool := newEntry.getBufferPool() - newEntry.Logger.mu.Unlock() + logger.mu.Unlock() - if reportCaller { + // Preserve explicitly set caller information. + if reportCaller && newEntry.Caller == nil { newEntry.Caller = getCaller() } - newEntry.fireHooks() - buffer = bufPool.Get() + // Select hooks based on the level for this log call. Hooks receive the + // Entry and may mutate it, but that does not affect which hooks are + // fired for this event. + hooks := logger.hooksForLevel(level) + newEntry.fireHooks(hooks) + + buffer := bufPool.Get() defer func() { newEntry.Buffer = nil buffer.Reset() @@ -251,15 +360,12 @@ func (entry *Entry) log(level Level, msg string) { }() buffer.Reset() newEntry.Buffer = buffer - newEntry.write() - newEntry.Buffer = nil - // To avoid Entry#log() returning a value that only would make sense for - // panic() to use in Entry#Panic(), we avoid the allocation by checking - // directly here. - if level <= PanicLevel { + // Panic here so the panic value contains the fully populated entry without + // requiring log to return it to the caller. + if panicAfter { panic(newEntry) } } @@ -271,175 +377,207 @@ func (entry *Entry) getBufferPool() (pool BufferPool) { return bufferPool } -func (entry *Entry) fireHooks() { - var tmpHooks LevelHooks - entry.Logger.mu.Lock() - tmpHooks = make(LevelHooks, len(entry.Logger.Hooks)) - for k, v := range entry.Logger.Hooks { - tmpHooks[k] = v - } - entry.Logger.mu.Unlock() - - err := tmpHooks.Fire(entry.Level, entry) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) +func (entry *Entry) fireHooks(hooks []Hook) { + for _, hook := range hooks { + if err := hook.Fire(entry); err != nil { + _, _ = fmt.Fprintln(os.Stderr, "Failed to fire hook:", err) + return + } } } func (entry *Entry) write() { + // Snapshot the formatter under the lock to protect against concurrent + // SetFormatter calls, then release the lock before formatting. + // This avoids a deadlock when Format() triggers reentrant logging (e.g., + // a field's MarshalJSON calls logrus). See #1448, #1440. entry.Logger.mu.Lock() - defer entry.Logger.mu.Unlock() - serialized, err := entry.Logger.Formatter.Format(entry) + formatter := entry.Logger.Formatter + entry.Logger.mu.Unlock() + + serialized, err := formatter.Format(entry) if err != nil { - fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) + _, _ = fmt.Fprintln(os.Stderr, "Failed to format entry:", err) return } + + // Re-acquire the lock to serialize writes to the underlying io.Writer. + entry.Logger.mu.Lock() + defer entry.Logger.mu.Unlock() if _, err := entry.Logger.Out.Write(serialized); err != nil { - fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + _, _ = fmt.Fprintln(os.Stderr, "Failed to write to log:", err) } } -// Log will log a message at the level given as parameter. -// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit. -// For this behaviour Entry.Panic or Entry.Fatal should be used instead. -func (entry *Entry) Log(level Level, args ...interface{}) { +// Log logs a message at the specified level. +// +// Using Log with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. Log treats the level as logging severity only; +// use [Entry.Panic] or [Entry.Fatal] when those side effects are desired. +func (entry *Entry) Log(level Level, args ...any) { + const panicAfter = false if entry.Logger.IsLevelEnabled(level) { - entry.log(level, fmt.Sprint(args...)) + entry.logArgs(level, panicAfter, args...) } } -func (entry *Entry) Trace(args ...interface{}) { +func (entry *Entry) Trace(args ...any) { entry.Log(TraceLevel, args...) } -func (entry *Entry) Debug(args ...interface{}) { +func (entry *Entry) Debug(args ...any) { entry.Log(DebugLevel, args...) } -func (entry *Entry) Print(args ...interface{}) { +func (entry *Entry) Print(args ...any) { entry.Info(args...) } -func (entry *Entry) Info(args ...interface{}) { +func (entry *Entry) Info(args ...any) { entry.Log(InfoLevel, args...) } -func (entry *Entry) Warn(args ...interface{}) { +func (entry *Entry) Warn(args ...any) { entry.Log(WarnLevel, args...) } -func (entry *Entry) Warning(args ...interface{}) { +func (entry *Entry) Warning(args ...any) { entry.Warn(args...) } -func (entry *Entry) Error(args ...interface{}) { +func (entry *Entry) Error(args ...any) { entry.Log(ErrorLevel, args...) } -func (entry *Entry) Fatal(args ...interface{}) { +func (entry *Entry) Fatal(args ...any) { entry.Log(FatalLevel, args...) entry.Logger.Exit(1) } -func (entry *Entry) Panic(args ...interface{}) { - entry.Log(PanicLevel, args...) +func (entry *Entry) Panic(args ...any) { + const panicAfter = true + if entry.Logger.IsLevelEnabled(PanicLevel) { + entry.logArgs(PanicLevel, panicAfter, args...) + } } // Entry Printf family functions -func (entry *Entry) Logf(level Level, format string, args ...interface{}) { +// Logf logs a formatted message at the specified level. +// +// Using Logf with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. Logf treats the level as logging severity only; +// use [Entry.Panicf] or [Entry.Fatalf] when those side effects are desired. +func (entry *Entry) Logf(level Level, format string, args ...any) { + const panicAfter = false if entry.Logger.IsLevelEnabled(level) { - entry.Log(level, fmt.Sprintf(format, args...)) + entry.logf(level, panicAfter, format, args...) } } -func (entry *Entry) Tracef(format string, args ...interface{}) { +func (entry *Entry) Tracef(format string, args ...any) { entry.Logf(TraceLevel, format, args...) } -func (entry *Entry) Debugf(format string, args ...interface{}) { +func (entry *Entry) Debugf(format string, args ...any) { entry.Logf(DebugLevel, format, args...) } -func (entry *Entry) Infof(format string, args ...interface{}) { +func (entry *Entry) Infof(format string, args ...any) { entry.Logf(InfoLevel, format, args...) } -func (entry *Entry) Printf(format string, args ...interface{}) { +func (entry *Entry) Printf(format string, args ...any) { entry.Infof(format, args...) } -func (entry *Entry) Warnf(format string, args ...interface{}) { +func (entry *Entry) Warnf(format string, args ...any) { entry.Logf(WarnLevel, format, args...) } -func (entry *Entry) Warningf(format string, args ...interface{}) { +func (entry *Entry) Warningf(format string, args ...any) { entry.Warnf(format, args...) } -func (entry *Entry) Errorf(format string, args ...interface{}) { +func (entry *Entry) Errorf(format string, args ...any) { entry.Logf(ErrorLevel, format, args...) } -func (entry *Entry) Fatalf(format string, args ...interface{}) { +func (entry *Entry) Fatalf(format string, args ...any) { entry.Logf(FatalLevel, format, args...) entry.Logger.Exit(1) } -func (entry *Entry) Panicf(format string, args ...interface{}) { - entry.Logf(PanicLevel, format, args...) +func (entry *Entry) Panicf(format string, args ...any) { + const panicAfter = true + if entry.Logger.IsLevelEnabled(PanicLevel) { + entry.logf(PanicLevel, panicAfter, format, args...) + } } // Entry Println family functions -func (entry *Entry) Logln(level Level, args ...interface{}) { +// Logln logs a message at the specified level with Println-style spacing. +// +// Using Logln with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. Logln treats the level as logging severity only; +// use [Entry.Panicln] or [Entry.Fatalln] when those side effects are desired. +func (entry *Entry) Logln(level Level, args ...any) { + const panicAfter = false if entry.Logger.IsLevelEnabled(level) { - entry.Log(level, entry.sprintlnn(args...)) + entry.logln(level, panicAfter, args...) } } -func (entry *Entry) Traceln(args ...interface{}) { +func (entry *Entry) Traceln(args ...any) { entry.Logln(TraceLevel, args...) } -func (entry *Entry) Debugln(args ...interface{}) { +func (entry *Entry) Debugln(args ...any) { entry.Logln(DebugLevel, args...) } -func (entry *Entry) Infoln(args ...interface{}) { +func (entry *Entry) Infoln(args ...any) { entry.Logln(InfoLevel, args...) } -func (entry *Entry) Println(args ...interface{}) { +func (entry *Entry) Println(args ...any) { entry.Infoln(args...) } -func (entry *Entry) Warnln(args ...interface{}) { +func (entry *Entry) Warnln(args ...any) { entry.Logln(WarnLevel, args...) } -func (entry *Entry) Warningln(args ...interface{}) { +func (entry *Entry) Warningln(args ...any) { entry.Warnln(args...) } -func (entry *Entry) Errorln(args ...interface{}) { +func (entry *Entry) Errorln(args ...any) { entry.Logln(ErrorLevel, args...) } -func (entry *Entry) Fatalln(args ...interface{}) { +func (entry *Entry) Fatalln(args ...any) { entry.Logln(FatalLevel, args...) entry.Logger.Exit(1) } -func (entry *Entry) Panicln(args ...interface{}) { - entry.Logln(PanicLevel, args...) +func (entry *Entry) Panicln(args ...any) { + const panicAfter = true + if entry.Logger.IsLevelEnabled(PanicLevel) { + entry.logln(PanicLevel, panicAfter, args...) + } } -// sprintlnn => Sprint no newline. This is to get the behavior of how -// fmt.Sprintln where spaces are always added between operands, regardless of -// their type. Instead of vendoring the Sprintln implementation to spare a -// string allocation, we do the simplest thing. -func (entry *Entry) sprintlnn(args ...interface{}) string { - msg := fmt.Sprintln(args...) - return msg[:len(msg)-1] +// sprint is fmt.Sprint with fast paths for zero or one string argument. +func sprint(args ...any) string { + switch len(args) { + case 0: + return "" + case 1: + if msg, ok := args[0].(string); ok { + return msg + } + } + return fmt.Sprint(args...) } diff --git a/vendor/github.com/sirupsen/logrus/exported.go b/vendor/github.com/sirupsen/logrus/exported.go index 017c30ce67..844691a941 100644 --- a/vendor/github.com/sirupsen/logrus/exported.go +++ b/vendor/github.com/sirupsen/logrus/exported.go @@ -6,11 +6,12 @@ import ( "time" ) -var ( - // std is the name of the standard logger in stdlib `log` - std = New() -) +// std is the package-level standard logger, similar to the default logger +// in the stdlib [log] package. +var std = New() +// StandardLogger returns the package-level standard logger used by +// the top-level logging functions. func StandardLogger() *Logger { return std } @@ -41,7 +42,7 @@ func GetLevel() Level { return std.GetLevel() } -// IsLevelEnabled checks if the log level of the standard logger is greater than the level param +// IsLevelEnabled checks if logging for the given level is enabled for the standard logger. func IsLevelEnabled(level Level) bool { return std.IsLevelEnabled(level) } @@ -51,7 +52,8 @@ func AddHook(hook Hook) { std.AddHook(hook) } -// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key. +// WithError creates an entry from the standard logger and adds an error to it, +// using the value defined in [ErrorKey] as key. func WithError(err error) *Entry { return std.WithField(ErrorKey, err) } @@ -61,210 +63,203 @@ func WithContext(ctx context.Context) *Entry { return std.WithContext(ctx) } -// WithField creates an entry from the standard logger and adds a field to -// it. If you want multiple fields, use `WithFields`. -// -// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal -// or Panic on the Entry it returns. -func WithField(key string, value interface{}) *Entry { +// WithField creates an entry from the standard logger and adds a single field. +// For multiple fields, prefer [WithFields] over chaining WithField calls. +func WithField(key string, value any) *Entry { return std.WithField(key, value) } -// WithFields creates an entry from the standard logger and adds multiple -// fields to it. This is simply a helper for `WithField`, invoking it -// once for each field. -// -// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal -// or Panic on the Entry it returns. +// WithFields creates an entry from the standard logger and adds the fields to it. func WithFields(fields Fields) *Entry { return std.WithFields(fields) } -// WithTime creates an entry from the standard logger and overrides the time of -// logs generated with it. -// -// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal -// or Panic on the Entry it returns. +// WithTime creates an entry from the standard logger and overrides the time +// used for logs generated with it. func WithTime(t time.Time) *Entry { return std.WithTime(t) } -// Trace logs a message at level Trace on the standard logger. -func Trace(args ...interface{}) { +// Trace logs a message at level [TraceLevel] on the standard logger. +func Trace(args ...any) { std.Trace(args...) } -// Debug logs a message at level Debug on the standard logger. -func Debug(args ...interface{}) { +// Debug logs a message at level [DebugLevel] on the standard logger. +func Debug(args ...any) { std.Debug(args...) } -// Print logs a message at level Info on the standard logger. -func Print(args ...interface{}) { +// Print logs a message at level [InfoLevel] on the standard logger. +func Print(args ...any) { std.Print(args...) } -// Info logs a message at level Info on the standard logger. -func Info(args ...interface{}) { +// Info logs a message at level [InfoLevel] on the standard logger. +func Info(args ...any) { std.Info(args...) } -// Warn logs a message at level Warn on the standard logger. -func Warn(args ...interface{}) { +// Warn logs a message at level [WarnLevel] on the standard logger. +func Warn(args ...any) { std.Warn(args...) } -// Warning logs a message at level Warn on the standard logger. -func Warning(args ...interface{}) { +// Warning logs a message at level [WarnLevel] on the standard logger. +func Warning(args ...any) { std.Warning(args...) } -// Error logs a message at level Error on the standard logger. -func Error(args ...interface{}) { +// Error logs a message at level [ErrorLevel] on the standard logger. +func Error(args ...any) { std.Error(args...) } -// Panic logs a message at level Panic on the standard logger. -func Panic(args ...interface{}) { +// Panic logs a message at level [PanicLevel] on the standard logger. +func Panic(args ...any) { std.Panic(args...) } -// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1. -func Fatal(args ...interface{}) { +// Fatal logs a message at level [FatalLevel] on the standard logger, +// then exits the process with status 1. +func Fatal(args ...any) { std.Fatal(args...) } -// TraceFn logs a message from a func at level Trace on the standard logger. +// TraceFn logs a message from a func at level [TraceLevel] on the standard logger. func TraceFn(fn LogFunction) { std.TraceFn(fn) } -// DebugFn logs a message from a func at level Debug on the standard logger. +// DebugFn logs a message from a func at level [DebugLevel] on the standard logger. func DebugFn(fn LogFunction) { std.DebugFn(fn) } -// PrintFn logs a message from a func at level Info on the standard logger. +// PrintFn logs a message from a func at level [InfoLevel] on the standard logger. func PrintFn(fn LogFunction) { std.PrintFn(fn) } -// InfoFn logs a message from a func at level Info on the standard logger. +// InfoFn logs a message from a func at level [InfoLevel] on the standard logger. func InfoFn(fn LogFunction) { std.InfoFn(fn) } -// WarnFn logs a message from a func at level Warn on the standard logger. +// WarnFn logs a message from a func at level [WarnLevel] on the standard logger. func WarnFn(fn LogFunction) { std.WarnFn(fn) } -// WarningFn logs a message from a func at level Warn on the standard logger. +// WarningFn logs a message from a func at level [WarnLevel] on the standard logger. func WarningFn(fn LogFunction) { std.WarningFn(fn) } -// ErrorFn logs a message from a func at level Error on the standard logger. +// ErrorFn logs a message from a func at level [ErrorLevel] on the standard logger. func ErrorFn(fn LogFunction) { std.ErrorFn(fn) } -// PanicFn logs a message from a func at level Panic on the standard logger. +// PanicFn logs a message from a func at level [PanicLevel] on the standard logger. func PanicFn(fn LogFunction) { std.PanicFn(fn) } -// FatalFn logs a message from a func at level Fatal on the standard logger then the process will exit with status set to 1. +// FatalFn logs a message from a func at level [FatalLevel] on the standard logger, +// then exits the process with status 1. func FatalFn(fn LogFunction) { std.FatalFn(fn) } -// Tracef logs a message at level Trace on the standard logger. -func Tracef(format string, args ...interface{}) { +// Tracef logs a message at level [TraceLevel] on the standard logger. +func Tracef(format string, args ...any) { std.Tracef(format, args...) } -// Debugf logs a message at level Debug on the standard logger. -func Debugf(format string, args ...interface{}) { +// Debugf logs a message at level [DebugLevel] on the standard logger. +func Debugf(format string, args ...any) { std.Debugf(format, args...) } -// Printf logs a message at level Info on the standard logger. -func Printf(format string, args ...interface{}) { +// Printf logs a message at level [InfoLevel] on the standard logger. +func Printf(format string, args ...any) { std.Printf(format, args...) } -// Infof logs a message at level Info on the standard logger. -func Infof(format string, args ...interface{}) { +// Infof logs a message at level [InfoLevel] on the standard logger. +func Infof(format string, args ...any) { std.Infof(format, args...) } -// Warnf logs a message at level Warn on the standard logger. -func Warnf(format string, args ...interface{}) { +// Warnf logs a message at level [WarnLevel] on the standard logger. +func Warnf(format string, args ...any) { std.Warnf(format, args...) } -// Warningf logs a message at level Warn on the standard logger. -func Warningf(format string, args ...interface{}) { +// Warningf logs a message at level [WarnLevel] on the standard logger. +func Warningf(format string, args ...any) { std.Warningf(format, args...) } -// Errorf logs a message at level Error on the standard logger. -func Errorf(format string, args ...interface{}) { +// Errorf logs a message at level [ErrorLevel] on the standard logger. +func Errorf(format string, args ...any) { std.Errorf(format, args...) } -// Panicf logs a message at level Panic on the standard logger. -func Panicf(format string, args ...interface{}) { +// Panicf logs a message at level [PanicLevel] on the standard logger. +func Panicf(format string, args ...any) { std.Panicf(format, args...) } -// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1. -func Fatalf(format string, args ...interface{}) { +// Fatalf logs a message at level [FatalLevel] on the standard logger, +// then exits the process with status 1. +func Fatalf(format string, args ...any) { std.Fatalf(format, args...) } -// Traceln logs a message at level Trace on the standard logger. -func Traceln(args ...interface{}) { +// Traceln logs a message at level [TraceLevel] on the standard logger. +func Traceln(args ...any) { std.Traceln(args...) } -// Debugln logs a message at level Debug on the standard logger. -func Debugln(args ...interface{}) { +// Debugln logs a message at level [DebugLevel] on the standard logger. +func Debugln(args ...any) { std.Debugln(args...) } -// Println logs a message at level Info on the standard logger. -func Println(args ...interface{}) { +// Println logs a message at level [InfoLevel] on the standard logger. +func Println(args ...any) { std.Println(args...) } -// Infoln logs a message at level Info on the standard logger. -func Infoln(args ...interface{}) { +// Infoln logs a message at level [InfoLevel] on the standard logger. +func Infoln(args ...any) { std.Infoln(args...) } -// Warnln logs a message at level Warn on the standard logger. -func Warnln(args ...interface{}) { +// Warnln logs a message at level [WarnLevel] on the standard logger. +func Warnln(args ...any) { std.Warnln(args...) } -// Warningln logs a message at level Warn on the standard logger. -func Warningln(args ...interface{}) { +// Warningln logs a message at level [WarnLevel] on the standard logger. +func Warningln(args ...any) { std.Warningln(args...) } -// Errorln logs a message at level Error on the standard logger. -func Errorln(args ...interface{}) { +// Errorln logs a message at level [ErrorLevel] on the standard logger. +func Errorln(args ...any) { std.Errorln(args...) } -// Panicln logs a message at level Panic on the standard logger. -func Panicln(args ...interface{}) { +// Panicln logs a message at level [PanicLevel] on the standard logger. +func Panicln(args ...any) { std.Panicln(args...) } -// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1. -func Fatalln(args ...interface{}) { +// Fatalln logs a message at level [FatalLevel] on the standard logger, +// then exits the process with status 1. +func Fatalln(args ...any) { std.Fatalln(args...) } diff --git a/vendor/github.com/sirupsen/logrus/formatter.go b/vendor/github.com/sirupsen/logrus/formatter.go index 408883773e..16f2e0e0fc 100644 --- a/vendor/github.com/sirupsen/logrus/formatter.go +++ b/vendor/github.com/sirupsen/logrus/formatter.go @@ -2,27 +2,40 @@ package logrus import "time" -// Default key names for the default fields const ( + // defaultTimestampFormat is the layout used to format entry timestamps + // when a formatter has not specified a custom TimestampFormat. + // It follows time.RFC3339 and is applied unless timestamps are disabled. defaultTimestampFormat = time.RFC3339 - FieldKeyMsg = "msg" - FieldKeyLevel = "level" - FieldKeyTime = "time" - FieldKeyLogrusError = "logrus_error" - FieldKeyFunc = "func" - FieldKeyFile = "file" + + // defaultFields is the number of commonly included predefined log entry fields + // (msg, level, time). It is used as a capacity hint when constructing + // intermediate collections during formatting (for example, the fixed key list). + // + // It does not include the optional "logrus_error", "func", or "file" fields. + defaultFields = 3 +) + +// Default key names for the default fields +const ( + FieldKeyMsg = "msg" + FieldKeyLevel = "level" + FieldKeyTime = "time" + FieldKeyLogrusError = "logrus_error" + FieldKeyFunc = "func" + FieldKeyFile = "file" ) -// The Formatter interface is used to implement a custom Formatter. It takes an -// `Entry`. It exposes all the fields, including the default ones: +// Formatter is implemented by types that format log entries. It receives an +// [*Entry], which contains: // -// * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. -// * `entry.Data["time"]`. The timestamp. -// * `entry.Data["level"]. The level the entry was logged at. +// - entry.Message: the message passed to logging methods such as [Info], [Warn], [Error] +// - entry.Time: the timestamp +// - entry.Level: the log level // -// Any additional fields added with `WithField` or `WithFields` are also in -// `entry.Data`. Format is expected to return an array of bytes which are then -// logged to `logger.Out`. +// Additional fields added with [WithField] or [WithFields] are available in +// [Entry.Data]. Format should return the formatted log entry as a byte slice, +// which is written to [Logger.Out]. type Formatter interface { Format(*Entry) ([]byte, error) } @@ -30,12 +43,12 @@ type Formatter interface { // This is to not silently overwrite `time`, `msg`, `func` and `level` fields when // dumping it. If this code wasn't there doing: // -// logrus.WithField("level", 1).Info("hello") +// logrus.WithField("level", 1).Info("hello") // // Would just silently drop the user provided level. Instead with this code // it'll logged as: // -// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} +// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} // // It's not exported because it's still using Data in an opinionated way. It's to // avoid code duplication between the two default formatters. diff --git a/vendor/github.com/sirupsen/logrus/json_formatter.go b/vendor/github.com/sirupsen/logrus/json_formatter.go index c96dc5636b..fac7695e9e 100644 --- a/vendor/github.com/sirupsen/logrus/json_formatter.go +++ b/vendor/github.com/sirupsen/logrus/json_formatter.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "runtime" + "strconv" ) type fieldKey string @@ -20,7 +21,13 @@ func (f FieldMap) resolve(key fieldKey) string { return string(key) } -// JSONFormatter formats logs into parsable json +// JSONFormatter formats logs into parsable JSON. +// +// Fields from [Entry.Data] are included in the JSON object together with the +// standard fields derived from the entry. If a field conflicts with a standard +// field, it is prefixed with "fields.". Standard field names can be customized +// through FieldMap. When DataKey is set, fields from [Entry.Data] are nested +// under that key instead. type JSONFormatter struct { // TimestampFormat sets the format used for marshaling timestamps. // The format to use is the same than for time.Format or time.Parse from the standard @@ -61,7 +68,8 @@ type JSONFormatter struct { // Format renders a single log entry func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { - data := make(Fields, len(entry.Data)+4) + caller := entry.Caller + data := make(Fields, len(entry.Data)+defaultFields) for k, v := range entry.Data { switch v := v.(type) { case error: @@ -73,13 +81,14 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } } - if f.DataKey != "" { - newData := make(Fields, 4) + if f.DataKey != "" && len(entry.Data) > 0 { + newData := make(Fields, defaultFields+1) newData[f.DataKey] = data data = newData } - prefixFieldClashes(data, f.FieldMap, entry.HasCaller()) + hasCaller := caller != nil + prefixFieldClashes(data, f.FieldMap, hasCaller) timestampFormat := f.TimestampFormat if timestampFormat == "" { @@ -94,11 +103,13 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() - if entry.HasCaller() { - funcVal := entry.Caller.Function - fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + if caller != nil { + var funcVal, fileVal string if f.CallerPrettyfier != nil { - funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + funcVal, fileVal = f.CallerPrettyfier(caller) + } else { + funcVal = caller.Function + fileVal = caller.File + ":" + strconv.FormatInt(int64(caller.Line), 10) } if funcVal != "" { data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal @@ -108,11 +119,9 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { } } - var b *bytes.Buffer - if entry.Buffer != nil { - b = entry.Buffer - } else { - b = &bytes.Buffer{} + b := entry.Buffer + if b == nil { + b = new(bytes.Buffer) } encoder := json.NewEncoder(b) diff --git a/vendor/github.com/sirupsen/logrus/level.go b/vendor/github.com/sirupsen/logrus/level.go new file mode 100644 index 0000000000..7bd4255d3c --- /dev/null +++ b/vendor/github.com/sirupsen/logrus/level.go @@ -0,0 +1,101 @@ +package logrus + +import ( + "strings" + "sync" +) + +const ( + ansiReset = "\x1b[0m" // reset attributes + ansiRed = "\x1b[31m" // red + ansiYellow = "\x1b[33m" // yellow + ansiCyan = "\x1b[36m" // cyan + ansiDimCyan = "\x1b[2;36m" // dim cyan + ansiDimWhite = "\x1b[2;37m" // dim white (light gray) +) + +type lvlPrefix struct { + full string + truncated string + padded string +} + +func colorize(level Level, s string) string { + color := ansiCyan + switch level { + case TraceLevel: + color = ansiDimWhite + case DebugLevel: + color = ansiDimCyan + case WarnLevel: + color = ansiYellow + case ErrorLevel, FatalLevel, PanicLevel: + color = ansiRed + case InfoLevel: + color = ansiCyan + } + return color + s + ansiReset +} + +func formatLevel(level Level, disableTrunc, pad bool, maxLen int) string { + upper := strings.ToUpper(level.String()) + + if pad && maxLen > len(upper) { + upper += strings.Repeat(" ", maxLen-len(upper)) + } + + if !pad && !disableTrunc && len(upper) > 4 { + upper = upper[:4] + } + + return colorize(level, upper) +} + +var levelPrefixOnce = sync.OnceValues(func() (map[Level]lvlPrefix, lvlPrefix) { + var maxLevel Level + maxLen := 0 + for _, lvl := range AllLevels { + if lvl > maxLevel { + maxLevel = lvl + } + if l := len(lvl.String()); l > maxLen { + maxLen = l + } + } + + prefix := make(map[Level]lvlPrefix, len(AllLevels)) + for _, lvl := range AllLevels { + prefix[lvl] = lvlPrefix{ + full: formatLevel(lvl, true, false, maxLen), + truncated: formatLevel(lvl, false, false, maxLen), + padded: formatLevel(lvl, true, true, maxLen), + } + } + + unknownLevel := maxLevel + 1 + unknown := lvlPrefix{ + full: formatLevel(unknownLevel, true, false, maxLen), + truncated: formatLevel(unknownLevel, false, false, maxLen), + padded: formatLevel(unknownLevel, true, true, maxLen), + } + + return prefix, unknown +}) + +func levelPrefix(level Level, disableTrunc, pad bool) string { + prefix, unknown := levelPrefixOnce() + + p, ok := prefix[level] + if !ok { + p = unknown + } + + switch { + case pad: + return p.padded + case !disableTrunc: + return p.truncated + default: + return p.full + } +} diff --git a/vendor/github.com/sirupsen/logrus/logger.go b/vendor/github.com/sirupsen/logrus/logger.go index f5b8c439ee..17a46e6a65 100644 --- a/vendor/github.com/sirupsen/logrus/logger.go +++ b/vendor/github.com/sirupsen/logrus/logger.go @@ -12,17 +12,19 @@ import ( // LogFunction For big messages, it can be more efficient to pass a function // and only call it if the log level is actually enables rather than // generating the log message and then checking if the level is enabled -type LogFunction func() []interface{} +type LogFunction func() []any type Logger struct { // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a // file, or leave it default which is `os.Stderr`. You can also set this to // something more adventurous, such as logging to Kafka. Out io.Writer + // Hooks for the logger instance. These allow firing events based on logging // levels and log entries. For example, to send errors to an error tracking // service, log to StatsD or dump the core on fatal errors. Hooks LevelHooks + // All log entries pass through the formatter before logged to Out. The // included formatters are `TextFormatter` and `JSONFormatter` for which // TextFormatter is the default. In development (when a TTY is attached) it @@ -38,37 +40,44 @@ type Logger struct { // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be // logged. Level Level + // Used to sync writing to the log. Locking is enabled by Default - mu MutexWrap + mu mutexWrap + // Reusable empty entry entryPool sync.Pool + // Function to exit the application, defaults to `os.Exit()` - ExitFunc exitFunc + ExitFunc func(int) + // The buffer pool used to format the log. If it is nil, the default global // buffer pool will be used. BufferPool BufferPool } -type exitFunc func(int) +// MutexWrap is the mutex implementation used by [Logger]. +// +// Deprecated: MutexWrap is an implementation detail of Logger and should not be used directly. +type MutexWrap = mutexWrap -type MutexWrap struct { +type mutexWrap struct { lock sync.Mutex disabled bool } -func (mw *MutexWrap) Lock() { +func (mw *mutexWrap) Lock() { if !mw.disabled { mw.lock.Lock() } } -func (mw *MutexWrap) Unlock() { +func (mw *mutexWrap) Unlock() { if !mw.disabled { mw.lock.Unlock() } } -func (mw *MutexWrap) Disable() { +func (mw *mutexWrap) Disable() { mw.disabled = true } @@ -104,7 +113,7 @@ func (logger *Logger) newEntry() *Entry { } func (logger *Logger) releaseEntry(entry *Entry) { - entry.Data = map[string]interface{}{} + entry.Data = map[string]any{} logger.entryPool.Put(entry) } @@ -112,7 +121,7 @@ func (logger *Logger) releaseEntry(entry *Entry) { // Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to // this new returned entry. // If you want multiple fields, use `WithFields`. -func (logger *Logger) WithField(key string, value interface{}) *Entry { +func (logger *Logger) WithField(key string, value any) *Entry { entry := logger.newEntry() defer logger.releaseEntry(entry) return entry.WithField(key, value) @@ -148,7 +157,12 @@ func (logger *Logger) WithTime(t time.Time) *Entry { return entry.WithTime(t) } -func (logger *Logger) Logf(level Level, format string, args ...interface{}) { +// Logf logs a formatted message at the specified level. +// +// Using Logf with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. Logf treats the level as logging severity only; +// use [Logger.Panicf] or [Logger.Fatalf] when those side effects are desired. +func (logger *Logger) Logf(level Level, format string, args ...any) { if logger.IsLevelEnabled(level) { entry := logger.newEntry() entry.Logf(level, format, args...) @@ -156,49 +170,55 @@ func (logger *Logger) Logf(level Level, format string, args ...interface{}) { } } -func (logger *Logger) Tracef(format string, args ...interface{}) { +func (logger *Logger) Tracef(format string, args ...any) { logger.Logf(TraceLevel, format, args...) } -func (logger *Logger) Debugf(format string, args ...interface{}) { +func (logger *Logger) Debugf(format string, args ...any) { logger.Logf(DebugLevel, format, args...) } -func (logger *Logger) Infof(format string, args ...interface{}) { +func (logger *Logger) Infof(format string, args ...any) { logger.Logf(InfoLevel, format, args...) } -func (logger *Logger) Printf(format string, args ...interface{}) { +func (logger *Logger) Printf(format string, args ...any) { entry := logger.newEntry() entry.Printf(format, args...) logger.releaseEntry(entry) } -func (logger *Logger) Warnf(format string, args ...interface{}) { +func (logger *Logger) Warnf(format string, args ...any) { logger.Logf(WarnLevel, format, args...) } -func (logger *Logger) Warningf(format string, args ...interface{}) { +func (logger *Logger) Warningf(format string, args ...any) { logger.Warnf(format, args...) } -func (logger *Logger) Errorf(format string, args ...interface{}) { +func (logger *Logger) Errorf(format string, args ...any) { logger.Logf(ErrorLevel, format, args...) } -func (logger *Logger) Fatalf(format string, args ...interface{}) { +func (logger *Logger) Fatalf(format string, args ...any) { logger.Logf(FatalLevel, format, args...) logger.Exit(1) } -func (logger *Logger) Panicf(format string, args ...interface{}) { - logger.Logf(PanicLevel, format, args...) +func (logger *Logger) Panicf(format string, args ...any) { + if logger.IsLevelEnabled(PanicLevel) { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + entry.Panicf(format, args...) + } } -// Log will log a message at the level given as parameter. -// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit. -// For this behaviour Logger.Panic or Logger.Fatal should be used instead. -func (logger *Logger) Log(level Level, args ...interface{}) { +// Log logs a message at the specified level. +// +// Using Log with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. Log treats the level as logging severity only; +// use [Logger.Panic] or [Logger.Fatal] when those side effects are desired. +func (logger *Logger) Log(level Level, args ...any) { if logger.IsLevelEnabled(level) { entry := logger.newEntry() entry.Log(level, args...) @@ -206,6 +226,11 @@ func (logger *Logger) Log(level Level, args ...interface{}) { } } +// LogFn logs a message returned by fn at the specified level. +// +// Using LogFn with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. LogFn treats the level as logging severity only; +// use [Logger.PanicFn] or [Logger.FatalFn] when those side effects are desired. func (logger *Logger) LogFn(level Level, fn LogFunction) { if logger.IsLevelEnabled(level) { entry := logger.newEntry() @@ -214,43 +239,47 @@ func (logger *Logger) LogFn(level Level, fn LogFunction) { } } -func (logger *Logger) Trace(args ...interface{}) { +func (logger *Logger) Trace(args ...any) { logger.Log(TraceLevel, args...) } -func (logger *Logger) Debug(args ...interface{}) { +func (logger *Logger) Debug(args ...any) { logger.Log(DebugLevel, args...) } -func (logger *Logger) Info(args ...interface{}) { +func (logger *Logger) Info(args ...any) { logger.Log(InfoLevel, args...) } -func (logger *Logger) Print(args ...interface{}) { +func (logger *Logger) Print(args ...any) { entry := logger.newEntry() entry.Print(args...) logger.releaseEntry(entry) } -func (logger *Logger) Warn(args ...interface{}) { +func (logger *Logger) Warn(args ...any) { logger.Log(WarnLevel, args...) } -func (logger *Logger) Warning(args ...interface{}) { +func (logger *Logger) Warning(args ...any) { logger.Warn(args...) } -func (logger *Logger) Error(args ...interface{}) { +func (logger *Logger) Error(args ...any) { logger.Log(ErrorLevel, args...) } -func (logger *Logger) Fatal(args ...interface{}) { +func (logger *Logger) Fatal(args ...any) { logger.Log(FatalLevel, args...) logger.Exit(1) } -func (logger *Logger) Panic(args ...interface{}) { - logger.Log(PanicLevel, args...) +func (logger *Logger) Panic(args ...any) { + if logger.IsLevelEnabled(PanicLevel) { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + entry.Panic(args...) + } } func (logger *Logger) TraceFn(fn LogFunction) { @@ -289,10 +318,19 @@ func (logger *Logger) FatalFn(fn LogFunction) { } func (logger *Logger) PanicFn(fn LogFunction) { - logger.LogFn(PanicLevel, fn) + if logger.IsLevelEnabled(PanicLevel) { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + entry.Panic(fn()...) + } } -func (logger *Logger) Logln(level Level, args ...interface{}) { +// Logln logs a message at the specified level with Println-style spacing. +// +// Using Logln with [PanicLevel] or [FatalLevel] intentionally does not +// trigger a panic or exit. Logln treats the level as logging severity only; +// use [Logger.Panicln] or [Logger.Fatalln] when those side effects are desired. +func (logger *Logger) Logln(level Level, args ...any) { if logger.IsLevelEnabled(level) { entry := logger.newEntry() entry.Logln(level, args...) @@ -300,43 +338,47 @@ func (logger *Logger) Logln(level Level, args ...interface{}) { } } -func (logger *Logger) Traceln(args ...interface{}) { +func (logger *Logger) Traceln(args ...any) { logger.Logln(TraceLevel, args...) } -func (logger *Logger) Debugln(args ...interface{}) { +func (logger *Logger) Debugln(args ...any) { logger.Logln(DebugLevel, args...) } -func (logger *Logger) Infoln(args ...interface{}) { +func (logger *Logger) Infoln(args ...any) { logger.Logln(InfoLevel, args...) } -func (logger *Logger) Println(args ...interface{}) { +func (logger *Logger) Println(args ...any) { entry := logger.newEntry() entry.Println(args...) logger.releaseEntry(entry) } -func (logger *Logger) Warnln(args ...interface{}) { +func (logger *Logger) Warnln(args ...any) { logger.Logln(WarnLevel, args...) } -func (logger *Logger) Warningln(args ...interface{}) { +func (logger *Logger) Warningln(args ...any) { logger.Warnln(args...) } -func (logger *Logger) Errorln(args ...interface{}) { +func (logger *Logger) Errorln(args ...any) { logger.Logln(ErrorLevel, args...) } -func (logger *Logger) Fatalln(args ...interface{}) { +func (logger *Logger) Fatalln(args ...any) { logger.Logln(FatalLevel, args...) logger.Exit(1) } -func (logger *Logger) Panicln(args ...interface{}) { - logger.Logln(PanicLevel, args...) +func (logger *Logger) Panicln(args ...any) { + if logger.IsLevelEnabled(PanicLevel) { + entry := logger.newEntry() + defer logger.releaseEntry(entry) + entry.Panicln(args...) + } } func (logger *Logger) Exit(code int) { @@ -375,7 +417,22 @@ func (logger *Logger) AddHook(hook Hook) { logger.Hooks.Add(hook) } -// IsLevelEnabled checks if the log level of the logger is greater than the level param +// hooksForLevel returns a snapshot of the hooks registered for the given level. +// The returned slice is a shallow copy and may be used without holding logger.mu. +func (logger *Logger) hooksForLevel(level Level) []Hook { + logger.mu.Lock() + hooks := logger.Hooks[level] + if len(hooks) == 0 { + logger.mu.Unlock() + return nil + } + out := make([]Hook, len(hooks)) + copy(out, hooks) + logger.mu.Unlock() + return out +} + +// IsLevelEnabled checks if logging for the given level is enabled. func (logger *Logger) IsLevelEnabled(level Level) bool { return logger.level() >= level } @@ -394,6 +451,7 @@ func (logger *Logger) SetOutput(output io.Writer) { logger.Out = output } +// SetReportCaller sets whether the caller stack frame must be logged. func (logger *Logger) SetReportCaller(reportCaller bool) { logger.mu.Lock() defer logger.mu.Unlock() @@ -403,9 +461,9 @@ func (logger *Logger) SetReportCaller(reportCaller bool) { // ReplaceHooks replaces the logger hooks and returns the old ones func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks { logger.mu.Lock() + defer logger.mu.Unlock() oldHooks := logger.Hooks logger.Hooks = hooks - logger.mu.Unlock() return oldHooks } diff --git a/vendor/github.com/sirupsen/logrus/logrus.go b/vendor/github.com/sirupsen/logrus/logrus.go index 37fc4fef85..d52b4ba73d 100644 --- a/vendor/github.com/sirupsen/logrus/logrus.go +++ b/vendor/github.com/sirupsen/logrus/logrus.go @@ -1,13 +1,13 @@ package logrus import ( + "bytes" "fmt" "log" - "strings" ) // Fields type, used to pass to [WithFields]. -type Fields map[string]interface{} +type Fields map[string]any // Level type // @@ -16,39 +16,56 @@ type Level uint32 // Convert the Level to a string. E.g. [PanicLevel] becomes "panic". func (level Level) String() string { - if b, err := level.MarshalText(); err == nil { - return string(b) - } else { + switch level { + case TraceLevel: + return "trace" + case DebugLevel: + return "debug" + case InfoLevel: + return "info" + case WarnLevel: + return "warning" + case ErrorLevel: + return "error" + case FatalLevel: + return "fatal" + case PanicLevel: + return "panic" + default: return "unknown" } } // ParseLevel takes a string level and returns the Logrus log level constant. func ParseLevel(lvl string) (Level, error) { - switch strings.ToLower(lvl) { - case "panic": + return parseLevel([]byte(lvl)) +} + +func parseLevel(b []byte) (Level, error) { + switch { + case bytes.EqualFold(b, []byte("panic")): return PanicLevel, nil - case "fatal": + case bytes.EqualFold(b, []byte("fatal")): return FatalLevel, nil - case "error": + case bytes.EqualFold(b, []byte("error")): return ErrorLevel, nil - case "warn", "warning": + case bytes.EqualFold(b, []byte("warn")), + bytes.EqualFold(b, []byte("warning")): return WarnLevel, nil - case "info": + case bytes.EqualFold(b, []byte("info")): return InfoLevel, nil - case "debug": + case bytes.EqualFold(b, []byte("debug")): return DebugLevel, nil - case "trace": + case bytes.EqualFold(b, []byte("trace")): return TraceLevel, nil + default: + return 0, fmt.Errorf("not a valid logrus Level: %q", b) } - - var l Level - return l, fmt.Errorf("not a valid logrus Level: %q", lvl) } // UnmarshalText implements encoding.TextUnmarshaler. func (level *Level) UnmarshalText(text []byte) error { - l, err := ParseLevel(string(text)) + l, err := parseLevel(text) if err != nil { return err } @@ -60,23 +77,11 @@ func (level *Level) UnmarshalText(text []byte) error { func (level Level) MarshalText() ([]byte, error) { switch level { - case TraceLevel: - return []byte("trace"), nil - case DebugLevel: - return []byte("debug"), nil - case InfoLevel: - return []byte("info"), nil - case WarnLevel: - return []byte("warning"), nil - case ErrorLevel: - return []byte("error"), nil - case FatalLevel: - return []byte("fatal"), nil - case PanicLevel: - return []byte("panic"), nil + case TraceLevel, DebugLevel, InfoLevel, WarnLevel, ErrorLevel, FatalLevel, PanicLevel: + return []byte(level.String()), nil + default: + return nil, fmt.Errorf("not a valid logrus level %d", level) } - - return nil, fmt.Errorf("not a valid logrus level %d", level) } // AllLevels exposing all logging levels. @@ -91,7 +96,7 @@ var AllLevels = []Level{ } // These are the different logging levels. You can set the logging level to log -// on your instance of logger, obtained with `logrus.New()`. +// on your instance of logger, obtained with [logrus.New]. const ( // PanicLevel level, highest level of severity. Logs and then calls panic with the // message passed to Debug, Info, ... @@ -113,78 +118,110 @@ const ( TraceLevel ) -// Won't compile if StdLogger can't be realized by a log.Logger +// Compile-time interface assertions. var ( - _ StdLogger = &log.Logger{} - _ StdLogger = &Entry{} - _ StdLogger = &Logger{} + _ StdLogger = (*log.Logger)(nil) + _ StdLogger = (*Entry)(nil) + _ StdLogger = (*Logger)(nil) + + _ FieldLogger = (*Logger)(nil) + _ FieldLogger = (*Entry)(nil) + _ FieldLogger = Ext1FieldLogger(nil) + + _ DebugLogger = (*Logger)(nil) + _ InfoLogger = (*Logger)(nil) + _ WarnLogger = (*Logger)(nil) + _ ErrorLogger = (*Logger)(nil) + _ TraceLogger = (*Logger)(nil) + + _ DebugLogger = (*Entry)(nil) + _ InfoLogger = (*Entry)(nil) + _ WarnLogger = (*Entry)(nil) + _ ErrorLogger = (*Entry)(nil) + _ TraceLogger = (*Entry)(nil) + + _ Ext1FieldLogger = (*Logger)(nil) + _ Ext1FieldLogger = (*Entry)(nil) ) // StdLogger is what your logrus-enabled library should take, that way // it'll accept a stdlib logger ([log.Logger]) and a logrus logger. // There's no standard interface, so this is the closest we get, unfortunately. type StdLogger interface { - Print(...interface{}) - Printf(string, ...interface{}) - Println(...interface{}) + Print(args ...any) + Printf(format string, args ...any) + Println(args ...any) - Fatal(...interface{}) - Fatalf(string, ...interface{}) - Fatalln(...interface{}) + Fatal(args ...any) + Fatalf(format string, args ...any) + Fatalln(args ...any) - Panic(...interface{}) - Panicf(string, ...interface{}) - Panicln(...interface{}) + Panic(args ...any) + Panicf(format string, args ...any) + Panicln(args ...any) } // FieldLogger extends the [StdLogger] interface, generalizing // the [Entry] and [Logger] types. type FieldLogger interface { - WithField(key string, value interface{}) *Entry + WithField(key string, value any) *Entry WithFields(fields Fields) *Entry WithError(err error) *Entry - Debugf(format string, args ...interface{}) - Infof(format string, args ...interface{}) - Printf(format string, args ...interface{}) - Warnf(format string, args ...interface{}) - Warningf(format string, args ...interface{}) - Errorf(format string, args ...interface{}) - Fatalf(format string, args ...interface{}) - Panicf(format string, args ...interface{}) - - Debug(args ...interface{}) - Info(args ...interface{}) - Print(args ...interface{}) - Warn(args ...interface{}) - Warning(args ...interface{}) - Error(args ...interface{}) - Fatal(args ...interface{}) - Panic(args ...interface{}) - - Debugln(args ...interface{}) - Infoln(args ...interface{}) - Println(args ...interface{}) - Warnln(args ...interface{}) - Warningln(args ...interface{}) - Errorln(args ...interface{}) - Fatalln(args ...interface{}) - Panicln(args ...interface{}) - - // IsDebugEnabled() bool - // IsInfoEnabled() bool - // IsWarnEnabled() bool - // IsErrorEnabled() bool - // IsFatalEnabled() bool - // IsPanicEnabled() bool + StdLogger + DebugLogger + InfoLogger + WarnLogger + ErrorLogger + + // Legacy warning aliases. These are kept on FieldLogger for backwards + // compatibility, but are intentionally omitted from [WarnLogger]. + + Warning(args ...any) + Warningf(format string, args ...any) + Warningln(args ...any) +} + +// DebugLogger provides convenience functions to log messages at level [DebugLevel]. +type DebugLogger interface { + Debug(args ...any) + Debugf(format string, args ...any) + Debugln(args ...any) +} + +// InfoLogger provides convenience functions to log messages at level [InfoLevel]. +type InfoLogger interface { + Info(args ...any) + Infof(format string, args ...any) + Infoln(args ...any) +} + +// WarnLogger provides convenience functions to log messages at level [WarnLevel]. +type WarnLogger interface { + Warn(args ...any) + Warnf(format string, args ...any) + Warnln(args ...any) +} + +// ErrorLogger provides convenience functions to log messages at level [ErrorLevel]. +type ErrorLogger interface { + Error(args ...any) + Errorf(format string, args ...any) + Errorln(args ...any) +} + +// TraceLogger provides convenience functions to log messages at level [TraceLevel]. +type TraceLogger interface { + Trace(args ...any) + Tracef(format string, args ...any) + Traceln(args ...any) } -// Ext1FieldLogger (the first extension to [FieldLogger]) is superfluous, it is -// here for consistency. Do not use. Use [FieldLogger], [Logger] or [Entry] -// instead. +// Ext1FieldLogger is FieldLogger extended with Trace-level methods. +// +// New code should prefer the smallest applicable interface, such as +// [FieldLogger] or [TraceLogger], or use [Logger] or [Entry] directly. type Ext1FieldLogger interface { FieldLogger - Tracef(format string, args ...interface{}) - Trace(args ...interface{}) - Traceln(args ...interface{}) + TraceLogger } diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go b/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go index 2403de9819..1c6202b8c4 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go @@ -1,11 +1,7 @@ -// +build appengine +//go:build appengine package logrus -import ( - "io" -) - -func checkIfTerminal(w io.Writer) bool { +func checkIfTerminal(_ any) bool { return true } diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go index 69956b425a..ff9531ac44 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go @@ -1,5 +1,4 @@ -// +build darwin dragonfly freebsd netbsd openbsd hurd -// +build !js +//go:build (darwin || dragonfly || freebsd || netbsd || openbsd || hurd) && !tinygo package logrus diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_js.go b/vendor/github.com/sirupsen/logrus/terminal_check_js.go deleted file mode 100644 index ebdae3ec62..0000000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_js.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build js - -package logrus - -func isTerminal(fd int) bool { - return false -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go b/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go index 97af92c68e..17ae9f04fb 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go @@ -1,11 +1,7 @@ -// +build js nacl plan9 +//go:build js || nacl || plan9 || wasi || wasip1 || tinygo package logrus -import ( - "io" -) - -func checkIfTerminal(w io.Writer) bool { +func checkIfTerminal(_ any) bool { return false } diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go b/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go index 3293fb3caa..780a42a57f 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go @@ -1,4 +1,4 @@ -// +build !appengine,!js,!windows,!nacl,!plan9 +//go:build !appengine && !js && !windows && !nacl && !plan9 && !wasi && !wasip1 && !tinygo package logrus @@ -10,7 +10,11 @@ import ( func checkIfTerminal(w io.Writer) bool { switch v := w.(type) { case *os.File: - return isTerminal(int(v.Fd())) + fd := v.Fd() + if fd > uintptr(^uint(0)>>1) { + return false + } + return isTerminal(int(fd)) default: return false } diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go b/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go index f6710b3bd0..8d9b26fcad 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go @@ -1,3 +1,5 @@ +//go:build solaris && !tinygo + package logrus import ( diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go index c9aed267a4..b161506d3f 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go @@ -1,7 +1,4 @@ -//go:build (linux || aix || zos) && !js && !wasi -// +build linux aix zos -// +build !js -// +build !wasi +//go:build (linux || aix || zos) && !tinygo package logrus diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_wasi.go b/vendor/github.com/sirupsen/logrus/terminal_check_wasi.go deleted file mode 100644 index 2822b212fb..0000000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_wasi.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build wasi -// +build wasi - -package logrus - -func isTerminal(fd int) bool { - return false -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_wasip1.go b/vendor/github.com/sirupsen/logrus/terminal_check_wasip1.go deleted file mode 100644 index 108a6be12b..0000000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_wasip1.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build wasip1 -// +build wasip1 - -package logrus - -func isTerminal(fd int) bool { - return false -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_windows.go b/vendor/github.com/sirupsen/logrus/terminal_check_windows.go index 2879eb50ea..5fd3c43139 100644 --- a/vendor/github.com/sirupsen/logrus/terminal_check_windows.go +++ b/vendor/github.com/sirupsen/logrus/terminal_check_windows.go @@ -1,4 +1,4 @@ -// +build !appengine,!js,windows +//go:build windows && !appengine package logrus diff --git a/vendor/github.com/sirupsen/logrus/text_formatter.go b/vendor/github.com/sirupsen/logrus/text_formatter.go index 6dfeb18b10..9b0395adf0 100644 --- a/vendor/github.com/sirupsen/logrus/text_formatter.go +++ b/vendor/github.com/sirupsen/logrus/text_formatter.go @@ -3,30 +3,31 @@ package logrus import ( "bytes" "fmt" + "maps" "os" "runtime" - "sort" + "slices" "strconv" "strings" "sync" "time" - "unicode/utf8" ) -const ( - red = 31 - yellow = 33 - blue = 36 - gray = 37 -) - -var baseTimestamp time.Time - -func init() { - baseTimestamp = time.Now() -} - -// TextFormatter formats logs into text +var baseTimestamp = time.Now() + +// TextFormatter formats logs into text. +// +// Output is logfmt-like: key=value pairs separated by spaces. Fields from +// [Entry.Data] are included together with the standard fields derived from the +// entry. If a field conflicts with a standard field, it is prefixed with +// "fields.". Standard field names can be customized through FieldMap. +// +// Field keys are written as-is (unquoted and unescaped) in the plain +// (non-colored) format; only field values may be quoted depending on +// DisableQuote, ForceQuote, QuoteEmptyFields, and the value content. +// +// When colors are enabled, ANSI escape sequences may be added for presentation. +// For fully escaped structured output (including safe keys), use JSONFormatter. type TextFormatter struct { // Set to true to bypass checking for a TTY before outputting colors. ForceColors bool @@ -64,7 +65,7 @@ type TextFormatter struct { // be desired. DisableSorting bool - // The keys sorting function, when uninitialized it uses sort.Strings. + // The keys sorting function, when uninitialized it uses slices.Sort. SortingFunc func([]string) // Disables the truncation of the level text to 4 characters. @@ -77,16 +78,22 @@ type TextFormatter struct { // QuoteEmptyFields will wrap empty fields in quotes if true QuoteEmptyFields bool - // Whether the logger's out is to a terminal - isTerminal bool + // Whether the logger's out is to a terminal. Don't use this field + // directly; use TextFormatter.isTerminal instead. + terminal bool // FieldMap allows users to customize the names of keys for default fields. + // Mapped keys are written as-is, so they should be safe for plain-text output. + // // As an example: + // // formatter := &TextFormatter{ - // FieldMap: FieldMap{ - // FieldKeyTime: "@timestamp", - // FieldKeyLevel: "@level", - // FieldKeyMsg: "@message"}} + // FieldMap: FieldMap{ + // FieldKeyTime: "@timestamp", + // FieldKeyLevel: "@level", + // FieldKeyMsg: "@message", + // }, + // } FieldMap FieldMap // CallerPrettyfier can be set by the user to modify the content @@ -96,54 +103,78 @@ type TextFormatter struct { CallerPrettyfier func(*runtime.Frame) (function string, file string) terminalInitOnce sync.Once - - // The max length of the level text, generated dynamically on init - levelTextMaxLength int } -func (f *TextFormatter) init(entry *Entry) { - if entry.Logger != nil { - f.isTerminal = checkIfTerminal(entry.Logger.Out) - } - // Get the max length of the level text - for _, level := range AllLevels { - levelTextLength := utf8.RuneCount([]byte(level.String())) - if levelTextLength > f.levelTextMaxLength { - f.levelTextMaxLength = levelTextLength - } +func (f *TextFormatter) isTerminal(entry *Entry) bool { + if entry == nil || entry.Logger == nil { + // Don't run the terminalInitOnce without a logger, otherwise we'd + // cache the default (false) forever even if a logger is attached + // later. + return false } -} -func (f *TextFormatter) isColored() bool { - isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows")) + f.terminalInitOnce.Do(func() { + entry.Logger.mu.Lock() + out := entry.Logger.Out + entry.Logger.mu.Unlock() - if f.EnvironmentOverrideColors { - switch force, ok := os.LookupEnv("CLICOLOR_FORCE"); { - case ok && force != "0": - isColored = true - case ok && force == "0", os.Getenv("CLICOLOR") == "0": - isColored = false - } + f.terminal = checkIfTerminal(out) + }) + + return f.terminal +} + +func (f *TextFormatter) isColored(isTerminal bool) bool { + if f.DisableColors { + return false } - return isColored && !f.DisableColors + colored := f.ForceColors || isTerminal + if !f.EnvironmentOverrideColors { + return colored + } + if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok { + return force != "0" + } + if os.Getenv("CLICOLOR") == "0" { + return false + } + return colored } // Format renders a single log entry func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { - data := make(Fields) - for k, v := range entry.Data { - data[k] = v - } - prefixFieldClashes(data, f.FieldMap, entry.HasCaller()) + data := make(Fields, len(entry.Data)) + maps.Copy(data, entry.Data) + isColored := f.isColored(f.isTerminal(entry)) + + caller := entry.Caller + hasCaller := caller != nil + prefixFieldClashes(data, f.FieldMap, hasCaller) keys := make([]string, 0, len(data)) for k := range data { keys = append(keys, k) } - var funcVal, fileVal string + b := entry.Buffer + if b == nil { + b = new(bytes.Buffer) + } - fixedKeys := make([]string, 0, 4+len(data)) + if isColored { + f.printColored(b, entry, keys, data) + } else { + f.printPlain(b, entry, keys, data) + } + + return b.Bytes(), nil +} + +func (f *TextFormatter) printPlain(b *bytes.Buffer, entry *Entry, keys []string, data Fields) { + caller := entry.Caller + hasCaller := caller != nil + + fixedKeys := make([]string, 0, len(keys)+defaultFields) if !f.DisableTimestamp { fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime)) } @@ -154,12 +185,14 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { if entry.err != "" { fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError)) } - if entry.HasCaller() { + + var funcVal, fileVal string + if caller != nil { if f.CallerPrettyfier != nil { - funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + funcVal, fileVal = f.CallerPrettyfier(caller) } else { - funcVal = entry.Caller.Function - fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) + funcVal = caller.Function + fileVal = caller.File + ":" + strconv.FormatInt(int64(caller.Line), 10) } if funcVal != "" { @@ -172,169 +205,255 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { if !f.DisableSorting { if f.SortingFunc == nil { - sort.Strings(keys) + // Default sorting does not sort the "fixed keys"; + // see https://github.com/sirupsen/logrus/commit/73bc94e60c753099e8bae902f81fbd6e7dd95f26 + slices.Sort(keys) fixedKeys = append(fixedKeys, keys...) } else { - if !f.isColored() { - fixedKeys = append(fixedKeys, keys...) - f.SortingFunc(fixedKeys) - } else { - f.SortingFunc(keys) - } + fixedKeys = append(fixedKeys, keys...) + f.SortingFunc(fixedKeys) } } else { fixedKeys = append(fixedKeys, keys...) } - var b *bytes.Buffer - if entry.Buffer != nil { - b = entry.Buffer - } else { - b = &bytes.Buffer{} - } - - f.terminalInitOnce.Do(func() { f.init(entry) }) - - timestampFormat := f.TimestampFormat - if timestampFormat == "" { - timestampFormat = defaultTimestampFormat - } - if f.isColored() { - f.printColored(b, entry, keys, data, timestampFormat) - } else { - - for _, key := range fixedKeys { - var value interface{} - switch { - case key == f.FieldMap.resolve(FieldKeyTime): - value = entry.Time.Format(timestampFormat) - case key == f.FieldMap.resolve(FieldKeyLevel): - value = entry.Level.String() - case key == f.FieldMap.resolve(FieldKeyMsg): - value = entry.Message - case key == f.FieldMap.resolve(FieldKeyLogrusError): - value = entry.err - case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller(): - value = funcVal - case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller(): - value = fileVal - default: - value = data[key] + for _, key := range fixedKeys { + var value any + switch { + case key == f.FieldMap.resolve(FieldKeyTime): + if f.TimestampFormat == "" { + value = entry.Time.Format(defaultTimestampFormat) + } else { + value = entry.Time.Format(f.TimestampFormat) } - f.appendKeyValue(b, key, value) + case key == f.FieldMap.resolve(FieldKeyLevel): + value = entry.Level.String() + case key == f.FieldMap.resolve(FieldKeyMsg): + value = entry.Message + case key == f.FieldMap.resolve(FieldKeyLogrusError): + value = entry.err + case key == f.FieldMap.resolve(FieldKeyFunc) && hasCaller: + value = funcVal + case key == f.FieldMap.resolve(FieldKeyFile) && hasCaller: + value = fileVal + default: + value = data[key] } + f.appendKeyValue(b, key, value) } b.WriteByte('\n') - return b.Bytes(), nil } -func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) { - var levelColor int - switch entry.Level { - case DebugLevel, TraceLevel: - levelColor = gray - case WarnLevel: - levelColor = yellow - case ErrorLevel, FatalLevel, PanicLevel: - levelColor = red - case InfoLevel: - levelColor = blue - default: - levelColor = blue - } - - levelText := strings.ToUpper(entry.Level.String()) - if !f.DisableLevelTruncation && !f.PadLevelText { - levelText = levelText[0:4] - } - if f.PadLevelText { - // Generates the format string used in the next line, for example "%-6s" or "%-7s". - // Based on the max level text length. - formatString := "%-" + strconv.Itoa(f.levelTextMaxLength) + "s" - // Formats the level text by appending spaces up to the max length, for example: - // - "INFO " - // - "WARNING" - levelText = fmt.Sprintf(formatString, levelText) - } - +func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields) { // Remove a single newline if it already exists in the message to keep // the behavior of logrus text_formatter the same as the stdlib log package entry.Message = strings.TrimSuffix(entry.Message, "\n") - caller := "" - if entry.HasCaller() { - funcVal := fmt.Sprintf("%s()", entry.Caller.Function) - fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line) - + var callerText string + if caller := entry.Caller; caller != nil { + var funcVal, fileVal string if f.CallerPrettyfier != nil { - funcVal, fileVal = f.CallerPrettyfier(entry.Caller) + funcVal, fileVal = f.CallerPrettyfier(caller) + } else { + if caller.Function != "" { + funcVal = caller.Function + "()" + } + fileVal = caller.File + ":" + strconv.FormatInt(int64(caller.Line), 10) } if fileVal == "" { - caller = funcVal + callerText = funcVal } else if funcVal == "" { - caller = fileVal + callerText = fileVal } else { - caller = fileVal + " " + funcVal + callerText = fileVal + " " + funcVal } } + levelText := levelPrefix(entry.Level, f.DisableLevelTruncation, f.PadLevelText) switch { case f.DisableTimestamp: - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message) + _, _ = fmt.Fprintf(b, "%s%s %-44s ", levelText, callerText, entry.Message) case !f.FullTimestamp: - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message) + _, _ = fmt.Fprintf(b, "%s[%04d]%s %-44s ", levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), callerText, entry.Message) default: - fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message) + timestampFormat := f.TimestampFormat + if timestampFormat == "" { + timestampFormat = defaultTimestampFormat + } + _, _ = fmt.Fprintf(b, "%s[%s]%s %-44s ", levelText, entry.Time.Format(timestampFormat), callerText, entry.Message) } + + if !f.DisableSorting { + if f.SortingFunc == nil { + slices.Sort(keys) + } else { + f.SortingFunc(keys) + } + } + + // Keys use the same color as the level-prefix. for _, k := range keys { - v := data[k] - fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k) - f.appendValue(b, v) + b.WriteByte(' ') + b.WriteString(colorize(entry.Level, k)) + b.WriteByte('=') + f.appendValue(b, data[k]) } + + b.WriteByte('\n') } -func (f *TextFormatter) needsQuoting(text string) bool { - if f.ForceQuote { - return true +// appendKeyValue writes key=value. Keys are written verbatim (unquoted/unescaped); +// values are subject to quoting/escaping. +func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value any) { + if b.Len() > 0 { + b.WriteByte(' ') } - if f.QuoteEmptyFields && len(text) == 0 { - return true + b.WriteString(key) + b.WriteByte('=') + f.appendValue(b, value) +} + +func (f *TextFormatter) appendValue(b *bytes.Buffer, value any) { + // Fast paths. + switch v := value.(type) { + case string: + f.appendString(b, v) + return + case []byte: + f.appendBytes(b, v) + return + case bool: + var raw [8]byte + f.appendBytes(b, strconv.AppendBool(raw[:0], v)) + return + case error: + f.appendString(b, v.Error()) + return + case fmt.Stringer: + f.appendString(b, v.String()) + return } - if f.DisableQuote { - return false + + // Handle common primitives. + var raw [64]byte + var num []byte + + switch v := value.(type) { + case int: + num = strconv.AppendInt(raw[:0], int64(v), 10) + case int8: + num = strconv.AppendInt(raw[:0], int64(v), 10) + case int16: + num = strconv.AppendInt(raw[:0], int64(v), 10) + case int32: + num = strconv.AppendInt(raw[:0], int64(v), 10) + case int64: + num = strconv.AppendInt(raw[:0], v, 10) + + case uint: + num = strconv.AppendUint(raw[:0], uint64(v), 10) + case uint8: + num = strconv.AppendUint(raw[:0], uint64(v), 10) + case uint16: + num = strconv.AppendUint(raw[:0], uint64(v), 10) + case uint32: + num = strconv.AppendUint(raw[:0], uint64(v), 10) + case uint64: + num = strconv.AppendUint(raw[:0], v, 10) + case uintptr: + num = strconv.AppendUint(raw[:0], uint64(v), 10) + + case float32: + num = strconv.AppendFloat(raw[:0], float64(v), 'g', -1, 32) + case float64: + num = strconv.AppendFloat(raw[:0], v, 'g', -1, 64) + + default: + f.appendString(b, fmt.Sprint(value)) + return + } + + f.appendNumeric(b, num) +} + +func (f *TextFormatter) appendString(b *bytes.Buffer, s string) { + quote := f.ForceQuote || (f.QuoteEmptyFields && len(s) == 0) || (!f.DisableQuote && needsQuoting(s)) + if !quote { + b.WriteString(s) + return } - for _, ch := range text { - //nolint:staticcheck // QF1001: could apply De Morgan's law - if !((ch >= 'a' && ch <= 'z') || - (ch >= 'A' && ch <= 'Z') || - (ch >= '0' && ch <= '9') || - ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') { + if len(s) == 0 { + b.WriteString(`""`) + return + } + + var tmp [128]byte + b.Write(strconv.AppendQuote(tmp[:0], s)) +} + +func (f *TextFormatter) appendBytes(b *bytes.Buffer, bs []byte) { + quote := f.ForceQuote || (f.QuoteEmptyFields && len(bs) == 0) || (!f.DisableQuote && needsQuotingBytes(bs)) + if !quote { + b.Write(bs) + return + } + if len(bs) == 0 { + b.WriteString(`""`) + return + } + + var tmp [128]byte + b.Write(strconv.AppendQuote(tmp[:0], string(bs))) +} + +func (f *TextFormatter) appendNumeric(b *bytes.Buffer, out []byte) { + if f.ForceQuote { + var tmp [128]byte + b.Write(strconv.AppendQuote(tmp[:0], string(out))) + return + } + b.Write(out) +} + +// needsQuoting returns true if the string contains any byte that +// requires quoting. It returns false when every byte is "safe" according +// to isSafeByte. +func needsQuoting(s string) bool { + // use an index loop (avoid rune decoding). + for i := range len(s) { + c := s[i] + if !isSafeByte(c) { return true } } return false } -func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { - if b.Len() > 0 { - b.WriteByte(' ') +// needsQuotingBytes returns true if the byte slice contains any byte that +// requires quoting. It returns false when every byte is "safe" according +// to isSafeByte. +func needsQuotingBytes(bs []byte) bool { + for _, c := range bs { + if !isSafeByte(c) { + return true + } } - b.WriteString(key) - b.WriteByte('=') - f.appendValue(b, value) + return false } -func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) { - stringVal, ok := value.(string) - if !ok { - stringVal = fmt.Sprint(value) +// isSafeByte returns true if the byte is allowed unquoted (ASCII and in the allowlist). +// It purposely uses byte arithmetic (no runes) for performance. +func isSafeByte(ch byte) bool { + ok := ch < 0x80 && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) + if ok { + return true } - - if !f.needsQuoting(stringVal) { - b.WriteString(stringVal) - } else { - fmt.Fprintf(b, "%q", stringVal) + switch ch { + case '-', '.', '_', '/', '@', '^', '+': + return true + default: + return false } } diff --git a/vendor/github.com/sirupsen/logrus/writer.go b/vendor/github.com/sirupsen/logrus/writer.go index 074fd4b8bd..30e34cda72 100644 --- a/vendor/github.com/sirupsen/logrus/writer.go +++ b/vendor/github.com/sirupsen/logrus/writer.go @@ -30,7 +30,7 @@ func (entry *Entry) Writer() *io.PipeWriter { func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { reader, writer := io.Pipe() - var printFunc func(args ...interface{}) + printFunc := entry.Print // Determine which log function to use based on the specified log level switch level { @@ -48,8 +48,6 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { printFunc = entry.Fatal case PanicLevel: printFunc = entry.Panic - default: - printFunc = entry.Print } // Start a new goroutine to scan the input and write it to the logger using the specified print function. @@ -63,7 +61,7 @@ func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { } // writerScanner scans the input from the reader and writes it to the logger -func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) { +func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...any)) { scanner := bufio.NewScanner(reader) // Set the buffer size to the maximum token size to avoid buffer overflows diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index c592f6ad5f..a19a89279c 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -84,7 +84,7 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar return Equal(t, expected, actual, append([]interface{}{msg}, args...)...) } -// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -124,7 +124,7 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) } -// Errorf asserts that a function returned an error (i.e. not `nil`). +// Errorf asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // assert.Errorf(t, err, "error message %s", "formatted") @@ -144,8 +144,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) } -// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") @@ -190,10 +190,10 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick // time.Sleep(8*time.Second) // externalValue = true // }() -// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// assert.EventuallyWithTf(t, func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "error message %s", "formatted") func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -552,7 +552,7 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool return NoDirExists(t, path, append([]interface{}{msg}, args...)...) } -// NoErrorf asserts that a function returned no error (i.e. `nil`). +// NoErrorf asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if assert.NoErrorf(t, err, "error message %s", "formatted") { @@ -849,7 +849,19 @@ func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...) } -// YAMLEqf asserts that two YAML strings are equivalent. +// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// assert.YAMLEqf(t, expected, actual, "error message %s", "formatted") func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 58db928450..cd2a86061c 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -146,7 +146,7 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs return Equal(a.t, expected, actual, msgAndArgs...) } -// EqualError asserts that a function returned an error (i.e. not `nil`) +// EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -158,7 +158,7 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... return EqualError(a.t, theError, errString, msgAndArgs...) } -// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -240,7 +240,7 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string return Equalf(a.t, expected, actual, msg, args...) } -// Error asserts that a function returned an error (i.e. not `nil`). +// Error asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // a.Error(err) @@ -269,8 +269,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. return ErrorAsf(a.t, err, target, msg, args...) } -// ErrorContains asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContains(err, expectedErrorSubString) @@ -281,8 +281,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . return ErrorContains(a.t, theError, contains, msgAndArgs...) } -// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") @@ -311,7 +311,7 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter return ErrorIsf(a.t, err, target, msg, args...) } -// Errorf asserts that a function returned an error (i.e. not `nil`). +// Errorf asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // a.Errorf(err, "error message %s", "formatted") @@ -372,10 +372,10 @@ func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor // time.Sleep(8*time.Second) // externalValue = true // }() -// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// a.EventuallyWithTf(func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "error message %s", "formatted") func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1096,7 +1096,7 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) return NoDirExistsf(a.t, path, msg, args...) } -// NoError asserts that a function returned no error (i.e. `nil`). +// NoError asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if a.NoError(err) { @@ -1109,7 +1109,7 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { return NoError(a.t, err, msgAndArgs...) } -// NoErrorf asserts that a function returned no error (i.e. `nil`). +// NoErrorf asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if a.NoErrorf(err, "error message %s", "formatted") { @@ -1690,7 +1690,19 @@ func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Ti return WithinRangef(a.t, actual, start, end, msg, args...) } -// YAMLEq asserts that two YAML strings are equivalent. +// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// a.YAMLEq(expected, actual) func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1698,7 +1710,19 @@ func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interf return YAMLEq(a.t, expected, actual, msgAndArgs...) } -// YAMLEqf asserts that two YAML strings are equivalent. +// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// a.YAMLEqf(expected, actual, "error message %s", "formatted") func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go index 2fdf80fdd3..a44b40ed3f 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -9,7 +9,7 @@ import ( func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { objKind := reflect.TypeOf(object).Kind() if objKind != reflect.Slice && objKind != reflect.Array { - return false + return Fail(t, fmt.Sprintf("object %T is not an ordered collection", object), msgAndArgs...) } objValue := reflect.ValueOf(object) @@ -50,6 +50,9 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareR // assert.IsIncreasing(t, []float{1, 2}) // assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return isOrdered(t, object, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } @@ -59,6 +62,9 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonIncreasing(t, []float{2, 1}) // assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return isOrdered(t, object, []compareResult{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } @@ -68,6 +74,9 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // assert.IsDecreasing(t, []float{2, 1}) // assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return isOrdered(t, object, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } @@ -77,5 +86,8 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonDecreasing(t, []float{1, 2}) // assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } return isOrdered(t, object, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index de8de0cb6c..1419e47761 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -17,11 +17,10 @@ import ( "unicode" "unicode/utf8" - "github.com/davecgh/go-spew/spew" - "github.com/pmezard/go-difflib/difflib" - // Wrapper around gopkg.in/yaml.v3 "github.com/stretchr/testify/assert/yaml" + "github.com/stretchr/testify/internal/difflib" + "github.com/stretchr/testify/internal/spew" ) //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl" @@ -33,19 +32,19 @@ type TestingT interface { // ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful // for table driven tests. -type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) bool +type ComparisonAssertionFunc = func(TestingT, interface{}, interface{}, ...interface{}) bool // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful // for table driven tests. -type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool +type ValueAssertionFunc = func(TestingT, interface{}, ...interface{}) bool // BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful // for table driven tests. -type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool +type BoolAssertionFunc = func(TestingT, bool, ...interface{}) bool // ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful // for table driven tests. -type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool +type ErrorAssertionFunc = func(TestingT, error, ...interface{}) bool // PanicAssertionFunc is a common function prototype when validating a panic value. Can be useful // for table driven tests. @@ -325,13 +324,15 @@ func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { func indentMessageLines(message string, longestLabelLen int) string { outBuf := new(bytes.Buffer) - for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { - // no need to align first line because it starts at the correct location (after the label) - if i != 0 { - // append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab - outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") + scanner := bufio.NewScanner(strings.NewReader(message)) + for firstLine := true; scanner.Scan(); firstLine = false { + if !firstLine { + fmt.Fprint(outBuf, "\n\t"+strings.Repeat(" ", longestLabelLen+1)+"\t") } - outBuf.WriteString(scanner.Text()) + fmt.Fprint(outBuf, scanner.Text()) + } + if err := scanner.Err(); err != nil { + return fmt.Sprintf("cannot display message: %s", err) } return outBuf.String() @@ -544,9 +545,8 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b if !same { // both are pointers but not the same type & pointing to the same address return Fail(t, fmt.Sprintf("Not same: \n"+ - "expected: %p %#[1]v\n"+ - "actual : %p %#[2]v", - expected, actual), msgAndArgs...) + "expected: %[2]s (%[1]T)(%[1]p)\n"+ + "actual : %[4]s (%[3]T)(%[3]p)", expected, truncatingFormat("%#v", expected), actual, truncatingFormat("%#v", actual)), msgAndArgs...) } return true @@ -571,8 +571,8 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} if same { return Fail(t, fmt.Sprintf( - "Expected and actual point to the same object: %p %#[1]v", - expected), msgAndArgs...) + "Expected and actual point to the same object: %p %s", + expected, truncatingFormat("%#v", expected)), msgAndArgs...) } return true } @@ -604,25 +604,26 @@ func samePointers(first, second interface{}) (same bool, ok bool) { // to a type conversion in the Go grammar. func formatUnequalValues(expected, actual interface{}) (e string, a string) { if reflect.TypeOf(expected) != reflect.TypeOf(actual) { - return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)), - fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual)) + return fmt.Sprintf("%T(%s)", expected, truncatingFormat("%#v", expected)), + fmt.Sprintf("%T(%s)", actual, truncatingFormat("%#v", actual)) } switch expected.(type) { case time.Duration: return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual) } - return truncatingFormat(expected), truncatingFormat(actual) + return truncatingFormat("%#v", expected), truncatingFormat("%#v", actual) } // truncatingFormat formats the data and truncates it if it's too long. // // This helps keep formatted error messages lines from exceeding the // bufio.MaxScanTokenSize max line length that the go testing framework imposes. -func truncatingFormat(data interface{}) string { - value := fmt.Sprintf("%#v", data) - max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed. - if len(value) > max { - value = value[0:max] + "<... truncated>" +func truncatingFormat(format string, data interface{}) string { + value := fmt.Sprintf(format, data) + // Give us space for two truncated objects and the surrounding sentence. + maxMessageSize := bufio.MaxScanTokenSize/2 - 100 + if len(value) > maxMessageSize { + value = value[0:maxMessageSize] + "<... truncated>" } return value } @@ -743,7 +744,7 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } - return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) + return Fail(t, fmt.Sprintf("Expected nil, but got: %s", truncatingFormat("%#v", object)), msgAndArgs...) } // isEmpty gets whether the specified object is considered empty or not. @@ -793,7 +794,7 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } - Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) + Fail(t, fmt.Sprintf("Should be empty, but was %s", truncatingFormat("%v", object)), msgAndArgs...) } return pass @@ -836,11 +837,11 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) } l, ok := getLen(object) if !ok { - return Fail(t, fmt.Sprintf("\"%v\" could not be applied builtin len()", object), msgAndArgs...) + return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", truncatingFormat("%v", object)), msgAndArgs...) } if l != length { - return Fail(t, fmt.Sprintf("\"%v\" should have %d item(s), but has %d", object, length, l), msgAndArgs...) + return Fail(t, fmt.Sprintf("%q should have %d item(s), but has %d", truncatingFormat("%v", object), length, l), msgAndArgs...) } return true } @@ -889,7 +890,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ } if ObjectsAreEqual(expected, actual) { - return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) + return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) } return true @@ -904,7 +905,7 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte } if ObjectsAreEqualValues(expected, actual) { - return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) + return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) } return true @@ -964,10 +965,10 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo ok, found := containsElement(s, contains) if !ok { - return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s could not be applied builtin len()", truncatingFormat("%#v", s)), msgAndArgs...) } if !found { - return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...) } return true @@ -986,10 +987,10 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) ok, found := containsElement(s, contains) if !ok { - return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s could not be applied builtin len()", truncatingFormat("%#v", s)), msgAndArgs...) } if found { - return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s should not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...) } return true @@ -1031,10 +1032,10 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok av := actualMap.MapIndex(k) if !av.IsValid() { - return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...) } if !ObjectsAreEqual(ev.Interface(), av.Interface()) { - return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...) } } @@ -1056,7 +1057,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) } if !found { - return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", list), element), msgAndArgs...) } } @@ -1082,12 +1083,12 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) listKind := reflect.TypeOf(list).Kind() if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", list, listKind), msgAndArgs...) } subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", subset, subsetKind), msgAndArgs...) } if subsetKind == reflect.Map && listKind == reflect.Map { @@ -1106,7 +1107,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) } } - return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%#v", subset), truncatingFormat("%#v", list)), msgAndArgs...) } subsetList := reflect.ValueOf(subset) @@ -1121,14 +1122,14 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { - return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) } if !found { return true } } - return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%#v", subset), truncatingFormat("%#v", list)), msgAndArgs...) } // ElementsMatch asserts that the specified listA(array, slice...) is equal to specified @@ -1343,9 +1344,15 @@ func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs . if !funcDidPanic { return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) } - panicErr, ok := panicValue.(error) - if !ok || panicErr.Error() != errString { - return Fail(t, fmt.Sprintf("func %#v should panic with error message:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, errString, panicValue, panickedStack), msgAndArgs...) + panicErr, isError := panicValue.(error) + if !isError || panicErr.Error() != errString { + msg := fmt.Sprintf("func %#v should panic with error message:\t%#v\n", f, errString) + if isError { + msg += fmt.Sprintf("\tError message:\t%#v\n", panicErr.Error()) + } + msg += fmt.Sprintf("\tPanic value:\t%#v\n", panicValue) + msg += fmt.Sprintf("\tPanic stack:\t%s\n", panickedStack) + return Fail(t, msg, msgAndArgs...) } return true @@ -1624,7 +1631,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m Errors */ -// NoError asserts that a function returned no error (i.e. `nil`). +// NoError asserts that a function returned a nil error (ie. no error). // // actualObj, err := SomeFunction() // if assert.NoError(t, err) { @@ -1635,13 +1642,13 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } - return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) + return Fail(t, fmt.Sprintf("Received unexpected error:\n%s", truncatingFormat("%+v", err)), msgAndArgs...) } return true } -// Error asserts that a function returned an error (i.e. not `nil`). +// Error asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // assert.Error(t, err) @@ -1656,7 +1663,7 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { return true } -// EqualError asserts that a function returned an error (i.e. not `nil`) +// EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -1674,13 +1681,13 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte if expected != actual { return Fail(t, fmt.Sprintf("Error message not equal:\n"+ "expected: %q\n"+ - "actual : %q", expected, actual), msgAndArgs...) + "actual : %s", expected, truncatingFormat("%q", actual)), msgAndArgs...) } return true } -// ErrorContains asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // assert.ErrorContains(t, err, expectedErrorSubString) @@ -1694,7 +1701,7 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in actual := theError.Error() if !strings.Contains(actual, contains) { - return Fail(t, fmt.Sprintf("Error %#v does not contain %#v", actual, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("Error %s does not contain %#v", truncatingFormat("%#v", actual), contains), msgAndArgs...) } return true @@ -1760,7 +1767,7 @@ func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { h.Helper() } if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { - return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...) + return Fail(t, fmt.Sprintf("Should be zero, but was %s", truncatingFormat("%v", i)), msgAndArgs...) } return true } @@ -1874,7 +1881,19 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) } -// YAMLEq asserts that two YAML strings are equivalent. +// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// assert.YAMLEq(t, expected, actual) func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -2188,8 +2207,8 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { chain := buildErrorChainString(err, false) return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ - "expected: %q\n"+ - "in chain: %s", expectedText, chain, + "expected: %s\n"+ + "in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain), ), msgAndArgs...) } @@ -2211,8 +2230,8 @@ func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { chain := buildErrorChainString(err, false) return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ - "found: %q\n"+ - "in chain: %s", expectedText, chain, + "found: %s\n"+ + "in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain), ), msgAndArgs...) } @@ -2236,7 +2255,7 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{ return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ "expected: %s\n"+ - "in chain: %s", expectedType, chain, + "in chain: %s", expectedType, truncatingFormat("%s", chain), ), msgAndArgs...) } @@ -2254,7 +2273,7 @@ func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interfa return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ "found: %s\n"+ - "in chain: %s", reflect.TypeOf(target).Elem().String(), chain, + "in chain: %s", reflect.TypeOf(target).Elem().String(), truncatingFormat("%s", chain), ), msgAndArgs...) } diff --git a/vendor/github.com/stretchr/testify/assert/doc.go b/vendor/github.com/stretchr/testify/assert/doc.go index a0b953aa5c..c111589c7e 100644 --- a/vendor/github.com/stretchr/testify/assert/doc.go +++ b/vendor/github.com/stretchr/testify/assert/doc.go @@ -40,8 +40,8 @@ // // # Assertions // -// Assertions allow you to easily write test code, and are global funcs in the `assert` package. -// All assertion functions take, as the first argument, the `*testing.T` object provided by the +// Assertions allow you to easily write test code, and are global funcs in the assert package. +// All assertion functions take, as the first argument, the [*testing.T] object provided by the // testing framework. This allows the assertion funcs to write the failings and other details to // the correct place. // diff --git a/vendor/github.com/stretchr/testify/internal/difflib/LICENSE b/vendor/github.com/stretchr/testify/internal/difflib/LICENSE new file mode 100644 index 0000000000..485be13c67 --- /dev/null +++ b/vendor/github.com/stretchr/testify/internal/difflib/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2013, Patrick Mezard +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + The names of its contributors may not be used to endorse or promote +products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/github.com/stretchr/testify/internal/difflib/difflib.go b/vendor/github.com/stretchr/testify/internal/difflib/difflib.go new file mode 100644 index 0000000000..9984599b4f --- /dev/null +++ b/vendor/github.com/stretchr/testify/internal/difflib/difflib.go @@ -0,0 +1,592 @@ +// Package difflib is a partial port of Python difflib module. +// +// It provides tools to compare sequences of strings and generate textual diffs. +// +// The following class and functions have been ported: +// +// - SequenceMatcher +// +// - unified_diff +// +// Getting unified diffs was the main goal of the port. Keep in mind this code +// is mostly suitable to output text differences in a human friendly way, there +// are no guarantees generated diffs are consumable by patch(1). +// +// This package was adopted from [github.com/pmezard/go-difflib] which +// is no longer maintained. +// +// [github.com/pmezard/go-difflib]: https://github.com/pmezard/go-difflib +package difflib + +import ( + "bufio" + "bytes" + "fmt" + "io" + "strings" +) + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +type Match struct { + A int + B int + Size int +} + +type OpCode struct { + Tag byte + I1 int + I2 int + J1 int + J2 int +} + +// SequenceMatcher compares sequence of strings. The basic +// algorithm predates, and is a little fancier than, an algorithm +// published in the late 1980's by Ratcliff and Obershelp under the +// hyperbolic name "gestalt pattern matching". The basic idea is to find +// the longest contiguous matching subsequence that contains no "junk" +// elements (R-O doesn't address junk). The same idea is then applied +// recursively to the pieces of the sequences to the left and to the right +// of the matching subsequence. This does not yield minimal edit +// sequences, but does tend to yield matches that "look right" to people. +// +// SequenceMatcher tries to compute a "human-friendly diff" between two +// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the +// longest *contiguous* & junk-free matching subsequence. That's what +// catches peoples' eyes. The Windows(tm) windiff has another interesting +// notion, pairing up elements that appear uniquely in each sequence. +// That, and the method here, appear to yield more intuitive difference +// reports than does diff. This method appears to be the least vulnerable +// to synching up on blocks of "junk lines", though (like blank lines in +// ordinary text files, or maybe "

" lines in HTML files). That may be +// because this is the only method of the 3 that has a *concept* of +// "junk" . +// +// Timing: Basic R-O is cubic time worst case and quadratic time expected +// case. SequenceMatcher is quadratic time for the worst case and has +// expected-case behavior dependent in a complicated way on how many +// elements the sequences have in common; best case time is linear. +type SequenceMatcher struct { + a []string + b []string + b2j map[string][]int + IsJunk func(string) bool + autoJunk bool + bJunk map[string]struct{} + matchingBlocks []Match + fullBCount map[string]int + bPopular map[string]struct{} + opCodes []OpCode +} + +func NewMatcher(a, b []string) *SequenceMatcher { + m := SequenceMatcher{autoJunk: true} + m.SetSeqs(a, b) + return &m +} + +// Set two sequences to be compared. +func (m *SequenceMatcher) SetSeqs(a, b []string) { + m.SetSeq1(a) + m.SetSeq2(b) +} + +// Set the first sequence to be compared. The second sequence to be compared is +// not changed. +// +// SequenceMatcher computes and caches detailed information about the second +// sequence, so if you want to compare one sequence S against many sequences, +// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other +// sequences. +// +// See also SetSeqs() and SetSeq2(). +func (m *SequenceMatcher) SetSeq1(a []string) { + if &a == &m.a { + return + } + m.a = a + m.matchingBlocks = nil + m.opCodes = nil +} + +// Set the second sequence to be compared. The first sequence to be compared is +// not changed. +func (m *SequenceMatcher) SetSeq2(b []string) { + if &b == &m.b { + return + } + m.b = b + m.matchingBlocks = nil + m.opCodes = nil + m.fullBCount = nil + m.chainB() +} + +func (m *SequenceMatcher) chainB() { + // Populate line -> index mapping + b2j := map[string][]int{} + for i, s := range m.b { + indices := b2j[s] + indices = append(indices, i) + b2j[s] = indices + } + + // Purge junk elements + m.bJunk = map[string]struct{}{} + if m.IsJunk != nil { + junk := m.bJunk + for s, _ := range b2j { + if m.IsJunk(s) { + junk[s] = struct{}{} + } + } + for s, _ := range junk { + delete(b2j, s) + } + } + + // Purge remaining popular elements + popular := map[string]struct{}{} + n := len(m.b) + if m.autoJunk && n >= 200 { + ntest := n/100 + 1 + for s, indices := range b2j { + if len(indices) > ntest { + popular[s] = struct{}{} + } + } + for s, _ := range popular { + delete(b2j, s) + } + } + m.bPopular = popular + m.b2j = b2j +} + +func (m *SequenceMatcher) isBJunk(s string) bool { + _, ok := m.bJunk[s] + return ok +} + +// Find longest matching block in a[alo:ahi] and b[blo:bhi]. +// +// If IsJunk is not defined: +// +// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where +// +// alo <= i <= i+k <= ahi +// blo <= j <= j+k <= bhi +// +// and for all (i',j',k') meeting those conditions, +// +// k >= k' +// i <= i' +// and if i == i', j <= j' +// +// In other words, of all maximal matching blocks, return one that +// starts earliest in a, and of all those maximal matching blocks that +// start earliest in a, return the one that starts earliest in b. +// +// If IsJunk is defined, first the longest matching block is +// determined as above, but with the additional restriction that no +// junk element appears in the block. Then that block is extended as +// far as possible by matching (only) junk elements on both sides. So +// the resulting block never matches on junk except as identical junk +// happens to be adjacent to an "interesting" match. +// +// If no blocks match, return (alo, blo, 0). +func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match { + // CAUTION: stripping common prefix or suffix would be incorrect. + // E.g., + // ab + // acab + // Longest matching block is "ab", but if common prefix is + // stripped, it's "a" (tied with "b"). UNIX(tm) diff does so + // strip, so ends up claiming that ab is changed to acab by + // inserting "ca" in the middle. That's minimal but unintuitive: + // "it's obvious" that someone inserted "ac" at the front. + // Windiff ends up at the same place as diff, but by pairing up + // the unique 'b's and then matching the first two 'a's. + besti, bestj, bestsize := alo, blo, 0 + + // find longest junk-free match + // during an iteration of the loop, j2len[j] = length of longest + // junk-free match ending with a[i-1] and b[j] + j2len := map[int]int{} + for i := alo; i != ahi; i++ { + // look at all instances of a[i] in b; note that because + // b2j has no junk keys, the loop is skipped if a[i] is junk + newj2len := map[int]int{} + for _, j := range m.b2j[m.a[i]] { + // a[i] matches b[j] + if j < blo { + continue + } + if j >= bhi { + break + } + k := j2len[j-1] + 1 + newj2len[j] = k + if k > bestsize { + besti, bestj, bestsize = i-k+1, j-k+1, k + } + } + j2len = newj2len + } + + // Extend the best by non-junk elements on each end. In particular, + // "popular" non-junk elements aren't in b2j, which greatly speeds + // the inner loop above, but also means "the best" match so far + // doesn't contain any junk *or* popular non-junk elements. + for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) && + m.a[besti-1] == m.b[bestj-1] { + besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 + } + for besti+bestsize < ahi && bestj+bestsize < bhi && + !m.isBJunk(m.b[bestj+bestsize]) && + m.a[besti+bestsize] == m.b[bestj+bestsize] { + bestsize += 1 + } + + // Now that we have a wholly interesting match (albeit possibly + // empty!), we may as well suck up the matching junk on each + // side of it too. Can't think of a good reason not to, and it + // saves post-processing the (possibly considerable) expense of + // figuring out what to do with it. In the case of an empty + // interesting match, this is clearly the right thing to do, + // because no other kind of match is possible in the regions. + for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) && + m.a[besti-1] == m.b[bestj-1] { + besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 + } + for besti+bestsize < ahi && bestj+bestsize < bhi && + m.isBJunk(m.b[bestj+bestsize]) && + m.a[besti+bestsize] == m.b[bestj+bestsize] { + bestsize += 1 + } + + return Match{A: besti, B: bestj, Size: bestsize} +} + +// Return list of triples describing matching subsequences. +// +// Each triple is of the form (i, j, n), and means that +// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in +// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are +// adjacent triples in the list, and the second is not the last triple in the +// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe +// adjacent equal blocks. +// +// The last triple is a dummy, (len(a), len(b), 0), and is the only +// triple with n==0. +func (m *SequenceMatcher) GetMatchingBlocks() []Match { + if m.matchingBlocks != nil { + return m.matchingBlocks + } + + var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match + matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match { + match := m.findLongestMatch(alo, ahi, blo, bhi) + i, j, k := match.A, match.B, match.Size + if match.Size > 0 { + if alo < i && blo < j { + matched = matchBlocks(alo, i, blo, j, matched) + } + matched = append(matched, match) + if i+k < ahi && j+k < bhi { + matched = matchBlocks(i+k, ahi, j+k, bhi, matched) + } + } + return matched + } + matched := matchBlocks(0, len(m.a), 0, len(m.b), nil) + + // It's possible that we have adjacent equal blocks in the + // matching_blocks list now. + nonAdjacent := []Match{} + i1, j1, k1 := 0, 0, 0 + for _, b := range matched { + // Is this block adjacent to i1, j1, k1? + i2, j2, k2 := b.A, b.B, b.Size + if i1+k1 == i2 && j1+k1 == j2 { + // Yes, so collapse them -- this just increases the length of + // the first block by the length of the second, and the first + // block so lengthened remains the block to compare against. + k1 += k2 + } else { + // Not adjacent. Remember the first block (k1==0 means it's + // the dummy we started with), and make the second block the + // new block to compare against. + if k1 > 0 { + nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) + } + i1, j1, k1 = i2, j2, k2 + } + } + if k1 > 0 { + nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) + } + + nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0}) + m.matchingBlocks = nonAdjacent + return m.matchingBlocks +} + +// Return list of 5-tuples describing how to turn a into b. +// +// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple +// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the +// tuple preceding it, and likewise for j1 == the previous j2. +// +// The tags are characters, with these meanings: +// +// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2] +// +// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case. +// +// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case. +// +// 'e' (equal): a[i1:i2] == b[j1:j2] +func (m *SequenceMatcher) GetOpCodes() []OpCode { + if m.opCodes != nil { + return m.opCodes + } + i, j := 0, 0 + matching := m.GetMatchingBlocks() + opCodes := make([]OpCode, 0, len(matching)) + for _, m := range matching { + // invariant: we've pumped out correct diffs to change + // a[:i] into b[:j], and the next matching block is + // a[ai:ai+size] == b[bj:bj+size]. So we need to pump + // out a diff to change a[i:ai] into b[j:bj], pump out + // the matching block, and move (i,j) beyond the match + ai, bj, size := m.A, m.B, m.Size + tag := byte(0) + if i < ai && j < bj { + tag = 'r' + } else if i < ai { + tag = 'd' + } else if j < bj { + tag = 'i' + } + if tag > 0 { + opCodes = append(opCodes, OpCode{tag, i, ai, j, bj}) + } + i, j = ai+size, bj+size + // the list of matching blocks is terminated by a + // sentinel with size 0 + if size > 0 { + opCodes = append(opCodes, OpCode{'e', ai, i, bj, j}) + } + } + m.opCodes = opCodes + return m.opCodes +} + +// Isolate change clusters by eliminating ranges with no changes. +// +// Return a generator of groups with up to n lines of context. +// Each group is in the same format as returned by GetOpCodes(). +func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { + if n < 0 { + n = 3 + } + codes := m.GetOpCodes() + if len(codes) == 0 { + codes = []OpCode{OpCode{'e', 0, 1, 0, 1}} + } + // Fixup leading and trailing groups if they show no changes. + if codes[0].Tag == 'e' { + c := codes[0] + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2} + } + if codes[len(codes)-1].Tag == 'e' { + c := codes[len(codes)-1] + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)} + } + nn := n + n + groups := [][]OpCode{} + group := []OpCode{} + for _, c := range codes { + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + // End the current group and start a new one whenever + // there is a large range with no changes. + if c.Tag == 'e' && i2-i1 > nn { + group = append(group, OpCode{c.Tag, i1, min(i2, i1+n), + j1, min(j2, j1+n)}) + groups = append(groups, group) + group = []OpCode{} + i1, j1 = max(i1, i2-n), max(j1, j2-n) + } + group = append(group, OpCode{c.Tag, i1, i2, j1, j2}) + } + if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') { + groups = append(groups, group) + } + return groups +} + +// Convert range to the "ed" format +func formatRangeUnified(start, stop int) string { + // Per the diff spec at http://www.unix.org/single_unix_specification/ + beginning := start + 1 // lines start numbering with one + length := stop - start + if length == 1 { + return fmt.Sprintf("%d", beginning) + } + if length == 0 { + beginning -= 1 // empty ranges begin at line just before the range + } + return fmt.Sprintf("%d,%d", beginning, length) +} + +// Unified diff parameters +type UnifiedDiff struct { + A []string // First sequence lines + FromFile string // First file name + FromDate string // First file time + B []string // Second sequence lines + ToFile string // Second file name + ToDate string // Second file time + Eol string // Headers end of line, defaults to LF + Context int // Number of context lines +} + +// Compare two sequences of lines; generate the delta as a unified diff. +// +// Unified diffs are a compact way of showing line changes and a few +// lines of context. The number of context lines is set by 'n' which +// defaults to three. +// +// By default, the diff control lines (those with ---, +++, or @@) are +// created with a trailing newline. This is helpful so that inputs +// created from file.readlines() result in diffs that are suitable for +// file.writelines() since both the inputs and outputs have trailing +// newlines. +// +// For inputs that do not have trailing newlines, set the lineterm +// argument to "" so that the output will be uniformly newline free. +// +// The unidiff format normally has a header for filenames and modification +// times. Any or all of these may be specified using strings for +// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'. +// The modification times are normally expressed in the ISO 8601 format. +func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error { + buf := bufio.NewWriter(writer) + defer buf.Flush() + wf := func(format string, args ...interface{}) error { + _, err := buf.WriteString(fmt.Sprintf(format, args...)) + return err + } + ws := func(s string) error { + _, err := buf.WriteString(s) + return err + } + + if len(diff.Eol) == 0 { + diff.Eol = "\n" + } + + started := false + m := NewMatcher(diff.A, diff.B) + for _, g := range m.GetGroupedOpCodes(diff.Context) { + if !started { + started = true + fromDate := "" + if len(diff.FromDate) > 0 { + fromDate = "\t" + diff.FromDate + } + toDate := "" + if len(diff.ToDate) > 0 { + toDate = "\t" + diff.ToDate + } + if diff.FromFile != "" || diff.ToFile != "" { + err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol) + if err != nil { + return err + } + err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol) + if err != nil { + return err + } + } + } + first, last := g[0], g[len(g)-1] + range1 := formatRangeUnified(first.I1, last.I2) + range2 := formatRangeUnified(first.J1, last.J2) + if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil { + return err + } + for _, c := range g { + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + if c.Tag == 'e' { + for _, line := range diff.A[i1:i2] { + if err := ws(" " + line); err != nil { + return err + } + } + continue + } + if c.Tag == 'r' || c.Tag == 'd' { + for _, line := range diff.A[i1:i2] { + if err := ws("-" + line); err != nil { + return err + } + } + } + if c.Tag == 'r' || c.Tag == 'i' { + for _, line := range diff.B[j1:j2] { + if err := ws("+" + line); err != nil { + return err + } + } + } + } + } + return nil +} + +// Like WriteUnifiedDiff but returns the diff a string. +func GetUnifiedDiffString(diff UnifiedDiff) (string, error) { + w := &bytes.Buffer{} + err := WriteUnifiedDiff(w, diff) + return string(w.Bytes()), err +} + +// Convert range to the "ed" format. +func formatRangeContext(start, stop int) string { + // Per the diff spec at http://www.unix.org/single_unix_specification/ + beginning := start + 1 // lines start numbering with one + length := stop - start + if length == 0 { + beginning -= 1 // empty ranges begin at line just before the range + } + if length <= 1 { + return fmt.Sprintf("%d", beginning) + } + return fmt.Sprintf("%d,%d", beginning, beginning+length-1) +} + +// Split a string on "\n" while preserving them. The output can be used +// as input for UnifiedDiff and ContextDiff structures. +func SplitLines(s string) []string { + lines := strings.SplitAfter(s, "\n") + lines[len(lines)-1] += "\n" + return lines +} diff --git a/vendor/github.com/stretchr/testify/internal/spew/LICENSE b/vendor/github.com/stretchr/testify/internal/spew/LICENSE new file mode 100644 index 0000000000..bc52e96f2b --- /dev/null +++ b/vendor/github.com/stretchr/testify/internal/spew/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2012-2016 Dave Collins + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/vendor/github.com/stretchr/testify/internal/spew/README.md b/vendor/github.com/stretchr/testify/internal/spew/README.md new file mode 100644 index 0000000000..51a909e2ef --- /dev/null +++ b/vendor/github.com/stretchr/testify/internal/spew/README.md @@ -0,0 +1,12 @@ +go-spew +======= + +[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) + +Go-spew implements a deep pretty printer for Go data structures to aid in +debugging. A comprehensive suite of tests with 100% test coverage is provided +to ensure proper functionality. + +## License + +Go-spew is licensed under the [copyfree](http://copyfree.org) ISC License. diff --git a/vendor/github.com/stretchr/testify/internal/spew/bypass.go b/vendor/github.com/stretchr/testify/internal/spew/bypass.go new file mode 100644 index 0000000000..70ddeaad3e --- /dev/null +++ b/vendor/github.com/stretchr/testify/internal/spew/bypass.go @@ -0,0 +1,146 @@ +// Copyright (c) 2015-2016 Dave Collins +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is not running on Google App Engine, compiled by GopherJS, and +// "-tags safe" is not added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// Go versions prior to 1.4 are disabled because they use a different layout +// for interfaces which make the implementation of unsafeReflectValue more complex. +//go:build !js && !appengine && !safe && !disableunsafe && go1.4 +// +build !js,!appengine,!safe,!disableunsafe,go1.4 + +package spew + +import ( + "reflect" + "unsafe" +) + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = false + + // ptrSize is the size of a pointer on the current arch. + ptrSize = unsafe.Sizeof((*byte)(nil)) +) + +type flag uintptr + +var ( + // flagRO indicates whether the value field of a reflect.Value + // is read-only. + flagRO flag + + // flagAddr indicates whether the address of the reflect.Value's + // value may be taken. + flagAddr flag +) + +// flagKindMask holds the bits that make up the kind +// part of the flags field. In all the supported versions, +// it is in the lower 5 bits. +const flagKindMask = flag(0x1f) + +// Different versions of Go have used different +// bit layouts for the flags type. This table +// records the known combinations. +var okFlags = []struct { + ro, addr flag +}{{ + // From Go 1.4 to 1.5 + ro: 1 << 5, + addr: 1 << 7, +}, { + // Up to Go tip. + ro: 1<<5 | 1<<6, + addr: 1 << 8, +}} + +var flagValOffset = func() uintptr { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") + } + return field.Offset +}() + +// flagField returns a pointer to the flag field of a reflect.Value. +func flagField(v *reflect.Value) *flag { + return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) +} + +// unsafeReflectValue converts the passed reflect.Value into a one that bypasses +// the typical safety restrictions preventing access to unaddressable and +// unexported data. It works by digging the raw pointer to the underlying +// value out of the protected value and generating a new unprotected (unsafe) +// reflect.Value to it. +// +// This allows us to check for implementations of the Stringer and error +// interfaces to be used for pretty printing ordinarily unaddressable and +// inaccessible values such as unexported struct fields. +func unsafeReflectValue(v reflect.Value) reflect.Value { + if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { + return v + } + flagFieldPtr := flagField(&v) + *flagFieldPtr &^= flagRO + *flagFieldPtr |= flagAddr + return v +} + +// Sanity checks against future reflect package changes +// to the type or semantics of the Value.flag field. +func init() { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") + } + if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { + panic("reflect.Value flag field has changed kind") + } + type t0 int + var t struct { + A t0 + // t0 will have flagEmbedRO set. + t0 + // a will have flagStickyRO set + a t0 + } + vA := reflect.ValueOf(t).FieldByName("A") + va := reflect.ValueOf(t).FieldByName("a") + vt0 := reflect.ValueOf(t).FieldByName("t0") + + // Infer flagRO from the difference between the flags + // for the (otherwise identical) fields in t. + flagPublic := *flagField(&vA) + flagWithRO := *flagField(&va) | *flagField(&vt0) + flagRO = flagPublic ^ flagWithRO + + // Infer flagAddr from the difference between a value + // taken from a pointer and not. + vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") + flagNoPtr := *flagField(&vA) + flagPtr := *flagField(&vPtrA) + flagAddr = flagNoPtr ^ flagPtr + + // Check that the inferred flags tally with one of the known versions. + for _, f := range okFlags { + if flagRO == f.ro && flagAddr == f.addr { + return + } + } + panic("reflect.Value read-only flag has changed semantics") +} diff --git a/vendor/github.com/stretchr/testify/internal/spew/bypasssafe.go b/vendor/github.com/stretchr/testify/internal/spew/bypasssafe.go new file mode 100644 index 0000000000..5e2d890d6a --- /dev/null +++ b/vendor/github.com/stretchr/testify/internal/spew/bypasssafe.go @@ -0,0 +1,39 @@ +// Copyright (c) 2015-2016 Dave Collins +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is running on Google App Engine, compiled by GopherJS, or +// "-tags safe" is added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +//go:build js || appengine || safe || disableunsafe || !go1.4 +// +build js appengine safe disableunsafe !go1.4 + +package spew + +import "reflect" + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = true +) + +// unsafeReflectValue typically converts the passed reflect.Value into a one +// that bypasses the typical safety restrictions preventing access to +// unaddressable and unexported data. However, doing this relies on access to +// the unsafe package. This is a stub version which simply returns the passed +// reflect.Value when the unsafe package is not available. +func unsafeReflectValue(v reflect.Value) reflect.Value { + return v +} diff --git a/vendor/github.com/stretchr/testify/internal/spew/common.go b/vendor/github.com/stretchr/testify/internal/spew/common.go new file mode 100644 index 0000000000..1be8ce9457 --- /dev/null +++ b/vendor/github.com/stretchr/testify/internal/spew/common.go @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "reflect" + "sort" + "strconv" +) + +// Some constants in the form of bytes to avoid string overhead. This mirrors +// the technique used in the fmt package. +var ( + panicBytes = []byte("(PANIC=") + plusBytes = []byte("+") + iBytes = []byte("i") + trueBytes = []byte("true") + falseBytes = []byte("false") + interfaceBytes = []byte("(interface {})") + commaNewlineBytes = []byte(",\n") + newlineBytes = []byte("\n") + openBraceBytes = []byte("{") + openBraceNewlineBytes = []byte("{\n") + closeBraceBytes = []byte("}") + asteriskBytes = []byte("*") + colonBytes = []byte(":") + colonSpaceBytes = []byte(": ") + openParenBytes = []byte("(") + closeParenBytes = []byte(")") + spaceBytes = []byte(" ") + pointerChainBytes = []byte("->") + nilAngleBytes = []byte("") + maxNewlineBytes = []byte("\n") + maxShortBytes = []byte("") + circularBytes = []byte("") + circularShortBytes = []byte("") + invalidAngleBytes = []byte("") + openBracketBytes = []byte("[") + closeBracketBytes = []byte("]") + percentBytes = []byte("%") + precisionBytes = []byte(".") + openAngleBytes = []byte("<") + closeAngleBytes = []byte(">") + openMapBytes = []byte("map[") + closeMapBytes = []byte("]") + lenEqualsBytes = []byte("len=") + capEqualsBytes = []byte("cap=") +) + +// hexDigits is used to map a decimal value to a hex digit. +var hexDigits = "0123456789abcdef" + +// catchPanic handles any panics that might occur during the handleMethods +// calls. +func catchPanic(w io.Writer, v reflect.Value) { + if err := recover(); err != nil { + w.Write(panicBytes) + fmt.Fprintf(w, "%v", err) + w.Write(closeParenBytes) + } +} + +// handleMethods attempts to call the Error and String methods on the underlying +// type the passed reflect.Value represents and outputes the result to Writer w. +// +// It handles panics in any called methods by catching and displaying the error +// as the formatted value. +func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) { + // We need an interface to check if the type implements the error or + // Stringer interface. However, the reflect package won't give us an + // interface on certain things like unexported struct fields in order + // to enforce visibility rules. We use unsafe, when it's available, + // to bypass these restrictions since this package does not mutate the + // values. + if !v.CanInterface() { + if UnsafeDisabled { + return false + } + + v = unsafeReflectValue(v) + } + + // Choose whether or not to do error and Stringer interface lookups against + // the base type or a pointer to the base type depending on settings. + // Technically calling one of these methods with a pointer receiver can + // mutate the value, however, types which choose to satisify an error or + // Stringer interface with a pointer receiver should not be mutating their + // state inside these interface methods. + if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() { + v = unsafeReflectValue(v) + } + if v.CanAddr() { + v = v.Addr() + } + + // Is it an error or Stringer? + switch iface := v.Interface().(type) { + case error: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.Error())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + + w.Write([]byte(iface.Error())) + return true + + case fmt.Stringer: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.String())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + w.Write([]byte(iface.String())) + return true + } + return false +} + +// printBool outputs a boolean value as true or false to Writer w. +func printBool(w io.Writer, val bool) { + if val { + w.Write(trueBytes) + } else { + w.Write(falseBytes) + } +} + +// printInt outputs a signed integer value to Writer w. +func printInt(w io.Writer, val int64, base int) { + w.Write([]byte(strconv.FormatInt(val, base))) +} + +// printUint outputs an unsigned integer value to Writer w. +func printUint(w io.Writer, val uint64, base int) { + w.Write([]byte(strconv.FormatUint(val, base))) +} + +// printFloat outputs a floating point value using the specified precision, +// which is expected to be 32 or 64bit, to Writer w. +func printFloat(w io.Writer, val float64, precision int) { + w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) +} + +// printComplex outputs a complex value using the specified float precision +// for the real and imaginary parts to Writer w. +func printComplex(w io.Writer, c complex128, floatPrecision int) { + r := real(c) + w.Write(openParenBytes) + w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) + i := imag(c) + if i >= 0 { + w.Write(plusBytes) + } + w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) + w.Write(iBytes) + w.Write(closeParenBytes) +} + +// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x' +// prefix to Writer w. +func printHexPtr(w io.Writer, p uintptr) { + // Null pointer. + num := uint64(p) + if num == 0 { + w.Write(nilAngleBytes) + return + } + + // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix + buf := make([]byte, 18) + + // It's simpler to construct the hex string right to left. + base := uint64(16) + i := len(buf) - 1 + for num >= base { + buf[i] = hexDigits[num%base] + num /= base + i-- + } + buf[i] = hexDigits[num] + + // Add '0x' prefix. + i-- + buf[i] = 'x' + i-- + buf[i] = '0' + + // Strip unused leading bytes. + buf = buf[i:] + w.Write(buf) +} + +// valuesSorter implements sort.Interface to allow a slice of reflect.Value +// elements to be sorted. +type valuesSorter struct { + values []reflect.Value + strings []string // either nil or same len and values + cs *ConfigState +} + +// newValuesSorter initializes a valuesSorter instance, which holds a set of +// surrogate keys on which the data should be sorted. It uses flags in +// ConfigState to decide if and how to populate those surrogate keys. +func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface { + vs := &valuesSorter{values: values, cs: cs} + if canSortSimply(vs.values[0].Kind()) { + return vs + } + if !cs.DisableMethods { + vs.strings = make([]string, len(values)) + for i := range vs.values { + b := bytes.Buffer{} + if !handleMethods(cs, &b, vs.values[i]) { + vs.strings = nil + break + } + vs.strings[i] = b.String() + } + } + if vs.strings == nil && cs.SpewKeys { + vs.strings = make([]string, len(values)) + for i := range vs.values { + vs.strings[i] = Sprintf("%#v", vs.values[i].Interface()) + } + } + return vs +} + +// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted +// directly, or whether it should be considered for sorting by surrogate keys +// (if the ConfigState allows it). +func canSortSimply(kind reflect.Kind) bool { + // This switch parallels valueSortLess, except for the default case. + switch kind { + case reflect.Bool: + return true + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return true + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.String: + return true + case reflect.Uintptr: + return true + case reflect.Array: + return true + } + return false +} + +// Len returns the number of values in the slice. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Len() int { + return len(s.values) +} + +// Swap swaps the values at the passed indices. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Swap(i, j int) { + s.values[i], s.values[j] = s.values[j], s.values[i] + if s.strings != nil { + s.strings[i], s.strings[j] = s.strings[j], s.strings[i] + } +} + +// valueSortLess returns whether the first value should sort before the second +// value. It is used by valueSorter.Less as part of the sort.Interface +// implementation. +func valueSortLess(a, b reflect.Value) bool { + switch a.Kind() { + case reflect.Bool: + return !a.Bool() && b.Bool() + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return a.Int() < b.Int() + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return a.Uint() < b.Uint() + case reflect.Float32, reflect.Float64: + return a.Float() < b.Float() + case reflect.String: + return a.String() < b.String() + case reflect.Uintptr: + return a.Uint() < b.Uint() + case reflect.Array: + // Compare the contents of both arrays. + l := a.Len() + for i := 0; i < l; i++ { + av := a.Index(i) + bv := b.Index(i) + if av.Interface() == bv.Interface() { + continue + } + return valueSortLess(av, bv) + } + } + return a.String() < b.String() +} + +// Less returns whether the value at index i should sort before the +// value at index j. It is part of the sort.Interface implementation. +func (s *valuesSorter) Less(i, j int) bool { + if s.strings == nil { + return valueSortLess(s.values[i], s.values[j]) + } + return s.strings[i] < s.strings[j] +} + +// sortValues is a sort function that handles both native types and any type that +// can be converted to error or Stringer. Other inputs are sorted according to +// their Value.String() value to ensure display stability. +func sortValues(values []reflect.Value, cs *ConfigState) { + if len(values) == 0 { + return + } + sort.Sort(newValuesSorter(values, cs)) +} diff --git a/vendor/github.com/stretchr/testify/internal/spew/config.go b/vendor/github.com/stretchr/testify/internal/spew/config.go new file mode 100644 index 0000000000..161895fc6d --- /dev/null +++ b/vendor/github.com/stretchr/testify/internal/spew/config.go @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "os" +) + +// ConfigState houses the configuration options used by spew to format and +// display values. There is a global instance, Config, that is used to control +// all top-level Formatter and Dump functionality. Each ConfigState instance +// provides methods equivalent to the top-level functions. +// +// The zero value for ConfigState provides no indentation. You would typically +// want to set it to a space or a tab. +// +// Alternatively, you can use NewDefaultConfig to get a ConfigState instance +// with default settings. See the documentation of NewDefaultConfig for default +// values. +type ConfigState struct { + // Indent specifies the string to use for each indentation level. The + // global config instance that all top-level functions use set this to a + // single space by default. If you would like more indentation, you might + // set this to a tab with "\t" or perhaps two spaces with " ". + Indent string + + // MaxDepth controls the maximum number of levels to descend into nested + // data structures. The default, 0, means there is no limit. + // + // NOTE: Circular data structures are properly detected, so it is not + // necessary to set this value unless you specifically want to limit deeply + // nested data structures. + MaxDepth int + + // DisableMethods specifies whether or not error and Stringer interfaces are + // invoked for types that implement them. + DisableMethods bool + + // DisablePointerMethods specifies whether or not to check for and invoke + // error and Stringer interfaces on types which only accept a pointer + // receiver when the current type is not a pointer. + // + // NOTE: This might be an unsafe action since calling one of these methods + // with a pointer receiver could technically mutate the value, however, + // in practice, types which choose to satisify an error or Stringer + // interface with a pointer receiver should not be mutating their state + // inside these interface methods. As a result, this option relies on + // access to the unsafe package, so it will not have any effect when + // running in environments without access to the unsafe package such as + // Google App Engine or with the "safe" build tag specified. + DisablePointerMethods bool + + // DisablePointerAddresses specifies whether to disable the printing of + // pointer addresses. This is useful when diffing data structures in tests. + DisablePointerAddresses bool + + // DisableCapacities specifies whether to disable the printing of capacities + // for arrays, slices, maps and channels. This is useful when diffing + // data structures in tests. + DisableCapacities bool + + // ContinueOnMethod specifies whether or not recursion should continue once + // a custom error or Stringer interface is invoked. The default, false, + // means it will print the results of invoking the custom error or Stringer + // interface and return immediately instead of continuing to recurse into + // the internals of the data type. + // + // NOTE: This flag does not have any effect if method invocation is disabled + // via the DisableMethods or DisablePointerMethods options. + ContinueOnMethod bool + + // SortKeys specifies map keys should be sorted before being printed. Use + // this to have a more deterministic, diffable output. Note that only + // native types (bool, int, uint, floats, uintptr and string) and types + // that support the error or Stringer interfaces (if methods are + // enabled) are supported, with other types sorted according to the + // reflect.Value.String() output which guarantees display stability. + SortKeys bool + + // SpewKeys specifies that, as a last resort attempt, map keys should + // be spewed to strings and sorted by those strings. This is only + // considered if SortKeys is true. + SpewKeys bool +} + +// Config is the active configuration of the top-level functions. +// The configuration can be changed by modifying the contents of spew.Config. +var Config = ConfigState{Indent: " "} + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the formatted string as a value that satisfies error. See NewFormatter +// for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, c.convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, c.convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, c.convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a Formatter interface returned by c.NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, c.convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Print(a ...interface{}) (n int, err error) { + return fmt.Print(c.convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, c.convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Println(a ...interface{}) (n int, err error) { + return fmt.Println(c.convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprint(a ...interface{}) string { + return fmt.Sprint(c.convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, c.convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a Formatter interface returned by c.NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintln(a ...interface{}) string { + return fmt.Sprintln(c.convertArgs(a)...) +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +c.Printf, c.Println, or c.Printf. +*/ +func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(c, v) +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) { + fdump(c, w, a...) +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + - Pointers are dereferenced and followed + - Circular data structures are detected and handled properly + - Custom Stringer/error interfaces are optionally invoked, including + on unexported types + - Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + - Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by modifying the public members +of c. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func (c *ConfigState) Dump(a ...interface{}) { + fdump(c, os.Stdout, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func (c *ConfigState) Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(c, &buf, a...) + return buf.String() +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a spew Formatter interface using +// the ConfigState associated with s. +func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = newFormatter(c, arg) + } + return formatters +} + +// NewDefaultConfig returns a ConfigState with the following default settings. +// +// Indent: " " +// MaxDepth: 0 +// DisableMethods: false +// DisablePointerMethods: false +// ContinueOnMethod: false +// SortKeys: false +func NewDefaultConfig() *ConfigState { + return &ConfigState{Indent: " "} +} diff --git a/vendor/github.com/stretchr/testify/internal/spew/doc.go b/vendor/github.com/stretchr/testify/internal/spew/doc.go new file mode 100644 index 0000000000..722e9aa791 --- /dev/null +++ b/vendor/github.com/stretchr/testify/internal/spew/doc.go @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +Package spew implements a deep pretty printer for Go data structures to aid in +debugging. + +A quick overview of the additional features spew provides over the built-in +printing facilities for Go data types are as follows: + + - Pointers are dereferenced and followed + - Circular data structures are detected and handled properly + - Custom Stringer/error interfaces are optionally invoked, including + on unexported types + - Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + - Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output (only when using + Dump style) + +There are two different approaches spew allows for dumping Go data structures: + + - Dump style which prints with newlines, customizable indentation, + and additional debug information such as types and all pointer addresses + used to indirect to the final value + - A custom Formatter interface that integrates cleanly with the standard fmt + package and replaces %v, %+v, %#v, and %#+v to provide inline printing + similar to the default %v while providing the additional functionality + outlined above and passing unsupported format verbs such as %x and %q + along to fmt + +# Quick Start + +This section demonstrates how to quickly get started with spew. See the +sections below for further details on formatting and configuration options. + +To dump a variable with full newlines, indentation, type, and pointer +information use Dump, Fdump, or Sdump: + + spew.Dump(myVar1, myVar2, ...) + spew.Fdump(someWriter, myVar1, myVar2, ...) + str := spew.Sdump(myVar1, myVar2, ...) + +Alternatively, if you would prefer to use format strings with a compacted inline +printing style, use the convenience wrappers Printf, Fprintf, etc with +%v (most compact), %+v (adds pointer addresses), %#v (adds types), or +%#+v (adds types and pointer addresses): + + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +# Configuration Options + +Configuration of spew is handled by fields in the ConfigState type. For +convenience, all of the top-level functions use a global state available +via the spew.Config global. + +It is also possible to create a ConfigState instance that provides methods +equivalent to the top-level functions. This allows concurrent configuration +options. See the ConfigState documentation for more details. + +The following configuration options are available: + + - Indent + String to use for each indentation level for Dump functions. + It is a single space by default. A popular alternative is "\t". + + - MaxDepth + Maximum number of levels to descend into nested data structures. + There is no limit by default. + + - DisableMethods + Disables invocation of error and Stringer interface methods. + Method invocation is enabled by default. + + - DisablePointerMethods + Disables invocation of error and Stringer interface methods on types + which only accept pointer receivers from non-pointer variables. + Pointer method invocation is enabled by default. + + - DisablePointerAddresses + DisablePointerAddresses specifies whether to disable the printing of + pointer addresses. This is useful when diffing data structures in tests. + + - DisableCapacities + DisableCapacities specifies whether to disable the printing of + capacities for arrays, slices, maps and channels. This is useful when + diffing data structures in tests. + + - ContinueOnMethod + Enables recursion into types after invoking error and Stringer interface + methods. Recursion after method invocation is disabled by default. + + - SortKeys + Specifies map keys should be sorted before being printed. Use + this to have a more deterministic, diffable output. Note that + only native types (bool, int, uint, floats, uintptr and string) + and types which implement error or Stringer interfaces are + supported with other types sorted according to the + reflect.Value.String() output which guarantees display + stability. Natural map order is used by default. + + - SpewKeys + Specifies that, as a last resort attempt, map keys should be + spewed to strings and sorted by those strings. This is only + considered if SortKeys is true. + +# Dump Usage + +Simply call spew.Dump with a list of variables you want to dump: + + spew.Dump(myVar1, myVar2, ...) + +You may also call spew.Fdump if you would prefer to output to an arbitrary +io.Writer. For example, to dump to standard error: + + spew.Fdump(os.Stderr, myVar1, myVar2, ...) + +A third option is to call spew.Sdump to get the formatted output as a string: + + str := spew.Sdump(myVar1, myVar2, ...) + +# Sample Dump Output + +See the Dump example for details on the setup of the types and variables being +shown here. + + (main.Foo) { + unexportedField: (*main.Bar)(0xf84002e210)({ + flag: (main.Flag) flagTwo, + data: (uintptr) + }), + ExportedField: (map[interface {}]interface {}) (len=1) { + (string) (len=3) "one": (bool) true + } + } + +Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C +command as shown. + + ([]uint8) (len=32 cap=32) { + 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | + 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| + 00000020 31 32 |12| + } + +# Custom Formatter + +Spew provides a custom formatter that implements the fmt.Formatter interface +so that it integrates cleanly with standard fmt package printing functions. The +formatter is useful for inline printing of smaller data types similar to the +standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +# Custom Formatter Usage + +The simplest way to make use of the spew custom formatter is to call one of the +convenience functions such as spew.Printf, spew.Println, or spew.Printf. The +functions have syntax you are most likely already familiar with: + + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Println(myVar, myVar2) + spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +See the Index for the full list convenience functions. + +# Sample Formatter Output + +Double pointer to a uint8: + + %v: <**>5 + %+v: <**>(0xf8400420d0->0xf8400420c8)5 + %#v: (**uint8)5 + %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 + +Pointer to circular struct with a uint8 field and a pointer to itself: + + %v: <*>{1 <*>} + %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} + %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} + %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} + +See the Printf example for details on the setup of variables being shown +here. + +# Errors + +Since it is possible for custom Stringer/error interfaces to panic, spew +detects them and handles them internally by printing the panic information +inline with the output. Since spew is intended to provide deep pretty printing +capabilities on structures, it intentionally does not return any errors. +*/ +package spew diff --git a/vendor/github.com/stretchr/testify/internal/spew/dump.go b/vendor/github.com/stretchr/testify/internal/spew/dump.go new file mode 100644 index 0000000000..8323041a48 --- /dev/null +++ b/vendor/github.com/stretchr/testify/internal/spew/dump.go @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "encoding/hex" + "fmt" + "io" + "os" + "reflect" + "regexp" + "strconv" + "strings" +) + +var ( + // uint8Type is a reflect.Type representing a uint8. It is used to + // convert cgo types to uint8 slices for hexdumping. + uint8Type = reflect.TypeOf(uint8(0)) + + // cCharRE is a regular expression that matches a cgo char. + // It is used to detect character arrays to hexdump them. + cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`) + + // cUnsignedCharRE is a regular expression that matches a cgo unsigned + // char. It is used to detect unsigned character arrays to hexdump + // them. + cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`) + + // cUint8tCharRE is a regular expression that matches a cgo uint8_t. + // It is used to detect uint8_t arrays to hexdump them. + cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`) +) + +// dumpState contains information about the state of a dump operation. +type dumpState struct { + w io.Writer + depth int + pointers map[uintptr]int + ignoreNextType bool + ignoreNextIndent bool + cs *ConfigState +} + +// indent performs indentation according to the depth level and cs.Indent +// option. +func (d *dumpState) indent() { + if d.ignoreNextIndent { + d.ignoreNextIndent = false + return + } + d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) +} + +// unpackValue returns values inside of non-nil interfaces when possible. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (d *dumpState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface && !v.IsNil() { + v = v.Elem() + } + return v +} + +// dumpPtr handles formatting of pointers by indirecting them as necessary. +func (d *dumpState) dumpPtr(v reflect.Value) { + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range d.pointers { + if depth >= d.depth { + delete(d.pointers, k) + } + } + + // Keep list of all dereferenced pointers to show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by dereferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := d.pointers[addr]; ok && pd < d.depth { + cycleFound = true + indirects-- + break + } + d.pointers[addr] = d.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type information. + d.w.Write(openParenBytes) + d.w.Write(bytes.Repeat(asteriskBytes, indirects)) + d.w.Write([]byte(ve.Type().String())) + d.w.Write(closeParenBytes) + + // Display pointer information. + if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { + d.w.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + d.w.Write(pointerChainBytes) + } + printHexPtr(d.w, addr) + } + d.w.Write(closeParenBytes) + } + + // Display dereferenced value. + d.w.Write(openParenBytes) + switch { + case nilFound: + d.w.Write(nilAngleBytes) + + case cycleFound: + d.w.Write(circularBytes) + + default: + d.ignoreNextType = true + d.dump(ve) + } + d.w.Write(closeParenBytes) +} + +// dumpSlice handles formatting of arrays and slices. Byte (uint8 under +// reflection) arrays and slices are dumped in hexdump -C fashion. +func (d *dumpState) dumpSlice(v reflect.Value) { + // Determine whether this type should be hex dumped or not. Also, + // for types which should be hexdumped, try to use the underlying data + // first, then fall back to trying to convert them to a uint8 slice. + var buf []uint8 + doConvert := false + doHexDump := false + numEntries := v.Len() + if numEntries > 0 { + vt := v.Index(0).Type() + vts := vt.String() + switch { + // C types that need to be converted. + case cCharRE.MatchString(vts): + fallthrough + case cUnsignedCharRE.MatchString(vts): + fallthrough + case cUint8tCharRE.MatchString(vts): + doConvert = true + + // Try to use existing uint8 slices and fall back to converting + // and copying if that fails. + case vt.Kind() == reflect.Uint8: + // We need an addressable interface to convert the type + // to a byte slice. However, the reflect package won't + // give us an interface on certain things like + // unexported struct fields in order to enforce + // visibility rules. We use unsafe, when available, to + // bypass these restrictions since this package does not + // mutate the values. + vs := v + if !vs.CanInterface() || !vs.CanAddr() { + vs = unsafeReflectValue(vs) + } + if !UnsafeDisabled { + vs = vs.Slice(0, numEntries) + + // Use the existing uint8 slice if it can be + // type asserted. + iface := vs.Interface() + if slice, ok := iface.([]uint8); ok { + buf = slice + doHexDump = true + break + } + } + + // The underlying data needs to be converted if it can't + // be type asserted to a uint8 slice. + doConvert = true + } + + // Copy and convert the underlying type if needed. + if doConvert && vt.ConvertibleTo(uint8Type) { + // Convert and copy each element into a uint8 byte + // slice. + buf = make([]uint8, numEntries) + for i := 0; i < numEntries; i++ { + vv := v.Index(i) + buf[i] = uint8(vv.Convert(uint8Type).Uint()) + } + doHexDump = true + } + } + + // Hexdump the entire slice as needed. + if doHexDump { + indent := strings.Repeat(d.cs.Indent, d.depth) + str := indent + hex.Dump(buf) + str = strings.Replace(str, "\n", "\n"+indent, -1) + str = strings.TrimRight(str, d.cs.Indent) + d.w.Write([]byte(str)) + return + } + + // Recursively call dump for each item. + for i := 0; i < numEntries; i++ { + d.dump(d.unpackValue(v.Index(i))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } +} + +// dump is the main workhorse for dumping a value. It uses the passed reflect +// value to figure out what kind of object we are dealing with and formats it +// appropriately. It is a recursive function, however circular data structures +// are detected and handled properly. +func (d *dumpState) dump(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + d.w.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + d.indent() + d.dumpPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !d.ignoreNextType { + d.indent() + d.w.Write(openParenBytes) + d.w.Write([]byte(v.Type().String())) + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + d.ignoreNextType = false + + // Display length and capacity if the built-in len and cap functions + // work with the value's kind and the len/cap itself is non-zero. + valueLen, valueCap := 0, 0 + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.Chan: + valueLen, valueCap = v.Len(), v.Cap() + case reflect.Map, reflect.String: + valueLen = v.Len() + } + if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { + d.w.Write(openParenBytes) + if valueLen != 0 { + d.w.Write(lenEqualsBytes) + printInt(d.w, int64(valueLen), 10) + } + if !d.cs.DisableCapacities && valueCap != 0 { + if valueLen != 0 { + d.w.Write(spaceBytes) + } + d.w.Write(capEqualsBytes) + printInt(d.w, int64(valueCap), 10) + } + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + + // Call Stringer/error interfaces if they exist and the handle methods flag + // is enabled + if !d.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(d.cs, d.w, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(d.w, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(d.w, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(d.w, v.Uint(), 10) + + case reflect.Float32: + printFloat(d.w, v.Float(), 32) + + case reflect.Float64: + printFloat(d.w, v.Float(), 64) + + case reflect.Complex64: + printComplex(d.w, v.Complex(), 32) + + case reflect.Complex128: + printComplex(d.w, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + d.dumpSlice(v) + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.String: + d.w.Write([]byte(strconv.Quote(v.String()))) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + d.w.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + numEntries := v.Len() + keys := v.MapKeys() + if d.cs.SortKeys { + sortValues(keys, d.cs) + } + for i, key := range keys { + d.dump(d.unpackValue(key)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.MapIndex(key))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Struct: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + vt := v.Type() + numFields := v.NumField() + for i := 0; i < numFields; i++ { + d.indent() + vtf := vt.Field(i) + d.w.Write([]byte(vtf.Name)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.Field(i))) + if i < (numFields - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(d.w, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(d.w, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it in case any new + // types are added. + default: + if v.CanInterface() { + fmt.Fprintf(d.w, "%v", v.Interface()) + } else { + fmt.Fprintf(d.w, "%v", v.String()) + } + } +} + +// fdump is a helper function to consolidate the logic from the various public +// methods which take varying writers and config states. +func fdump(cs *ConfigState, w io.Writer, a ...interface{}) { + for _, arg := range a { + if arg == nil { + w.Write(interfaceBytes) + w.Write(spaceBytes) + w.Write(nilAngleBytes) + w.Write(newlineBytes) + continue + } + + d := dumpState{w: w, cs: cs} + d.pointers = make(map[uintptr]int) + d.dump(reflect.ValueOf(arg)) + d.w.Write(newlineBytes) + } +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func Fdump(w io.Writer, a ...interface{}) { + fdump(&Config, w, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(&Config, &buf, a...) + return buf.String() +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + - Pointers are dereferenced and followed + - Circular data structures are detected and handled properly + - Custom Stringer/error interfaces are optionally invoked, including + on unexported types + - Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + - Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by an exported package global, +spew.Config. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func Dump(a ...interface{}) { + fdump(&Config, os.Stdout, a...) +} diff --git a/vendor/github.com/stretchr/testify/internal/spew/format.go b/vendor/github.com/stretchr/testify/internal/spew/format.go new file mode 100644 index 0000000000..b04edb7d7a --- /dev/null +++ b/vendor/github.com/stretchr/testify/internal/spew/format.go @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "reflect" + "strconv" + "strings" +) + +// supportedFlags is a list of all the character flags supported by fmt package. +const supportedFlags = "0-+# " + +// formatState implements the fmt.Formatter interface and contains information +// about the state of a formatting operation. The NewFormatter function can +// be used to get a new Formatter which can be used directly as arguments +// in standard fmt package printing calls. +type formatState struct { + value interface{} + fs fmt.State + depth int + pointers map[uintptr]int + ignoreNextType bool + cs *ConfigState +} + +// buildDefaultFormat recreates the original format string without precision +// and width information to pass in to fmt.Sprintf in the case of an +// unrecognized type. Unless new types are added to the language, this +// function won't ever be called. +func (f *formatState) buildDefaultFormat() (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + buf.WriteRune('v') + + format = buf.String() + return format +} + +// constructOrigFormat recreates the original format string including precision +// and width information to pass along to the standard fmt package. This allows +// automatic deferral of all format strings this package doesn't support. +func (f *formatState) constructOrigFormat(verb rune) (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + if width, ok := f.fs.Width(); ok { + buf.WriteString(strconv.Itoa(width)) + } + + if precision, ok := f.fs.Precision(); ok { + buf.Write(precisionBytes) + buf.WriteString(strconv.Itoa(precision)) + } + + buf.WriteRune(verb) + + format = buf.String() + return format +} + +// unpackValue returns values inside of non-nil interfaces when possible and +// ensures that types for values which have been unpacked from an interface +// are displayed when the show types flag is also set. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (f *formatState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface { + f.ignoreNextType = false + if !v.IsNil() { + v = v.Elem() + } + } + return v +} + +// formatPtr handles formatting of pointers by indirecting them as necessary. +func (f *formatState) formatPtr(v reflect.Value) { + // Display nil if top level pointer is nil. + showTypes := f.fs.Flag('#') + if v.IsNil() && (!showTypes || f.ignoreNextType) { + f.fs.Write(nilAngleBytes) + return + } + + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range f.pointers { + if depth >= f.depth { + delete(f.pointers, k) + } + } + + // Keep list of all dereferenced pointers to possibly show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by derferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := f.pointers[addr]; ok && pd < f.depth { + cycleFound = true + indirects-- + break + } + f.pointers[addr] = f.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type or indirection level depending on flags. + if showTypes && !f.ignoreNextType { + f.fs.Write(openParenBytes) + f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) + f.fs.Write([]byte(ve.Type().String())) + f.fs.Write(closeParenBytes) + } else { + if nilFound || cycleFound { + indirects += strings.Count(ve.Type().String(), "*") + } + f.fs.Write(openAngleBytes) + f.fs.Write([]byte(strings.Repeat("*", indirects))) + f.fs.Write(closeAngleBytes) + } + + // Display pointer information depending on flags. + if f.fs.Flag('+') && (len(pointerChain) > 0) { + f.fs.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + f.fs.Write(pointerChainBytes) + } + printHexPtr(f.fs, addr) + } + f.fs.Write(closeParenBytes) + } + + // Display dereferenced value. + switch { + case nilFound: + f.fs.Write(nilAngleBytes) + + case cycleFound: + f.fs.Write(circularShortBytes) + + default: + f.ignoreNextType = true + f.format(ve) + } +} + +// format is the main workhorse for providing the Formatter interface. It +// uses the passed reflect value to figure out what kind of object we are +// dealing with and formats it appropriately. It is a recursive function, +// however circular data structures are detected and handled properly. +func (f *formatState) format(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + f.fs.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + f.formatPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !f.ignoreNextType && f.fs.Flag('#') { + f.fs.Write(openParenBytes) + f.fs.Write([]byte(v.Type().String())) + f.fs.Write(closeParenBytes) + } + f.ignoreNextType = false + + // Call Stringer/error interfaces if they exist and the handle methods + // flag is enabled. + if !f.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(f.cs, f.fs, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(f.fs, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(f.fs, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(f.fs, v.Uint(), 10) + + case reflect.Float32: + printFloat(f.fs, v.Float(), 32) + + case reflect.Float64: + printFloat(f.fs, v.Float(), 64) + + case reflect.Complex64: + printComplex(f.fs, v.Complex(), 32) + + case reflect.Complex128: + printComplex(f.fs, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + f.fs.Write(openBracketBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + numEntries := v.Len() + for i := 0; i < numEntries; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(v.Index(i))) + } + } + f.depth-- + f.fs.Write(closeBracketBytes) + + case reflect.String: + f.fs.Write([]byte(v.String())) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + f.fs.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + + f.fs.Write(openMapBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + keys := v.MapKeys() + if f.cs.SortKeys { + sortValues(keys, f.cs) + } + for i, key := range keys { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(key)) + f.fs.Write(colonBytes) + f.ignoreNextType = true + f.format(f.unpackValue(v.MapIndex(key))) + } + } + f.depth-- + f.fs.Write(closeMapBytes) + + case reflect.Struct: + numFields := v.NumField() + f.fs.Write(openBraceBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + vt := v.Type() + for i := 0; i < numFields; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + vtf := vt.Field(i) + if f.fs.Flag('+') || f.fs.Flag('#') { + f.fs.Write([]byte(vtf.Name)) + f.fs.Write(colonBytes) + } + f.format(f.unpackValue(v.Field(i))) + } + } + f.depth-- + f.fs.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(f.fs, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(f.fs, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it if any get added. + default: + format := f.buildDefaultFormat() + if v.CanInterface() { + fmt.Fprintf(f.fs, format, v.Interface()) + } else { + fmt.Fprintf(f.fs, format, v.String()) + } + } +} + +// Format satisfies the fmt.Formatter interface. See NewFormatter for usage +// details. +func (f *formatState) Format(fs fmt.State, verb rune) { + f.fs = fs + + // Use standard formatting for verbs that are not v. + if verb != 'v' { + format := f.constructOrigFormat(verb) + fmt.Fprintf(fs, format, f.value) + return + } + + if f.value == nil { + if fs.Flag('#') { + fs.Write(interfaceBytes) + } + fs.Write(nilAngleBytes) + return + } + + f.format(reflect.ValueOf(f.value)) +} + +// newFormatter is a helper function to consolidate the logic from the various +// public methods which take varying config states. +func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { + fs := &formatState{value: v, cs: cs} + fs.pointers = make(map[uintptr]int) + return fs +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +Printf, Println, or Fprintf. +*/ +func NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(&Config, v) +} diff --git a/vendor/github.com/stretchr/testify/internal/spew/spew.go b/vendor/github.com/stretchr/testify/internal/spew/spew.go new file mode 100644 index 0000000000..32c0e33882 --- /dev/null +++ b/vendor/github.com/stretchr/testify/internal/spew/spew.go @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "fmt" + "io" +) + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the formatted string as a value that satisfies error. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a default Formatter interface returned by NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) +func Print(a ...interface{}) (n int, err error) { + return fmt.Print(convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) +func Println(a ...interface{}) (n int, err error) { + return fmt.Println(convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprint(a ...interface{}) string { + return fmt.Sprint(convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintln(a ...interface{}) string { + return fmt.Sprintln(convertArgs(a)...) +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a default spew Formatter interface. +func convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = NewFormatter(arg) + } + return formatters +} diff --git a/vendor/github.com/stretchr/testify/require/doc.go b/vendor/github.com/stretchr/testify/require/doc.go index c8e3f94a80..ee84cc4791 100644 --- a/vendor/github.com/stretchr/testify/require/doc.go +++ b/vendor/github.com/stretchr/testify/require/doc.go @@ -1,4 +1,4 @@ -// Package require implements the same assertions as the `assert` package but +// Package require implements the same assertions as the assert package but // stops test execution when a test fails. // // # Example Usage @@ -21,8 +21,8 @@ // // # Assertions // -// The `require` package have same global functions as in the `assert` package, -// but instead of returning a boolean result they call `t.FailNow()`. +// The require package have same global functions as in the assert package, +// but instead of returning a boolean result they call [testing.T.FailNow]. // A consequence of this is that it must be called from the goroutine running // the test function, not from other goroutines created during the test. // diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 2d02f9bcef..652871f2eb 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -180,7 +180,7 @@ func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...i t.FailNow() } -// EqualError asserts that a function returned an error (i.e. not `nil`) +// EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -195,7 +195,7 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte t.FailNow() } -// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -295,7 +295,7 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar t.FailNow() } -// Error asserts that a function returned an error (i.e. not `nil`). +// Error asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // require.Error(t, err) @@ -333,8 +333,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int t.FailNow() } -// ErrorContains asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // require.ErrorContains(t, err, expectedErrorSubString) @@ -348,8 +348,8 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in t.FailNow() } -// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // require.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") @@ -387,7 +387,7 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface t.FailNow() } -// Errorf asserts that a function returned an error (i.e. not `nil`). +// Errorf asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // require.Errorf(t, err, "error message %s", "formatted") @@ -429,7 +429,7 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t // time.Sleep(8*time.Second) // externalValue = true // }() -// require.EventuallyWithT(t, func(c *require.CollectT) { +// require.EventuallyWithT(t, func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // require.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") @@ -457,10 +457,10 @@ func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitF // time.Sleep(8*time.Second) // externalValue = true // }() -// require.EventuallyWithTf(t, func(c *require.CollectT, "error message %s", "formatted") { +// require.EventuallyWithTf(t, func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // require.True(c, externalValue, "expected 'externalValue' to be true") -// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "error message %s", "formatted") func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -671,8 +671,6 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // body that contains a string. // // require.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -687,8 +685,6 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url s // body that contains a string. // // require.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -703,8 +699,6 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // body that does not contain a string. // // require.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -719,8 +713,6 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, ur // body that does not contain a string. // // require.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -734,8 +726,6 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // HTTPError asserts that a specified handler returns an error status code. // // require.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -749,8 +739,6 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPErrorf asserts that a specified handler returns an error status code. // // require.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -764,8 +752,6 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPRedirect asserts that a specified handler returns a redirect status code. // // require.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -779,8 +765,6 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin // HTTPRedirectf asserts that a specified handler returns a redirect status code. // // require.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -794,8 +778,6 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // HTTPStatusCode asserts that a specified handler returns a specified status code. // // require.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) -// -// Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -809,8 +791,6 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url str // HTTPStatusCodef asserts that a specified handler returns a specified status code. // // require.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -824,8 +804,6 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st // HTTPSuccess asserts that a specified handler returns a success status code. // // require.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) -// -// Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -839,8 +817,6 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string // HTTPSuccessf asserts that a specified handler returns a success status code. // // require.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1385,12 +1361,11 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { t.FailNow() } -// NoError asserts that a function returned no error (i.e. `nil`). +// NoError asserts that a function returned a nil error (ie. no error). // -// actualObj, err := SomeFunction() -// if require.NoError(t, err) { -// require.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// require.NoError(t, err) +// require.Equal(t, expectedObj, actualObj) func NoError(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1401,12 +1376,11 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) { t.FailNow() } -// NoErrorf asserts that a function returned no error (i.e. `nil`). +// NoErrorf asserts that a function returned a nil error (ie. no error). // -// actualObj, err := SomeFunction() -// if require.NoErrorf(t, err, "error message %s", "formatted") { -// require.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// require.NoErrorf(t, err, "error message %s", "formatted") +// require.Equal(t, expectedObj, actualObj) func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1515,9 +1489,8 @@ func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg str // NotEmpty asserts that the specified object is NOT [Empty]. // -// if require.NotEmpty(t, obj) { -// require.Equal(t, "two", obj[1]) -// } +// require.NotEmpty(t, obj) +// require.Equal(t, "two", obj[1]) func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1530,9 +1503,8 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // NotEmptyf asserts that the specified object is NOT [Empty]. // -// if require.NotEmptyf(t, obj, "error message %s", "formatted") { -// require.Equal(t, "two", obj[1]) -// } +// require.NotEmptyf(t, obj, "error message %s", "formatted") +// require.Equal(t, "two", obj[1]) func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -2135,7 +2107,19 @@ func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, t.FailNow() } -// YAMLEq asserts that two YAML strings are equivalent. +// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// require.YAMLEq(t, expected, actual) func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -2146,7 +2130,19 @@ func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ t.FailNow() } -// YAMLEqf asserts that two YAML strings are equivalent. +// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// require.YAMLEqf(t, expected, actual, "error message %s", "formatted") func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/require/require.go.tmpl b/vendor/github.com/stretchr/testify/require/require.go.tmpl index 8b32836850..6a975501ee 100644 --- a/vendor/github.com/stretchr/testify/require/require.go.tmpl +++ b/vendor/github.com/stretchr/testify/require/require.go.tmpl @@ -1,4 +1,4 @@ -{{ replace .Comment "assert." "require."}} +{{.CommentRequire}} func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index e6f7e94468..edac147ef4 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -147,7 +147,7 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs Equal(a.t, expected, actual, msgAndArgs...) } -// EqualError asserts that a function returned an error (i.e. not `nil`) +// EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -159,7 +159,7 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... EqualError(a.t, theError, errString, msgAndArgs...) } -// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() @@ -241,7 +241,7 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string Equalf(a.t, expected, actual, msg, args...) } -// Error asserts that a function returned an error (i.e. not `nil`). +// Error asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // a.Error(err) @@ -270,8 +270,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. ErrorAsf(a.t, err, target, msg, args...) } -// ErrorContains asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContains(err, expectedErrorSubString) @@ -282,8 +282,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . ErrorContains(a.t, theError, contains, msgAndArgs...) } -// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) -// and that the error contains the specified substring. +// ErrorContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") @@ -312,7 +312,7 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter ErrorIsf(a.t, err, target, msg, args...) } -// Errorf asserts that a function returned an error (i.e. not `nil`). +// Errorf asserts that a function returned a non-nil error (ie. an error). // // actualObj, err := SomeFunction() // a.Errorf(err, "error message %s", "formatted") @@ -373,10 +373,10 @@ func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), w // time.Sleep(8*time.Second) // externalValue = true // }() -// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// a.EventuallyWithTf(func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") +// }, 10*time.Second, 1*time.Second, "error message %s", "formatted") func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -539,8 +539,6 @@ func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args . // body that contains a string. // // a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -552,8 +550,6 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u // body that contains a string. // // a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -565,8 +561,6 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, // body that does not contain a string. // // a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -578,8 +572,6 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string // body that does not contain a string. // // a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -590,8 +582,6 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin // HTTPError asserts that a specified handler returns an error status code. // // a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -602,8 +592,6 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri // HTTPErrorf asserts that a specified handler returns an error status code. // // a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -614,8 +602,6 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str // HTTPRedirect asserts that a specified handler returns a redirect status code. // // a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -626,8 +612,6 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s // HTTPRedirectf asserts that a specified handler returns a redirect status code. // // a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -638,8 +622,6 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url // HTTPStatusCode asserts that a specified handler returns a specified status code. // // a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) -// -// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -650,8 +632,6 @@ func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url // HTTPStatusCodef asserts that a specified handler returns a specified status code. // // a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -662,8 +642,6 @@ func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, ur // HTTPSuccess asserts that a specified handler returns a success status code. // // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) -// -// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -674,8 +652,6 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // // a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1097,12 +1073,11 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) NoDirExistsf(a.t, path, msg, args...) } -// NoError asserts that a function returned no error (i.e. `nil`). +// NoError asserts that a function returned a nil error (ie. no error). // -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// a.NoError(err) +// a.Equal(expectedObj, actualObj) func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1110,12 +1085,11 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { NoError(a.t, err, msgAndArgs...) } -// NoErrorf asserts that a function returned no error (i.e. `nil`). +// NoErrorf asserts that a function returned a nil error (ie. no error). // -// actualObj, err := SomeFunction() -// if a.NoErrorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// a.NoErrorf(err, "error message %s", "formatted") +// a.Equal(expectedObj, actualObj) func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1203,9 +1177,8 @@ func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg // NotEmpty asserts that the specified object is NOT [Empty]. // -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } +// a.NotEmpty(obj) +// a.Equal("two", obj[1]) func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1215,9 +1188,8 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { // NotEmptyf asserts that the specified object is NOT [Empty]. // -// if a.NotEmptyf(obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// a.NotEmptyf(obj, "error message %s", "formatted") +// a.Equal("two", obj[1]) func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1691,7 +1663,19 @@ func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Ti WithinRangef(a.t, actual, start, end, msg, args...) } -// YAMLEq asserts that two YAML strings are equivalent. +// YAMLEq asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// a.YAMLEq(expected, actual) func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1699,7 +1683,19 @@ func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interf YAMLEq(a.t, expected, actual, msgAndArgs...) } -// YAMLEqf asserts that two YAML strings are equivalent. +// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. +// +// expected := `--- +// key: value +// --- +// key: this is a second document, it is not evaluated +// ` +// actual := `--- +// key: value +// --- +// key: this is a subsequent document, it is not evaluated +// ` +// a.YAMLEqf(expected, actual, "error message %s", "formatted") func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl b/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl index 54124df1d3..b3b751de48 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl +++ b/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl @@ -1,4 +1,4 @@ -{{.CommentWithoutT "a"}} +{{.CommentRequireWithoutT "a"}} func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) { if h, ok := a.t.(tHelper); ok { h.Helper() } {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) diff --git a/vendor/github.com/stretchr/testify/require/requirements.go b/vendor/github.com/stretchr/testify/require/requirements.go index 6b7ce929eb..375adb0a6a 100644 --- a/vendor/github.com/stretchr/testify/require/requirements.go +++ b/vendor/github.com/stretchr/testify/require/requirements.go @@ -12,18 +12,18 @@ type tHelper = interface { // ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful // for table driven tests. -type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) +type ComparisonAssertionFunc = func(TestingT, interface{}, interface{}, ...interface{}) // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful // for table driven tests. -type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) +type ValueAssertionFunc = func(TestingT, interface{}, ...interface{}) // BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful // for table driven tests. -type BoolAssertionFunc func(TestingT, bool, ...interface{}) +type BoolAssertionFunc = func(TestingT, bool, ...interface{}) // ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful // for table driven tests. -type ErrorAssertionFunc func(TestingT, error, ...interface{}) +type ErrorAssertionFunc = func(TestingT, error, ...interface{}) //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require.go.tmpl -include-format-funcs" diff --git a/vendor/go.podman.io/image/v5/docker/docker_image_src.go b/vendor/go.podman.io/image/v5/docker/docker_image_src.go index e93d338d28..4a4a029198 100644 --- a/vendor/go.podman.io/image/v5/docker/docker_image_src.go +++ b/vendor/go.podman.io/image/v5/docker/docker_image_src.go @@ -8,15 +8,20 @@ import ( "fmt" "io" "math" + "math/rand/v2" "mime" "mime/multipart" + "net" "net/http" "net/url" "os" "os/exec" "strings" "sync" + "time" + "github.com/docker/distribution/registry/api/errcode" + v2 "github.com/docker/distribution/registry/api/v2" digest "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" "go.podman.io/image/v5/docker/reference" @@ -48,6 +53,31 @@ type dockerImageSource struct { // State cachedManifest []byte // nil if not loaded yet cachedManifestMIMEType string // Only valid if cachedManifest != nil + + // Mirror fallback: when a blob fetch fails with a fallback-worthy error, + // try remaining pull sources before giving up. Protected by mirrorMu. + mirrorMu sync.Mutex + mirrorOverride *mirrorSource // If non-nil, this is the mirror and physicalRef for the override client + prevOverrides []*dockerClient // mirror clients replaced by a newer mirrorOverride; kept open because other goroutines may still be reading blobs through them, closed in Close() + remainingSources []sysregistriesv2.PullSource // later pull sources not yet tried, consumed front-to-back during fallback; nil when selected source is already the original source. + fallbackSys *types.SystemContext + fallbackRegConfig *registryConfiguration +} + +type mirrorSource struct { + client *dockerClient + ref dockerReference +} + +type pullEndpoint struct { + client *dockerClient + ref dockerReference + endpointSys *types.SystemContext +} + +type sourceAttempt struct { + ref reference.Named + err error } // newImageSource creates a new ImageSource for the specified image reference. @@ -86,12 +116,8 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef if err != nil { return nil, err } - type attempt struct { - ref reference.Named - err error - } - attempts := []attempt{} - for _, pullSource := range pullSources { + attempts := []sourceAttempt{} + for i, pullSource := range pullSources { if sys != nil && sys.DockerLogMirrorChoice { logrus.Infof("Trying to access %q", pullSource.Reference) } else { @@ -99,10 +125,15 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef } s, err := newImageSourceAttempt(ctx, sys, ref, pullSource, registryConfig) if err == nil { + if i+1 < len(pullSources) { + s.remainingSources = pullSources[i+1:] + s.fallbackSys = sys + s.fallbackRegConfig = registryConfig + } return s, nil } logrus.Debugf("Accessing %q failed: %v", pullSource.Reference, err) - attempts = append(attempts, attempt{ + attempts = append(attempts, sourceAttempt{ ref: pullSource.Reference, err: err, }) @@ -131,53 +162,37 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref dockerRef func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, logicalRef dockerReference, pullSource sysregistriesv2.PullSource, registryConfig *registryConfiguration, ) (*dockerImageSource, error) { - physicalRef, err := newReference(pullSource.Reference, false) + endpoint, err := newPullClient(sys, logicalRef, pullSource, registryConfig) if err != nil { return nil, err } - endpointSys := sys - // sys.DockerAuthConfig does not explicitly specify a registry; we must not blindly send the credentials intended for the primary endpoint to mirrors. - if endpointSys != nil && endpointSys.DockerAuthConfig != nil && reference.Domain(physicalRef.ref) != reference.Domain(logicalRef.ref) { - copy := *endpointSys - copy.DockerAuthConfig = nil - copy.DockerBearerRegistryToken = "" - endpointSys = © - } - - client, err := newDockerClientFromRef(endpointSys, physicalRef, registryConfig, false, "pull") - if err != nil { - return nil, err - } - client.tlsClientConfig.InsecureSkipVerify = pullSource.Endpoint.Insecure - client.namespaceProxy = pullSource.Endpoint.NamespaceProxy - s := &dockerImageSource{ PropertyMethodsInitialize: impl.PropertyMethods(impl.Properties{ HasThreadSafeGetBlob: true, }), logicalRef: logicalRef, - physicalRef: physicalRef, - c: client, + physicalRef: endpoint.ref, + c: endpoint.client, } s.Compat = impl.AddCompat(s) if err := s.ensureManifestIsLoaded(ctx); err != nil { - client.Close() + endpoint.client.Close() return nil, err } - if h, err := sysregistriesv2.AdditionalLayerStoreAuthHelper(endpointSys); err == nil && h != "" { + if h, err := sysregistriesv2.AdditionalLayerStoreAuthHelper(endpoint.endpointSys); err == nil && h != "" { acf := map[string]struct { Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` IdentityToken string `json:"identityToken,omitempty"` }{ - physicalRef.ref.String(): { - Username: client.auth.Username, - Password: client.auth.Password, - IdentityToken: client.auth.IdentityToken, + endpoint.ref.ref.String(): { + Username: endpoint.client.auth.Username, + Password: endpoint.client.auth.Password, + IdentityToken: endpoint.client.auth.IdentityToken, }, } acfD, err := json.Marshal(acf) @@ -198,6 +213,36 @@ func newImageSourceAttempt(ctx context.Context, sys *types.SystemContext, logica return s, nil } +// newPullClient creates a dockerClient, reference, and endpoint-specific SystemContext +// for the given pull source. +// The caller must call client.Close() when done. +func newPullClient(sys *types.SystemContext, logicalRef dockerReference, pullSource sysregistriesv2.PullSource, + registryConfig *registryConfiguration, +) (*pullEndpoint, error) { + physicalRef, err := newReference(pullSource.Reference, false) + if err != nil { + return nil, err + } + + endpointSys := sys + // sys.DockerAuthConfig does not explicitly specify a registry; we must not blindly send the credentials intended for the primary endpoint to mirrors. + if endpointSys != nil && endpointSys.DockerAuthConfig != nil && reference.Domain(physicalRef.ref) != reference.Domain(logicalRef.ref) { + copy := *endpointSys + copy.DockerAuthConfig = nil + copy.DockerBearerRegistryToken = "" + endpointSys = © + } + + client, err := newDockerClientFromRef(endpointSys, physicalRef, registryConfig, false, "pull") + if err != nil { + return nil, err + } + client.tlsClientConfig.InsecureSkipVerify = pullSource.Endpoint.Insecure + client.namespaceProxy = pullSource.Endpoint.NamespaceProxy + + return &pullEndpoint{client: client, ref: physicalRef, endpointSys: endpointSys}, nil +} + // Reference returns the reference used to set up this source, _as specified by the user_ // (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image. func (s *dockerImageSource) Reference() types.ImageReference { @@ -206,6 +251,18 @@ func (s *dockerImageSource) Reference() types.ImageReference { // Close removes resources associated with an initialized ImageSource, if any. func (s *dockerImageSource) Close() error { + s.mirrorMu.Lock() + prev := s.prevOverrides + override := s.mirrorOverride + s.prevOverrides = nil + s.mirrorOverride = nil + s.mirrorMu.Unlock() + for _, m := range prev { + m.Close() + } + if override != nil { + override.client.Close() + } return s.c.Close() } @@ -416,9 +473,88 @@ func (s *dockerImageSource) GetBlobAt(ctx context.Context, info types.BlobInfo, if err := info.Digest.Validate(); err != nil { // Make sure info.Digest.String() does not contain any unexpected characters return nil, nil, err } - path := fmt.Sprintf(blobsPath, reference.Path(s.physicalRef.ref), info.Digest.String()) + + client, physRef, hasRemaining := s.getActiveSource() + streams, errs, err := tryGetBlobAt(ctx, client, physRef, info, chunks, headers, hasRemaining) + if err == nil || !hasRemaining { + return streams, errs, err + } + if isMirrorTransientError(err) || isMirrorFallbackError(err) { + logrus.Debugf("Partial blob %s fetch from %q failed (%v), trying fallback sources", info.Digest, physRef.ref, err) + return s.getBlobAtWithMirrorFallback(ctx, info, chunks, headers, err, client) + } + return streams, errs, err +} + +// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown). +// The Digest field in BlobInfo is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided. +// May update BlobInfoCache, preferably after it knows for certain that a blob truly exists at a specific location. +func (s *dockerImageSource) GetBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache) (io.ReadCloser, int64, error) { + client, physRef, hasRemaining := s.getActiveSource() + reader, size, err := tryGetBlob(ctx, client, physRef, info, cache, hasRemaining) + if err == nil || !hasRemaining { + return reader, size, err + } + if isMirrorTransientError(err) || isMirrorFallbackError(err) { + logrus.Debugf("Blob %s fetch from %q failed (%v), trying fallback sources", info.Digest, physRef.ref, err) + return s.getBlobWithMirrorFallback(ctx, info, cache, err, client) + } + return reader, size, err +} + +func (s *dockerImageSource) getActiveSource() (*dockerClient, dockerReference, bool) { + s.mirrorMu.Lock() + defer s.mirrorMu.Unlock() + hasRemaining := len(s.remainingSources) > 0 + if s.mirrorOverride != nil { + return s.mirrorOverride.client, s.mirrorOverride.ref, hasRemaining + } + return s.c, s.physicalRef, hasRemaining +} + +// tryGetBlob attempts to fetch a blob, retrying once on transient errors if fallback mirrors remain. +func tryGetBlob(ctx context.Context, client *dockerClient, physRef dockerReference, + info types.BlobInfo, cache types.BlobInfoCache, hasRemainingSources bool, +) (io.ReadCloser, int64, error) { + reader, size, err := client.getBlob(ctx, physRef, info, cache) + if err != nil && hasRemainingSources && isMirrorTransientError(err) { + logrus.Debugf("Transient error fetching blob %s from %q, retrying: %v", info.Digest, physRef.ref, err) + delay := time.Second + rand.N(time.Second/10) // same as retry.IfNecessary first attempt: 1s + 10% jitter + select { + case <-ctx.Done(): + return nil, 0, fmt.Errorf("%w (while retrying after: %v)", ctx.Err(), err) + case <-time.After(delay): + } + reader, size, err = client.getBlob(ctx, physRef, info, cache) + } + return reader, size, err +} + +// tryGetBlobAt attempts to fetch a partial blob (range request), retrying once on transient errors +// if fallback mirrors remain. Mirrors tryGetBlob for the partial-pull (zstd:chunked) path. +func tryGetBlobAt(ctx context.Context, client *dockerClient, physRef dockerReference, + info types.BlobInfo, chunks []private.ImageSourceChunk, headers map[string][]string, hasRemainingSources bool, +) (chan io.ReadCloser, chan error, error) { + streams, errs, err := fetchBlobAt(ctx, client, physRef, info, chunks, headers) + if err != nil && hasRemainingSources && isMirrorTransientError(err) { + logrus.Debugf("Transient error fetching partial blob %s from %q, retrying: %v", info.Digest, physRef.ref, err) + delay := time.Second + rand.N(time.Second/10) + select { + case <-ctx.Done(): + return nil, nil, fmt.Errorf("%w (while retrying after: %v)", ctx.Err(), err) + case <-time.After(delay): + } + streams, errs, err = fetchBlobAt(ctx, client, physRef, info, chunks, headers) + } + return streams, errs, err +} + +func fetchBlobAt(ctx context.Context, client *dockerClient, physRef dockerReference, + info types.BlobInfo, chunks []private.ImageSourceChunk, headers map[string][]string, +) (chan io.ReadCloser, chan error, error) { + path := fmt.Sprintf(blobsPath, reference.Path(physRef.ref), info.Digest.String()) logrus.Debugf("Downloading %s", path) - res, err := s.c.makeRequest(ctx, http.MethodGet, path, headers, nil, v2Auth, nil) + res, err := client.makeRequest(ctx, http.MethodGet, path, headers, nil, v2Auth, nil) if err != nil { return nil, nil, err } @@ -436,27 +572,195 @@ func (s *dockerImageSource) GetBlobAt(ctx context.Context, info types.BlobInfo, if err != nil { return nil, nil, err } - streams := make(chan io.ReadCloser) errs := make(chan error) - go handle206Response(streams, errs, res.Body, chunks, mediaType, params) return streams, errs, nil case http.StatusBadRequest: res.Body.Close() return nil, nil, private.BadPartialRequestError{Status: res.Status} default: - err := registryHTTPResponseToError(res) + httpErr := registryHTTPResponseToError(res) res.Body.Close() - return nil, nil, fmt.Errorf("fetching partial blob: %w", err) + return nil, nil, fmt.Errorf("fetching partial blob: %w", httpErr) } } -// GetBlob returns a stream for the specified blob, and the blob’s size (or -1 if unknown). -// The Digest field in BlobInfo is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided. -// May update BlobInfoCache, preferably after it knows for certain that a blob truly exists at a specific location. -func (s *dockerImageSource) GetBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache) (io.ReadCloser, int64, error) { - return s.c.getBlob(ctx, s.physicalRef, info, cache) +// isMirrorFallbackError returns true for errors where the blob is not present +// on this source but may be available from another — warranting a fallback attempt. +// All tested registries (docker.io, registry.redhat.io, quay.io, registry.access.redhat.com) +// return BLOB_UNKNOWN for blob 404s, matching the OCI distribution spec. +func isMirrorFallbackError(err error) bool { + var ec errcode.ErrorCoder + if errors.As(err, &ec) { + switch ec.ErrorCode() { + case v2.ErrorCodeBlobUnknown: + // Blob endpoint returns HTTP 404 with errcode JSON body + // {"errors":[{"code":"BLOB_UNKNOWN",...}]}. + // registryHTTPResponseToError calls handleErrorResponse → parseHTTPErrorResponse, + // then unwraps errcode.Errors to the first errcode.Error. + return true + case errcode.ErrorCodeTooManyRequests: + // makeRequestToResolvedURL already retried 5 times with exponential + // backoff. This source's rate limit is exhausted — skip straight to + // the next mirror which has its own rate limit budget. + return true + } + } + + // UnexpectedHTTPStatusError (capital U) is NOT matched here — for regular blobs, + // handleErrorResponse never produces it for 4xx (only for 5xx). For external blobs, + // getExternalBlob() produces it via newUnexpectedHTTPStatusError() directly for any + // non-200 including 404, but external URLs are fixed and mirror-independent — + // a different registry mirror cannot serve them. + return false +} + +// isMirrorTransientError returns true for errors that are transient; the source +// probably has the blob but temporarily cannot serve it. +func isMirrorTransientError(err error) bool { + // HTTP 5xx: handleErrorResponse returns UnexpectedHTTPStatusError for status + // codes outside 400–499. Server-side error, another mirror may succeed. + var httpErr UnexpectedHTTPStatusError + if errors.As(err, &httpErr) && httpErr.StatusCode >= 500 { + return true + } + + // Network timeout: makeRequest returns net.Error with Timeout() == true. + // The mirror is reachable but slow — worth retrying on another. + var netErr net.Error + return errors.As(err, &netErr) && netErr.Timeout() +} + +// retireStaleOverride checks if the current mirrorOverride was set by another +// goroutine and differs from failedClient. If so, it returns the override's +// client and ref for the caller to retry. If the override is the same client +// that already failed, or is nil, it returns nil. Must be called with mirrorMu held. +func (s *dockerImageSource) retireStaleOverride(failedClient *dockerClient) (*dockerClient, dockerReference, bool) { + if s.mirrorOverride != nil && s.mirrorOverride.client != failedClient { + return s.mirrorOverride.client, s.mirrorOverride.ref, true + } + return nil, dockerReference{}, false +} + +// retireCurrentOverride moves the current mirrorOverride to prevOverrides. +// Must be called with mirrorMu held. +func (s *dockerImageSource) retireCurrentOverride() { + if s.mirrorOverride != nil { + s.prevOverrides = append(s.prevOverrides, s.mirrorOverride.client) + s.mirrorOverride = nil + } +} + +func formatFallbackError(attempts []sourceAttempt, originalErr error) error { + if len(attempts) == 0 { + return originalErr + } + extras := make([]string, 0, len(attempts)) + for _, a := range attempts { + extras = append(extras, fmt.Sprintf("[%s: %v]", a.ref.String(), a.err)) + } + return fmt.Errorf("(Fallback sources also failed: %s): %w", strings.Join(extras, "\n"), originalErr) +} + +func (s *dockerImageSource) getBlobWithMirrorFallback(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache, originalErr error, failedClient *dockerClient) (io.ReadCloser, int64, error) { + // Held for the full fallback loop, including network I/O. This serializes + // concurrent blob fetchers during fallback — only one goroutine probes + // sources while the rest block on getActiveSource(). Acceptable trade-off: + // fallback is rare and serialization prevents thundering-herd probing. + s.mirrorMu.Lock() + defer s.mirrorMu.Unlock() + + if client, physRef, ok := s.retireStaleOverride(failedClient); ok { + reader, size, err := tryGetBlob(ctx, client, physRef, info, cache, len(s.remainingSources) > 0) + if err == nil { + return reader, size, nil + } + s.retireCurrentOverride() + } + + attempts := []sourceAttempt{} + for len(s.remainingSources) > 0 { + pullSource := s.remainingSources[0] + s.remainingSources = s.remainingSources[1:] + + logrus.Debugf("Trying to access %q", pullSource.Reference) + + fallbackEndpoint, clientErr := newPullClient(s.fallbackSys, s.logicalRef, pullSource, s.fallbackRegConfig) + if clientErr != nil { + logrus.Debugf("Accessing %q failed: %v", pullSource.Reference, clientErr) + attempts = append(attempts, sourceAttempt{ref: pullSource.Reference, err: clientErr}) + continue + } + + reader, size, err := tryGetBlob(ctx, fallbackEndpoint.client, fallbackEndpoint.ref, info, cache, len(s.remainingSources) > 0) + if err != nil { + fallbackEndpoint.client.Close() + logrus.Debugf("Accessing %q failed: %v", pullSource.Reference, err) + attempts = append(attempts, sourceAttempt{ref: pullSource.Reference, err: err}) + if !isMirrorTransientError(err) && !isMirrorFallbackError(err) { + // non-transient errors are unlikely to be resolved by trying another source: stop probing further. + // break here to log the attempts and return originalErr below. + break + } + continue + } + + s.retireCurrentOverride() + s.mirrorOverride = &mirrorSource{client: fallbackEndpoint.client, ref: fallbackEndpoint.ref} + logrus.Debugf("Blob fetch succeeded from fallback source %q, switching to it for future requests", pullSource.Reference) + + return reader, size, nil + } + return nil, 0, formatFallbackError(attempts, originalErr) +} + +func (s *dockerImageSource) getBlobAtWithMirrorFallback(ctx context.Context, info types.BlobInfo, chunks []private.ImageSourceChunk, headers map[string][]string, originalErr error, failedClient *dockerClient) (chan io.ReadCloser, chan error, error) { + s.mirrorMu.Lock() + defer s.mirrorMu.Unlock() + + if client, physRef, ok := s.retireStaleOverride(failedClient); ok { + streams, errs, err := tryGetBlobAt(ctx, client, physRef, info, chunks, headers, len(s.remainingSources) > 0) + if err == nil { + return streams, errs, nil + } + s.retireCurrentOverride() + } + + attempts := []sourceAttempt{} + for len(s.remainingSources) > 0 { + pullSource := s.remainingSources[0] + s.remainingSources = s.remainingSources[1:] + + logrus.Debugf("Trying to access %q", pullSource.Reference) + + fallbackEndpoint, clientErr := newPullClient(s.fallbackSys, s.logicalRef, pullSource, s.fallbackRegConfig) + if clientErr != nil { + logrus.Debugf("Accessing %q failed: %v", pullSource.Reference, clientErr) + attempts = append(attempts, sourceAttempt{ref: pullSource.Reference, err: clientErr}) + continue + } + + streams, errs, err := tryGetBlobAt(ctx, fallbackEndpoint.client, fallbackEndpoint.ref, info, chunks, headers, len(s.remainingSources) > 0) + if err != nil { + fallbackEndpoint.client.Close() + logrus.Debugf("Accessing %q failed: %v", pullSource.Reference, err) + attempts = append(attempts, sourceAttempt{ref: pullSource.Reference, err: err}) + if !isMirrorTransientError(err) && !isMirrorFallbackError(err) { + // non-transient errors are unlikely to be resolved by trying another source: stop probing further. + // break here to log the attempts and return originalErr below. + break + } + continue + } + + s.retireCurrentOverride() + s.mirrorOverride = &mirrorSource{client: fallbackEndpoint.client, ref: fallbackEndpoint.ref} + logrus.Debugf("Partial blob fetch succeeded from fallback source %q, switching to it for future requests", pullSource.Reference) + + return streams, errs, nil + } + return nil, nil, formatFallbackError(attempts, originalErr) } // GetSignaturesWithFormat returns the image's signatures. It may use a remote (= slow) service. diff --git a/vendor/go.podman.io/image/v5/version/version.go b/vendor/go.podman.io/image/v5/version/version.go index adb2667d4f..40852fed62 100644 --- a/vendor/go.podman.io/image/v5/version/version.go +++ b/vendor/go.podman.io/image/v5/version/version.go @@ -8,7 +8,7 @@ const ( // VersionMinor is for functionality in a backwards-compatible manner VersionMinor = 41 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 0 + VersionPatch = 1 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "" diff --git a/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go index 8d99551fee..b1da45687c 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (!amd64 && !loong64 && !ppc64le && !ppc64 && !s390x) || !gc || purego +//go:build (!amd64 && !loong64 && !ppc64le && !ppc64 && !riscv64 && !s390x) || !gc || purego package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_asm.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_asm.go index 315b84ac39..55041bf51d 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_asm.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_asm.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build gc && !purego && (amd64 || loong64 || ppc64 || ppc64le) +//go:build gc && !purego && (amd64 || loong64 || ppc64 || ppc64le || riscv64) package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_riscv64.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_riscv64.s new file mode 100644 index 0000000000..ce5eb3d43d --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_riscv64.s @@ -0,0 +1,158 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +#define LOAD64U(base, offset, t0, t1, t2, t3, dst) \ + MOVBU (offset+0*1)(base), t0; \ + MOVBU (offset+1*1)(base), t1; \ + MOVBU (offset+2*1)(base), t2; \ + MOVBU (offset+3*1)(base), t3; \ + SLL $8, t1; \ + SLL $16, t2; \ + SLL $24, t3; \ + OR t1, t0; \ + OR t3, t2; \ + OR t2, t0, dst; \ + MOVBU (offset+4*1)(base), t0; \ + MOVBU (offset+5*1)(base), t1; \ + MOVBU (offset+6*1)(base), t2; \ + MOVBU (offset+7*1)(base), t3; \ + SLL $32, t0; \ + SLL $40, t1; \ + SLL $48, t2; \ + SLL $56, t3; \ + OR t1, t0; \ + OR t3, t2; \ + OR t2, t0; \ + OR t0, dst + +// func update(state *macState, msg []byte) +TEXT ·update(SB), $0-32 + MOV state+0(FP), X5 + MOV msg_base+8(FP), X6 + MOV msg_len+16(FP), X7 + + MOV $16, X8 + + AND $7, X6, X28 + + MOV (0*8)(X5), X9 // h0 + MOV (1*8)(X5), X10 // h1 + MOV (2*8)(X5), X11 // h2 + MOV (3*8)(X5), X12 // r0 + MOV (4*8)(X5), X13 // r1 + + BLT X7, X8, tail + +loop: + BEQZ X28, aligned_load + + LOAD64U(X6, 0*8, X16, X18, X19, X20, X15) // msg[0:8] + LOAD64U(X6, 1*8, X16, X18, X19, X20, X17) // msg[8:16] + JMP block + +aligned_load: + MOV (0*8)(X6), X15 // msg[0:8] + MOV (1*8)(X6), X17 // msg[8:16] + +block: + ADD X15, X9 // h0 (x1 + y1 = z1', if z1' < x1 then z1' overflow) + SLTU X15, X9, X19 // h0.carry + ADD X17, X10, X22 + SLTU X17, X22, X23 + ADD X22, X19, X10 // h1 + SLTU X22, X10, X19 + OR X23, X19 // h1.carry + ADD $1, X19 + ADD X19, X11 // h2 + + ADD $16, X6 // msg = msg[16:] + +multiply: + MULHU X9, X12, X16 // h0r0.hi + MUL X9, X12, X15 // h0r0.lo + MULHU X10, X12, X17 // h1r0.hi + MUL X10, X12, X14 // h1r0.lo + ADD X14, X16 + SLTU X14, X16, X19 + ADD X19, X17 + MUL X11, X12, X20 + ADD X17, X20 + MULHU X9, X13, X17 // h0r1.hi + MUL X9, X13, X14 // h0r1.lo + ADD X14, X16 + SLTU X14, X16, X19 + ADD X19, X17 + MOV X17, X9 + MUL X11, X13, X21 // h2r1 + MULHU X10, X13, X17 // h1r1.hi + MUL X10, X13, X14 // h1r1.lo + ADD X14, X20 + ADD X17, X21, X22 + SLTU X14, X20, X19 + ADD X22, X19, X21 + ADD X9, X20 + SLTU X9, X20, X19 + ADD X19, X21 + AND $3, X20, X11 + AND $-4, X20, X18 + ADD X18, X15, X9 + ADD X21, X16, X22 + SLTU X18, X9, X19 + SLTU X21, X22, X23 + ADD X22, X19, X10 + SLTU X22, X10, X19 + OR X19, X23, X19 + ADD X19, X11 + SLL $62, X21, X22 + SRL $2, X20, X23 + SRL $2, X21, X21 + OR X22, X23, X20 + ADD X20, X9, X9 + ADD X21, X10, X22 + SLTU X20, X9, X19 + SLTU X21, X22, X23 + ADD X22, X19, X10 + SLTU X22, X10, X19 + OR X19, X23, X19 + ADD X19, X11, X11 + + SUB $16, X7, X7 + BGE X7, X8, loop + +tail: + BEQ X7, X0, done + MOV $1, X15 + MOV $0, X16 + ADD X7, X6, X6 + +flush_buffer: + MOVBU -1(X6), X20 + SRL $56, X15, X19 + SLL $8, X16, X23 + SLL $8, X15, X15 + OR X19, X23, X16 + XOR X20, X15 + SUB $1, X7, X7 + SUB $1, X6, X6 + BNE X7, X0, flush_buffer + + ADD X15, X9 + SLTU X15, X9, X19 + ADD X16, X10, X22 + SLTU X16, X22, X23 + ADD X22, X19, X10 + SLTU X22, X10, X19 + OR X23, X19 + ADD X19, X11 + + MOV $16, X7 + JMP multiply + +done: + MOV X9, (0*8)(X5) // h0 + MOV X10, (1*8)(X5) + MOV X11, (2*8)(X5) + RET diff --git a/vendor/golang.org/x/net/http/httpproxy/proxy.go b/vendor/golang.org/x/net/http/httpproxy/proxy.go index 5ab499b0bc..1e260f4444 100644 --- a/vendor/golang.org/x/net/http/httpproxy/proxy.go +++ b/vendor/golang.org/x/net/http/httpproxy/proxy.go @@ -80,18 +80,19 @@ type config struct { domainMatchers []matcher } -// FromEnvironment returns a Config instance populated from the -// environment variables HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the -// lowercase versions thereof). +// FromEnvironment returns a Config instance populated from the environment +// variables HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the lowercase versions +// thereof). When both the uppercase and lowercase versions are provided, the +// lowercase versions are prioritized. // // The environment values may be either a complete URL or a // "host[:port]", in which case the "http" scheme is assumed. An error // is returned if the value is a different form. func FromEnvironment() *Config { return &Config{ - HTTPProxy: getEnvAny("HTTP_PROXY", "http_proxy"), - HTTPSProxy: getEnvAny("HTTPS_PROXY", "https_proxy"), - NoProxy: getEnvAny("NO_PROXY", "no_proxy"), + HTTPProxy: getEnvAny("http_proxy", "HTTP_PROXY"), + HTTPSProxy: getEnvAny("https_proxy", "HTTPS_PROXY"), + NoProxy: getEnvAny("no_proxy", "NO_PROXY"), CGI: os.Getenv("REQUEST_METHOD") != "", } } diff --git a/vendor/golang.org/x/net/http2/hpack/encode.go b/vendor/golang.org/x/net/http2/hpack/encode.go index 46219da2b0..e6d0c265ac 100644 --- a/vendor/golang.org/x/net/http2/hpack/encode.go +++ b/vendor/golang.org/x/net/http2/hpack/encode.go @@ -39,7 +39,6 @@ func NewEncoder(w io.Writer) *Encoder { tableSizeUpdate: false, w: w, } - e.dynTab.table.init() e.dynTab.setMaxSize(initialHeaderTableSize) return e } diff --git a/vendor/golang.org/x/net/http2/hpack/hpack.go b/vendor/golang.org/x/net/http2/hpack/hpack.go index 7a1d976696..ecd3eeca99 100644 --- a/vendor/golang.org/x/net/http2/hpack/hpack.go +++ b/vendor/golang.org/x/net/http2/hpack/hpack.go @@ -105,7 +105,6 @@ func NewDecoder(maxDynamicTableSize uint32, emitFunc func(f HeaderField)) *Decod emitEnabled: true, firstField: true, } - d.dynTab.table.init() d.dynTab.allowedMaxSize = maxDynamicTableSize d.dynTab.setMaxSize(maxDynamicTableSize) return d diff --git a/vendor/golang.org/x/net/http2/hpack/tables.go b/vendor/golang.org/x/net/http2/hpack/tables.go index 8cbdf3f019..3bd7eb7533 100644 --- a/vendor/golang.org/x/net/http2/hpack/tables.go +++ b/vendor/golang.org/x/net/http2/hpack/tables.go @@ -31,10 +31,18 @@ type headerFieldTable struct { // byName maps a HeaderField name to the unique id of the newest entry with // the same name. See above for a definition of "unique id". + // + // byName and byNameValue are used only by search, which is only called + // for tables used by encoders. For tables used only by decoders, the + // maps are never built, as a memory optimization for servers with many + // mostly-idle connections, each pinning a dynamic table. The maps are + // built lazily by the first search call and are nil until then. The two + // maps are always both nil or both non-nil. byName map[string]uint64 // byNameValue maps a HeaderField name/value pair to the unique id of the newest // entry with the same name and value. See above for a definition of "unique id". + // See byName for when this map is non-nil. byNameValue map[pairNameValue]uint64 } @@ -42,9 +50,17 @@ type pairNameValue struct { name, value string } -func (t *headerFieldTable) init() { - t.byName = make(map[string]uint64) - t.byNameValue = make(map[pairNameValue]uint64) +// buildMaps initializes byName and byNameValue from ents. +func (t *headerFieldTable) buildMaps() { + t.byName = make(map[string]uint64, len(t.ents)) + t.byNameValue = make(map[pairNameValue]uint64, len(t.ents)) + for k, f := range t.ents { + // Map to the newest matching entry: later (newer) entries + // overwrite earlier ones, matching addEntry's behavior. + id := t.evictCount + uint64(k) + 1 + t.byName[f.Name] = id + t.byNameValue[pairNameValue{f.Name, f.Value}] = id + } } // len reports the number of entries in the table. @@ -54,9 +70,11 @@ func (t *headerFieldTable) len() int { // addEntry adds a new entry. func (t *headerFieldTable) addEntry(f HeaderField) { - id := uint64(t.len()) + t.evictCount + 1 - t.byName[f.Name] = id - t.byNameValue[pairNameValue{f.Name, f.Value}] = id + if t.byName != nil { + id := uint64(t.len()) + t.evictCount + 1 + t.byName[f.Name] = id + t.byNameValue[pairNameValue{f.Name, f.Value}] = id + } t.ents = append(t.ents, f) } @@ -65,14 +83,16 @@ func (t *headerFieldTable) evictOldest(n int) { if n > t.len() { panic(fmt.Sprintf("evictOldest(%v) on table with %v entries", n, t.len())) } - for k := 0; k < n; k++ { - f := t.ents[k] - id := t.evictCount + uint64(k) + 1 - if t.byName[f.Name] == id { - delete(t.byName, f.Name) - } - if p := (pairNameValue{f.Name, f.Value}); t.byNameValue[p] == id { - delete(t.byNameValue, p) + if t.byName != nil { + for k := 0; k < n; k++ { + f := t.ents[k] + id := t.evictCount + uint64(k) + 1 + if t.byName[f.Name] == id { + delete(t.byName, f.Name) + } + if p := (pairNameValue{f.Name, f.Value}); t.byNameValue[p] == id { + delete(t.byNameValue, p) + } } } copy(t.ents, t.ents[n:]) @@ -100,6 +120,9 @@ func (t *headerFieldTable) evictOldest(n int) { // // See Section 2.3.3. func (t *headerFieldTable) search(f HeaderField) (i uint64, nameValueMatch bool) { + if t.byName == nil { + t.buildMaps() + } if !f.Sensitive { if id := t.byNameValue[pairNameValue{f.Name, f.Value}]; id != 0 { return t.idToIndex(id), true diff --git a/vendor/golang.org/x/net/http2/transport_wrap.go b/vendor/golang.org/x/net/http2/transport_wrap.go index 534e77ab96..741fb97062 100644 --- a/vendor/golang.org/x/net/http2/transport_wrap.go +++ b/vendor/golang.org/x/net/http2/transport_wrap.go @@ -237,32 +237,40 @@ type ClientConn struct { } func (cc *ClientConn) roundTrip(req *http.Request) (*http.Response, error) { - err := func() error { + haveReservation, err := func() (bool, error) { cc.mu.Lock() defer cc.mu.Unlock() if cc.doNotReuse { - return errClientConnUnusable - } - cc.roundTrips++ - if cc.reserved > 0 { - // We've already reserved a concurrency slot for this request. - cc.reserved-- - } else if cc.cc.Reserve() != nil { - // We don't seem to have an available concurrency slot, - // so bump the pending count (requests waiting for a slot). - cc.pending++ + return false, errClientConnUnusable } + // ClientConn.Shutdown will not shut down the conn while // cc.starting > 0 or cc.cc.InFlight() > 0. // // The starting state covers the gap between us deciding to // start sending the request, and actually sending it. cc.starting++ - return nil + + cc.roundTrips++ + if cc.reserved == 0 { + // We do not have a concurrency slot reserved for this request. + return false, nil + } + cc.reserved-- + return true, nil }() if err != nil { return nil, err } + // If we have no reservation, try to acquire one. + // (This must be done without cc.mu held, since Reserve may call back to the state hook.) + if !haveReservation && cc.cc.Reserve() != nil { + // We could not acquire a concurrency slot, so bump the pending count + // (requests waiting for a slot). + cc.mu.Lock() + cc.pending++ + cc.mu.Unlock() + } resp, err := cc.cc.RoundTrip(req) cc.mu.Lock() cc.starting-- @@ -293,16 +301,21 @@ func (cc *ClientConn) ping(ctx context.Context) error { } func (cc *ClientConn) reserveNewRequest() bool { + if err := cc.cc.Reserve(); err != nil { + return false + } + reserved := true cc.mu.Lock() - defer cc.mu.Unlock() if cc.doNotReuse { - return false + reserved = false + } else { + cc.reserved++ } - if err := cc.cc.Reserve(); err != nil { - return false + cc.mu.Unlock() + if !reserved { + cc.cc.Release() } - cc.reserved++ - return true + return reserved } func (cc *ClientConn) setDoNotReuse() { diff --git a/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go index 0d6d0bced0..9c7401c9ba 100644 --- a/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go +++ b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go @@ -30,6 +30,7 @@ import ( "slices" "strconv" "strings" + "sync" "golang.org/x/tools/internal/typesinternal" ) @@ -126,7 +127,8 @@ func For(obj types.Object) (Path, error) { // An Encoder amortizes the cost of encoding the paths of multiple objects. // The zero value of an Encoder is ready to use. type Encoder struct { - pkgIndex map[*types.Package]*pkgIndex + pkgIndexMu sync.Mutex + pkgIndex map[*types.Package]*pkgIndex } // A traversal encapsulates the state of a single traversal of the object/type graph. @@ -191,6 +193,8 @@ type pkgIndex struct { // For returns the path to an object relative to its package, // or an error if the object is not accessible from the package's Scope. // +// For is safe for concurrent use. +// // The For function guarantees to return a path only for the following objects: // - package-level types // - exported package-level non-types @@ -320,6 +324,9 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { panic(obj) } + enc.pkgIndexMu.Lock() + defer enc.pkgIndexMu.Unlock() + // 4. Search the object/type graph for the path to // the var (field/param/result) or method. ix, ok := enc.pkgIndex[pkg] diff --git a/vendor/golang.org/x/tools/internal/imports/fix.go b/vendor/golang.org/x/tools/internal/imports/fix.go index b99ea6c914..6aa1e27e3a 100644 --- a/vendor/golang.org/x/tools/internal/imports/fix.go +++ b/vendor/golang.org/x/tools/internal/imports/fix.go @@ -32,6 +32,7 @@ import ( "golang.org/x/tools/internal/event" "golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/gopathwalk" + "golang.org/x/tools/internal/modindex" "golang.org/x/tools/internal/stdlib" ) @@ -320,6 +321,7 @@ func (p *pass) importIdentifier(imp *ImportInfo) string { // load reads in everything necessary to run a pass, and reports whether the // file already has all the imports it needs. It fills in p.missingRefs with the // file's missing symbols, if any, or removes unused imports if not. +// This is called 3(!) times: self, otherFiles, loadRealPackageNames func (p *pass) load(ctx context.Context) ([]*ImportFix, bool) { p.knownPackages = map[string]*PackageInfo{} p.missingRefs = References{} @@ -577,6 +579,17 @@ func getFixes(ctx context.Context, fset *token.FileSet, f *ast.File, filename st } func getFixesWithSource(ctx context.Context, fset *token.FileSet, f *ast.File, filename string, goroot string, logf func(string, ...any), source Source) ([]*ImportFix, error) { + // If there is an Index for the GOMODCACHE, remember that, and later make it so that the + // directory walk doesn't go into the module cache, since we already have all the information + var ix *modindex.Index + src, ok := source.(*ProcessEnvSource) + if ok { + var err error + if ix, err = modindex.Read(src.env.Env["GOMODCACHE"]); err != nil { + ix = nil // don't use it if there was an error + } + } + // This logic is defensively duplicated from getFixes. abs, err := filepath.Abs(filename) if err != nil { @@ -635,6 +648,20 @@ func getFixesWithSource(ctx context.Context, fset *token.FileSet, f *ast.File, f } p.loadRealPackageNames = true p.otherFiles = otherFiles + if ix != nil { + src, ok := p.source.(*ProcessEnvSource) + if ok { + // For safety, clone the env so that we don't modify the caller's env. + env := *src.env + env.Env = maps.Clone(src.env.Env) + src.env = &env + // avoid looking in the module cache, as we have the index instead: + // This makes a later call to newModuleresolver (from + // LoadPackageNames) produce a resolver that will not look + // in the module cache + src.env.Env["GOMODCACHE"] = "" + } + } if fixes, done := p.load(ctx); done { return fixes, nil } @@ -649,7 +676,7 @@ func getFixesWithSource(ctx context.Context, fset *token.FileSet, f *ast.File, f // Go look for candidates in $GOPATH, etc. We don't necessarily load // the real exports of sibling imports, so keep assuming their contents. - if err := addExternalCandidates(ctx, p, p.missingRefs, filename); err != nil { + if err := addExternalCandidates(ctx, p, p.missingRefs, filename, ix); err != nil { return nil, err } @@ -1184,7 +1211,7 @@ type scanCallback struct { exportsLoaded func(pkg *pkg, exports []stdlib.Symbol) } -func addExternalCandidates(ctx context.Context, pass *pass, refs References, filename string) error { +func addExternalCandidates(ctx context.Context, pass *pass, refs References, filename string, ix *modindex.Index) error { ctx, done := event.Start(ctx, "imports.addExternalCandidates") defer done() @@ -1193,6 +1220,24 @@ func addExternalCandidates(ctx context.Context, pass *pass, refs References, fil return err } + // Add candidates from the module cache. + if ix != nil { + for k, v := range refs { + for n := range v { + cands := ix.Lookup(k, n, false) + for _, cand := range cands { + x := &Result{ + &ImportInfo{ImportPath: cand.ImportPath}, + &PackageInfo{Name: cand.PkgName, + Exports: map[string]bool{cand.Name: true}, + }, + } + results = append(results, x) + } + } + } + } + for _, result := range results { if result == nil { continue diff --git a/vendor/golang.org/x/tools/internal/imports/mod.go b/vendor/golang.org/x/tools/internal/imports/mod.go index 76fae6a74b..a05faf9716 100644 --- a/vendor/golang.org/x/tools/internal/imports/mod.go +++ b/vendor/golang.org/x/tools/internal/imports/mod.go @@ -166,10 +166,7 @@ func newModuleResolver(e *ProcessEnv, moduleCacheCache *DirInfoCache) (*ModuleRe } } - r.moduleCacheDir = gomodcacheForEnv(goenv) - if r.moduleCacheDir == "" { - return nil, fmt.Errorf("cannot resolve GOMODCACHE") - } + r.moduleCacheDir = goenv["GOMODCACHE"] sort.Slice(r.modsByModPath, func(i, j int) bool { count := func(x int) int { @@ -238,26 +235,6 @@ func newModuleResolver(e *ProcessEnv, moduleCacheCache *DirInfoCache) (*ModuleRe return r, nil } -// gomodcacheForEnv returns the GOMODCACHE value to use based on the given env -// map, which must have GOMODCACHE and GOPATH populated. -// -// TODO(rfindley): this is defensive refactoring. -// 1. Is this even relevant anymore? Can't we just read GOMODCACHE. -// 2. Use this to separate module cache scanning from other scanning. -func gomodcacheForEnv(goenv map[string]string) string { - if gmc := goenv["GOMODCACHE"]; gmc != "" { - // golang/go#67156: ensure that the module cache is clean, since it is - // assumed as a prefix to directories scanned by gopathwalk, which are - // themselves clean. - return filepath.Clean(gmc) - } - gopaths := filepath.SplitList(goenv["GOPATH"]) - if len(gopaths) == 0 { - return "" - } - return filepath.Join(gopaths[0], "/pkg/mod") -} - func (r *ModuleResolver) initAllMods() error { stdout, err := r.env.invokeGo(context.TODO(), "list", "-m", "-e", "-json", "...") if err != nil { diff --git a/vendor/golang.org/x/tools/internal/imports/source_env.go b/vendor/golang.org/x/tools/internal/imports/source_env.go index ec996c3ccf..272ae80610 100644 --- a/vendor/golang.org/x/tools/internal/imports/source_env.go +++ b/vendor/golang.org/x/tools/internal/imports/source_env.go @@ -53,7 +53,7 @@ func (s *ProcessEnvSource) ResolveReferences(ctx context.Context, filename strin found := make(map[string][]pkgDistance) callback := &scanCallback{ rootFound: func(gopathwalk.Root) bool { - return true // We want everything. + return true }, dirFound: func(pkg *pkg) bool { return pkgIsCandidate(filename, refs, pkg) diff --git a/vendor/golang.org/x/tools/internal/modindex/directories.go b/vendor/golang.org/x/tools/internal/modindex/directories.go new file mode 100644 index 0000000000..9a963744b5 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/modindex/directories.go @@ -0,0 +1,131 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package modindex + +import ( + "fmt" + "log" + "os" + "path/filepath" + "regexp" + "strings" + "sync" + "time" + + "golang.org/x/mod/semver" + "golang.org/x/tools/internal/gopathwalk" +) + +type directory struct { + path string // relative to GOMODCACHE + importPath string + version string // semantic version +} + +// bestDirByImportPath returns the best directory for each import +// path, where "best" means most recent semantic version. These import +// paths are inferred from the GOMODCACHE-relative dir names in dirs. +func bestDirByImportPath(dirs []string) (map[string]directory, error) { + dirsByPath := make(map[string]directory) + for _, dir := range dirs { + importPath, version, err := dirToImportPathVersion(dir) + if err != nil { + return nil, err + } + new := directory{ + path: dir, + importPath: importPath, + version: version, + } + if old, ok := dirsByPath[importPath]; !ok || compareDirectory(new, old) < 0 { + dirsByPath[importPath] = new + } + } + return dirsByPath, nil +} + +// compareDirectory defines an ordering of path@version directories, +// by descending version, then by ascending path. +func compareDirectory(x, y directory) int { + if sign := -semver.Compare(x.version, y.version); sign != 0 { + return sign // latest first + } + return strings.Compare(string(x.path), string(y.path)) +} + +// modCacheRegexp splits a relpathpath into module, module version, and package. +var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`) + +// dirToImportPathVersion computes import path and semantic version +// from a GOMODCACHE-relative directory name. +func dirToImportPathVersion(dir string) (string, string, error) { + m := modCacheRegexp.FindStringSubmatch(string(dir)) + // m[1] is the module path + // m[2] is the version major.minor.patch(-

 1 && flds[1][1] == 'D',
+			}
+			if px.Type == Func {
+				n, err := strconv.Atoi(flds[2])
+				if err != nil {
+					continue // should never happen
+				}
+				px.Results = int16(n)
+				if len(flds) >= 4 {
+					sig := strings.Split(flds[3], " ")
+					for i := range sig {
+						// $ cannot otherwise occur. removing the spaces
+						// almost works, but for chan struct{}, e.g.
+						sig[i] = strings.Replace(sig[i], "$", " ", -1)
+					}
+					px.Sig = toFields(sig)
+				}
+			}
+			ans = append(ans, px)
+		}
+	}
+	return ans
+}
+
+func toFields(sig []string) []Field {
+	ans := make([]Field, len(sig)/2)
+	for i := range ans {
+		ans[i] = Field{Arg: sig[2*i], Type: sig[2*i+1]}
+	}
+	return ans
+}
+
+// benchmarks show this is measurably better than strings.Split
+// split into first 4 fields separated by single space
+func fastSplit(x string) []string {
+	ans := make([]string, 0, 4)
+	nxt := 0
+	start := 0
+	for i := 0; i < len(x); i++ {
+		if x[i] != ' ' {
+			continue
+		}
+		ans = append(ans, x[start:i])
+		nxt++
+		start = i + 1
+		if nxt >= 3 {
+			break
+		}
+	}
+	ans = append(ans, x[start:])
+	return ans
+}
+
+func asLexType(c byte) LexType {
+	switch c {
+	case 'C':
+		return Const
+	case 'V':
+		return Var
+	case 'T':
+		return Type
+	case 'F':
+		return Func
+	}
+	return -1
+}
diff --git a/vendor/golang.org/x/tools/internal/modindex/modindex.go b/vendor/golang.org/x/tools/internal/modindex/modindex.go
new file mode 100644
index 0000000000..16a7cabb94
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/modindex/modindex.go
@@ -0,0 +1,121 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package modindex contains code for building and searching an
+// [Index] of the Go module cache.
+package modindex
+
+// The directory containing the index, returned by
+// [IndexDir], contains a file index-name- that contains the name
+// of the current index. We believe writing that short file is atomic.
+// [Read] reads that file to get the file name of the index.
+// WriteIndex writes an index with a unique name and then
+// writes that name into a new version of index-name-.
+// ( stands for the CurrentVersion of the index format.)
+
+import (
+	"maps"
+	"os"
+	"path/filepath"
+	"slices"
+	"strings"
+	"time"
+
+	"golang.org/x/mod/semver"
+)
+
+// Update updates the index for the specified Go
+// module cache directory, creating it as needed.
+// On success it returns the current index.
+func Update(gomodcache string) (*Index, error) {
+	prev, err := Read(gomodcache)
+	if err != nil {
+		if !os.IsNotExist(err) {
+			return nil, err
+		}
+		prev = nil
+	}
+	return update(gomodcache, prev)
+}
+
+// update builds, writes, and returns the current index.
+//
+// If old is nil, the new index is built from all of GOMODCACHE;
+// otherwise it is built from the old index plus cache updates
+// since the previous index's time.
+func update(gomodcache string, old *Index) (*Index, error) {
+	gomodcache, err := filepath.Abs(gomodcache)
+	if err != nil {
+		return nil, err
+	}
+	new, changed, err := build(gomodcache, old)
+	if err != nil {
+		return nil, err
+	}
+	if old == nil || changed {
+		if err := write(gomodcache, new); err != nil {
+			return nil, err
+		}
+	}
+	return new, nil
+}
+
+// build returns a new index for the specified Go module cache (an
+// absolute path).
+//
+// If an old index is provided, only directories more recent than it
+// that it are scanned; older directories are provided by the old
+// Index.
+//
+// The boolean result indicates whether new entries were found.
+func build(gomodcache string, old *Index) (*Index, bool, error) {
+	// Set the time window.
+	var start time.Time // = dawn of time
+	if old != nil {
+		// -1s to accommodate skew between (fine-grained) time.Now
+		// and (coarse) file system's mtime clock.
+		start = old.ValidAt.Add(-1 * time.Second)
+	}
+	now := time.Now()
+	end := now.Add(24 * time.Hour) // safely in the future
+
+	// Enumerate GOMODCACHE package directories.
+	// Choose the best (latest) package for each import path.
+	pkgDirs := findDirs(gomodcache, start, end)
+	dirByPath, err := bestDirByImportPath(pkgDirs)
+	if err != nil {
+		return nil, false, err
+	}
+
+	// For each import path it might occur only in
+	// dirByPath, only in old, or in both.
+	// If both, use the semantically later one.
+	var entries []Entry
+	if old != nil {
+		for _, entry := range old.Entries {
+			dir, ok := dirByPath[entry.ImportPath]
+			if !ok || semver.Compare(dir.version, entry.Version) <= 0 {
+				// New dir is missing or not more recent; use old entry.
+				entries = append(entries, entry)
+				delete(dirByPath, entry.ImportPath)
+			}
+		}
+	}
+
+	// Extract symbol information for all the new directories.
+	newEntries := extractSymbols(gomodcache, maps.Values(dirByPath))
+	entries = append(entries, newEntries...)
+	slices.SortFunc(entries, func(x, y Entry) int {
+		if n := strings.Compare(x.PkgName, y.PkgName); n != 0 {
+			return n
+		}
+		return strings.Compare(x.ImportPath, y.ImportPath)
+	})
+
+	return &Index{
+		GOMODCACHE: gomodcache,
+		ValidAt:    now, // time before the directories were scanned
+		Entries:    entries,
+	}, len(newEntries) > 0, nil
+}
diff --git a/vendor/golang.org/x/tools/internal/modindex/symbols.go b/vendor/golang.org/x/tools/internal/modindex/symbols.go
new file mode 100644
index 0000000000..8e9702d84b
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/modindex/symbols.go
@@ -0,0 +1,244 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package modindex
+
+import (
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"iter"
+	"os"
+	"path/filepath"
+	"runtime"
+	"slices"
+	"strings"
+	"sync"
+
+	"golang.org/x/sync/errgroup"
+)
+
+// The name of a symbol contains information about the symbol:
+//  T for types, TD if the type is deprecated
+//  C for consts, CD if the const is deprecated
+//  V for vars, VD if the var is deprecated
+// and for funcs:  F  ( )*
+// any spaces in  are replaced by $s so that the fields
+// of the name are space separated. F is replaced by FD if the func
+// is deprecated.
+type symbol struct {
+	pkg  string // name of the symbols's package
+	name string // declared name
+	kind string // T, C, V, or F, followed by D if deprecated
+	sig  string // signature information, for F
+}
+
+// extractSymbols returns a (new, unordered) array of Entries, one for
+// each provided package directory, describing its exported symbols.
+func extractSymbols(cwd string, dirs iter.Seq[directory]) []Entry {
+	var (
+		mu      sync.Mutex
+		entries []Entry
+	)
+
+	var g errgroup.Group
+	g.SetLimit(max(2, runtime.GOMAXPROCS(0)/2))
+	for dir := range dirs {
+		g.Go(func() error {
+			thedir := filepath.Join(cwd, string(dir.path))
+			mode := parser.SkipObjectResolution | parser.ParseComments
+
+			// Parse all Go files in dir and extract symbols.
+			dirents, err := os.ReadDir(thedir)
+			if err != nil {
+				return nil // log this someday?
+			}
+			var syms []symbol
+			for _, dirent := range dirents {
+				if !strings.HasSuffix(dirent.Name(), ".go") ||
+					strings.HasSuffix(dirent.Name(), "_test.go") {
+					continue
+				}
+				fname := filepath.Join(thedir, dirent.Name())
+				tr, err := parser.ParseFile(token.NewFileSet(), fname, nil, mode)
+				if err != nil {
+					continue // ignore errors, someday log them?
+				}
+				syms = append(syms, getFileExports(tr)...)
+			}
+
+			// Create an entry for the package.
+			pkg, names := processSyms(syms)
+			if pkg != "" {
+				mu.Lock()
+				defer mu.Unlock()
+				entries = append(entries, Entry{
+					PkgName:    pkg,
+					Dir:        dir.path,
+					ImportPath: dir.importPath,
+					Version:    dir.version,
+					Names:      names,
+				})
+			}
+
+			return nil
+		})
+	}
+	g.Wait() // ignore error
+
+	return entries
+}
+
+func getFileExports(f *ast.File) []symbol {
+	pkg := f.Name.Name
+	if pkg == "main" || pkg == "" {
+		return nil
+	}
+	var ans []symbol
+	// should we look for //go:build ignore?
+	for _, decl := range f.Decls {
+		switch decl := decl.(type) {
+		case *ast.FuncDecl:
+			if decl.Recv != nil {
+				// ignore methods, as we are completing package selections
+				continue
+			}
+			name := decl.Name.Name
+			dtype := decl.Type
+			// not looking at dtype.TypeParams. That is, treating
+			// generic functions just like non-generic ones.
+			sig := dtype.Params
+			kind := "F"
+			if isDeprecated(decl.Doc) {
+				kind += "D"
+			}
+			result := []string{fmt.Sprintf("%d", dtype.Results.NumFields())}
+			for _, x := range sig.List {
+				// This code creates a string representing the type.
+				// TODO(pjw): it may be fragile:
+				// 1. x.Type could be nil, perhaps in ill-formed code
+				// 2. ExprString might someday change incompatibly to
+				//    include struct tags, which can be arbitrary strings
+				if x.Type == nil {
+					// Can this happen without a parse error? (Files with parse
+					// errors are ignored in getSymbols)
+					continue // maybe report this someday
+				}
+				tp := types.ExprString(x.Type)
+				if len(tp) == 0 {
+					// Can this happen?
+					continue // maybe report this someday
+				}
+				// This is only safe if ExprString never returns anything with a $
+				// The only place a $ can occur seems to be in a struct tag, which
+				// can be an arbitrary string literal, and ExprString does not presently
+				// print struct tags. So for this to happen the type of a formal parameter
+				// has to be a explicit struct, e.g. foo(x struct{a int "$"}) and ExprString
+				// would have to show the struct tag. Even testing for this case seems
+				// a waste of effort, but let's remember the possibility
+				if strings.Contains(tp, "$") {
+					continue
+				}
+				tp = strings.Replace(tp, " ", "$", -1)
+				if len(x.Names) == 0 {
+					result = append(result, "_")
+					result = append(result, tp)
+				} else {
+					for _, y := range x.Names {
+						result = append(result, y.Name)
+						result = append(result, tp)
+					}
+				}
+			}
+			sigs := strings.Join(result, " ")
+			if s := newsym(pkg, name, kind, sigs); s != nil {
+				ans = append(ans, *s)
+			}
+		case *ast.GenDecl:
+			depr := isDeprecated(decl.Doc)
+			switch decl.Tok {
+			case token.CONST, token.VAR:
+				tp := "V"
+				if decl.Tok == token.CONST {
+					tp = "C"
+				}
+				if depr {
+					tp += "D"
+				}
+				for _, sp := range decl.Specs {
+					for _, x := range sp.(*ast.ValueSpec).Names {
+						if s := newsym(pkg, x.Name, tp, ""); s != nil {
+							ans = append(ans, *s)
+						}
+					}
+				}
+			case token.TYPE:
+				tp := "T"
+				if depr {
+					tp += "D"
+				}
+				for _, sp := range decl.Specs {
+					if s := newsym(pkg, sp.(*ast.TypeSpec).Name.Name, tp, ""); s != nil {
+						ans = append(ans, *s)
+					}
+				}
+			}
+		}
+	}
+	return ans
+}
+
+func newsym(pkg, name, kind, sig string) *symbol {
+	if len(name) == 0 || !ast.IsExported(name) {
+		return nil
+	}
+	sym := symbol{pkg: pkg, name: name, kind: kind, sig: sig}
+	return &sym
+}
+
+func isDeprecated(doc *ast.CommentGroup) bool {
+	if doc == nil {
+		return false
+	}
+	// go.dev/wiki/Deprecated Paragraph starting 'Deprecated:'
+	// This code fails for /* Deprecated: */, but it's the code from
+	// gopls/internal/analysis/deprecated
+	for line := range strings.SplitSeq(doc.Text(), "\n\n") {
+		if strings.HasPrefix(line, "Deprecated:") {
+			return true
+		}
+	}
+	return false
+}
+
+// return the package name and the value for the symbols.
+// if there are multiple packages, choose one arbitrarily
+// the returned slice is sorted lexicographically
+func processSyms(syms []symbol) (string, []string) {
+	if len(syms) == 0 {
+		return "", nil
+	}
+	slices.SortFunc(syms, func(l, r symbol) int {
+		return strings.Compare(l.name, r.name)
+	})
+	pkg := syms[0].pkg
+	var names []string
+	for _, s := range syms {
+		if s.pkg != pkg {
+			// Symbols came from two files in same dir
+			// with different package declarations.
+			continue
+		}
+		var nx string
+		if s.sig != "" {
+			nx = fmt.Sprintf("%s %s %s", s.name, s.kind, s.sig)
+		} else {
+			nx = fmt.Sprintf("%s %s", s.name, s.kind)
+		}
+		names = append(names, nx)
+	}
+	return pkg, names
+}
diff --git a/vendor/golang.org/x/tools/internal/stdlib/deps.go b/vendor/golang.org/x/tools/internal/stdlib/deps.go
index 0f73c719cd..b049e5f977 100644
--- a/vendor/golang.org/x/tools/internal/stdlib/deps.go
+++ b/vendor/golang.org/x/tools/internal/stdlib/deps.go
@@ -12,386 +12,387 @@ type pkginfo struct {
 }
 
 var deps = [...]pkginfo{
-	{"archive/tar", "\x03{\x03F>\x01\n\x01&\x01\x01\x02\x05\b\x02\x01\x02\x02\r"},
-	{"archive/zip", "\x02\x04j\x0e\x03\x12\x022>\x01-\x05\x01\x0f\x03\x02\x0f\x04"},
-	{"bufio", "\x03{\x87\x01F\x15"},
-	{"bytes", "~*]\x03\fJ\x02\x02"},
+	{"archive/tar", "\x03|\x03F>\x01\n\x01&\x01\x01\x02\x05\b\x02\x01\x02\x02\r"},
+	{"archive/zip", "\x02\x04k\x0e\x03\x12\x022>\x01-\x05\x01\x0f\x03\x02\x0f\x04"},
+	{"bufio", "\x03|\x87\x01F\x15"},
+	{"bytes", "\x7f*]\x03\fJ\x02\x02"},
 	{"cmp", ""},
-	{"compress/bzip2", "\x02\x02\x81\x02C"},
-	{"compress/flate", "\x02|\x03\x84\x01\f\x034\x02\x03"},
-	{"compress/gzip", "\x02\x04j\x0e\x03\x14pW"},
-	{"compress/lzw", "\x02|\x03\x84\x01"},
-	{"compress/zlib", "\x02\x04j\x0e\x03\x12\x01q"},
-	{"container/heap", "\xc9\x02"},
+	{"compress/bzip2", "\x02\x02\x82\x02C"},
+	{"compress/flate", "\x02}\x03\x84\x01\f\x034\x02\x03"},
+	{"compress/gzip", "\x02\x04k\x0e\x03\x14pW"},
+	{"compress/lzw", "\x02}\x03\x84\x01"},
+	{"compress/zlib", "\x02\x04k\x0e\x03\x12\x01q"},
+	{"container/heap", "\xca\x02"},
 	{"container/list", ""},
 	{"container/ring", ""},
-	{"context", "~]r\x01\x0e"},
-	{"crypto", "\x93\x01rE"},
-	{"crypto/aes", "\x10\v\n\xa5\x02"},
-	{"crypto/cipher", "\x03\"\x01\x01 \x13$+\\"},
-	{"crypto/des", "\x10\x17 7+\xa1\x01\x03"},
-	{"crypto/dsa", "G\x034\x87\x01\r"},
-	{"crypto/ecdh", "\x03\v\r\x11\x04\x17\x03\x10$\x87\x01"},
-	{"crypto/ecdsa", "\x0e\x05\x03\x05\x01\x11\b\v\x06\x01\x03\x0f\x01$\x87\x01\r\x05O\x01"},
-	{"crypto/ed25519", "\x0e \x12\a\x03\t\a$I>E"},
-	{"crypto/elliptic", "5I\x87\x01\r;"},
-	{"crypto/fips140", "$\x05\x9e\x01\x9b\x01"},
-	{"crypto/hkdf", "1\x15\x017\x15"},
-	{"crypto/hmac", "\x1b\x17\x14\x01\x139"},
-	{"crypto/hpke", "\x03\v\x02\x03\x04\x01\r\x01\x05\x1f\x06\a\x01\x01%\x03\x12\x16\x9f\x01\x1d"},
-	{"crypto/internal/boring", "\x0e\x02\x0eu"},
-	{"crypto/internal/boring/bbig", "\x1b\xf7\x01P"},
-	{"crypto/internal/boring/bcache", "\xce\x02\x14"},
+	{"context", "\x7f]r\x01\x0e"},
+	{"crypto", "\x94\x01rE"},
+	{"crypto/aes", "\x10\v\v\xa5\x02"},
+	{"crypto/cipher", "\x03#\x01\x01 \x13$+\\"},
+	{"crypto/des", "\x10\x18 7+\xa1\x01\x03"},
+	{"crypto/dsa", "H\x034\x87\x01\r"},
+	{"crypto/ecdh", "\x03\v\r\x12\x04\x17\x03\x10$\x87\x01"},
+	{"crypto/ecdsa", "\x0e\x05\x03\x05\x01\x12\b\v\x06\x01\x03\x0f\x01$\x87\x01\r\x05O\x01"},
+	{"crypto/ed25519", "\x0e!\x12\a\x03\t\a$I>E"},
+	{"crypto/elliptic", "6I\x87\x01\r;"},
+	{"crypto/fips140", "%\x05\x9e\x01\x9b\x01"},
+	{"crypto/hkdf", "2\x15\x017\x15"},
+	{"crypto/hmac", "\x1b\x18\x14\x01\x139"},
+	{"crypto/hpke", "\x03\v\x02\x03\x04\x01\x0e\x01\x05\x1f\x06\a\x01\x01%\x03\x12\x16\x9f\x01\x1d"},
+	{"crypto/internal/boring", "\x0e\x02\x0ev"},
+	{"crypto/internal/boring/bbig", "\x1b\xf8\x01P"},
+	{"crypto/internal/boring/bcache", "\xcf\x02\x14"},
 	{"crypto/internal/boring/sig", ""},
 	{"crypto/internal/constanttime", ""},
-	{"crypto/internal/cryptotest", "\x03\r\v\t%\x11\x1a\r\x12\x12!\x04\x06\n\x19\x01\x11\x11\x1d\x01\a\x03\x02\b\x02\x01\x05\f"},
-	{"crypto/internal/cryptotest/wycheproof", "\x0e\x12S\x01\f\x01r@\x05\x03\x15\x10"},
-	{"crypto/internal/entropy", "L"},
-	{"crypto/internal/entropy/v1.0.0", "E9\x96\x01:\x14"},
-	{"crypto/internal/fips140", "D:\xc2\x01\v\x17"},
-	{"crypto/internal/fips140/aes", "\x03!\x03\x02\x14\x05\x01\x01\x055\x96\x016"},
-	{"crypto/internal/fips140/aes/gcm", "$\x01\x02\x02\x02\x12\x05\x01\x065\x93\x01"},
-	{"crypto/internal/fips140/alias", "\xe2\x02"},
-	{"crypto/internal/fips140/bigmod", ")\x19\x01\x065\x96\x01"},
-	{"crypto/internal/fips140/check", "$\x0e\a\t\x02\xc1\x01]"},
-	{"crypto/internal/fips140/check/checktest", ")\x97\x02\""},
-	{"crypto/internal/fips140/drbg", "\x03 \x01\x01\x04\x14\x05\n2\x87\x01\x0f9\x01"},
-	{"crypto/internal/fips140/ecdh", "\x03!\x05\x02\n\r<\x87\x01\x0f9"},
-	{"crypto/internal/fips140/ecdsa", "\x03!\x04\x01\x02\a\x03\x06C\x15r\x0f9"},
-	{"crypto/internal/fips140/ed25519", "\x03!\x05\x02\x04\fC\xcc\x01\x03"},
-	{"crypto/internal/fips140/edwards25519", "\x1f\n\a\x12<\x96\x019"},
-	{"crypto/internal/fips140/edwards25519/field", ")\x14\x05<\x96\x01"},
-	{"crypto/internal/fips140/hkdf", "\x03!\x05\t\aE\x15"},
-	{"crypto/internal/fips140/hmac", "\x03!\x15\x01\x01C\x15"},
-	{"crypto/internal/fips140/mldsa", "\x03\x1c\x05\x05\x02\x0e\x01\x03\x05<\x96\x019"},
-	{"crypto/internal/fips140/mlkem", "\x03!\x05\x02\x0f\x03\x05<\xcf\x01"},
-	{"crypto/internal/fips140/nistec", "\x1f\n\r\f<\x96\x01,\r\x15"},
-	{"crypto/internal/fips140/nistec/fiat", ")\x14A\x96\x01"},
-	{"crypto/internal/fips140/pbkdf2", "\x03!\x05\t\aE\x15"},
-	{"crypto/internal/fips140/rsa", "\x03\x1c\x05\x04\x01\x02\x0e\x01\x01\x02A\x15rH"},
-	{"crypto/internal/fips140/sha256", "\x03!\x1e\x01\x065\x15\x81\x01"},
-	{"crypto/internal/fips140/sha3", "\x03!\x19\x05\x01;\x96\x01N"},
-	{"crypto/internal/fips140/sha512", "\x03!\x1e\x01\x065\x15\x81\x01"},
-	{"crypto/internal/fips140/ssh", ")j"},
-	{"crypto/internal/fips140/subtle", "\x1f\b\x1b\xd2\x01"},
-	{"crypto/internal/fips140/tls12", "\x03!\x05\t\a\x02C\x15"},
-	{"crypto/internal/fips140/tls13", "\x03!\x05\b\b\t<\x15"},
-	{"crypto/internal/fips140cache", "\xc0\x02\r."},
+	{"crypto/internal/cryptotest", "\x03\r\v\n%\x11\x1a\r\x12\x12!\x04\x06\n\x19\x01\x11\x11\x1d\x01\a\x03\x02\b\x02\x01\x05\f"},
+	{"crypto/internal/cryptotest/wycheproof", "\x0e\x12T\x01\f\x01r@\x05\x03\x15\x10"},
+	{"crypto/internal/cryptotest/x509limbo", "u\n\x03\xba\x01\x03\x1e"},
+	{"crypto/internal/entropy", "M"},
+	{"crypto/internal/entropy/v1.0.0", "F9\x96\x01:\x14"},
+	{"crypto/internal/fips140", "E:\xc2\x01\v\x17"},
+	{"crypto/internal/fips140/aes", "\x03\"\x03\x02\x14\x05\x01\x01\x055\x96\x016"},
+	{"crypto/internal/fips140/aes/gcm", "%\x01\x02\x02\x02\x12\x05\x01\x065\x93\x01"},
+	{"crypto/internal/fips140/alias", "\xe3\x02"},
+	{"crypto/internal/fips140/bigmod", "*\x19\x01\x065\x96\x01"},
+	{"crypto/internal/fips140/check", "%\x0e\a\t\x02\xc1\x01]"},
+	{"crypto/internal/fips140/check/checktest", "*\x97\x02\""},
+	{"crypto/internal/fips140/drbg", "\x03!\x01\x01\x04\x14\x05\n2\x87\x01\x0f9\x01"},
+	{"crypto/internal/fips140/ecdh", "\x03\"\x05\x02\n\r<\x87\x01\x0f9"},
+	{"crypto/internal/fips140/ecdsa", "\x03\"\x04\x01\x02\a\x03\x06C\x15r\x0f9"},
+	{"crypto/internal/fips140/ed25519", "\x03\"\x05\x02\x04\fC\xcc\x01\x03"},
+	{"crypto/internal/fips140/edwards25519", "\x1f\v\a\x12<\x96\x019"},
+	{"crypto/internal/fips140/edwards25519/field", "*\x14\x05<\x96\x01"},
+	{"crypto/internal/fips140/hkdf", "\x03\"\x05\t\aE\x15"},
+	{"crypto/internal/fips140/hmac", "\x03\"\x15\x01\x01C\x15"},
+	{"crypto/internal/fips140/mldsa", "\x03\x1c\x06\x05\x02\x0e\x01\x03\x05<\x96\x019"},
+	{"crypto/internal/fips140/mlkem", "\x03\"\x05\x02\x0f\x03\x05<\xcf\x01"},
+	{"crypto/internal/fips140/nistec", "\x1f\v\r\f<\x96\x01,\r\x15"},
+	{"crypto/internal/fips140/nistec/fiat", "*\x14A\x96\x01"},
+	{"crypto/internal/fips140/pbkdf2", "\x03\"\x05\t\aE\x15"},
+	{"crypto/internal/fips140/rsa", "\x03\x1c\x06\x04\x01\x02\x0e\x01\x01\x02A\x15rH"},
+	{"crypto/internal/fips140/sha256", "\x03\"\x1e\x01\x065\x15\x81\x01"},
+	{"crypto/internal/fips140/sha3", "\x03\"\x19\x05\x01;\x96\x01N"},
+	{"crypto/internal/fips140/sha512", "\x03\"\x1e\x01\x065\x15\x81\x01"},
+	{"crypto/internal/fips140/ssh", "*j"},
+	{"crypto/internal/fips140/subtle", "\x1f\t\x1b\xd2\x01"},
+	{"crypto/internal/fips140/tls12", "\x03\"\x05\t\a\x02C\x15"},
+	{"crypto/internal/fips140/tls13", "\x03\"\x05\b\b\t<\x15"},
+	{"crypto/internal/fips140cache", "\xc1\x02\r."},
 	{"crypto/internal/fips140deps", ""},
-	{"crypto/internal/fips140deps/byteorder", "\xa9\x01"},
-	{"crypto/internal/fips140deps/cpu", "\xbe\x01\b"},
-	{"crypto/internal/fips140deps/godebug", "\xc7\x01"},
-	{"crypto/internal/fips140deps/time", "\xe2\x02"},
-	{"crypto/internal/fips140hash", ":\x1e;\xcf\x01"},
-	{"crypto/internal/fips140only", "\x17\x14\x0e\x01\x01Xr"},
+	{"crypto/internal/fips140deps/byteorder", "\xaa\x01"},
+	{"crypto/internal/fips140deps/cpu", "\xbf\x01\b"},
+	{"crypto/internal/fips140deps/godebug", "\xc8\x01"},
+	{"crypto/internal/fips140deps/time", "\xe3\x02"},
+	{"crypto/internal/fips140hash", ";\x1e;\xcf\x01"},
+	{"crypto/internal/fips140only", "\x17\x15\x0e\x01\x01Xr"},
 	{"crypto/internal/fips140test", ""},
-	{"crypto/internal/impl", "\xcb\x02"},
-	{"crypto/internal/rand", "\x1b\x10 |>]"},
-	{"crypto/internal/randutil", "\x85\x02\x12"},
-	{"crypto/internal/sysrand", "~r!\"\r\r\x01\x01\r\x06"},
-	{"crypto/internal/sysrand/internal/seccomp", "~"},
-	{"crypto/md5", "\x0e97\x15\x16k"},
-	{"crypto/mldsa", "\x0e%K\x87\x01"},
-	{"crypto/mlkem", "\x0e&"},
-	{"crypto/mlkem/mlkemtest", "4\x13\t."},
-	{"crypto/pbkdf2", "7\x0f\x017\x15"},
-	{"crypto/rand", "\x1b\x10\x1c\x034\x87\x01\rP"},
-	{"crypto/rc4", "' 7\xcc\x01"},
-	{"crypto/rsa", "\x0e\r\x01\f\x10\x0e\x01\x03\t\a$\x03\x124>\f\x01"},
-	{"crypto/sha1", "\x0e\r,\x025\x15\x16\x15V"},
-	{"crypto/sha256", "\x0e\r\x1eZ"},
-	{"crypto/sha3", "\x0e,Y\xcf\x01"},
-	{"crypto/sha512", "\x0e\r X"},
-	{"crypto/subtle", "\x1f\x1e\xa9\x01|"},
-	{"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\n\x01\x18\x01\x0f\x01\x01\x03\x01\x01\x01\x01\x02\x01\x02\x01\x1f\x02\x03\x12\x16\x15\t>\x16\x18\r\b\x01\x01\x01\x02\x01\x0e\x06\x03\x01\x15"},
-	{"crypto/tls/internal/fips140tls", "\x17\xb7\x02"},
-	{"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x01\x017\x01\x06\x01\x01\x02\x05\x0f\x06\t\x02\x03F\x03;\x01\x02\b\x01\x01\x02\a\x12\x05\x01\x06\a\b\x02\x01\x02\x0f\x02\x01\x01\x02\x04\x01"},
-	{"crypto/x509/pkix", "m\x06\x0e\x91\x019\x11"},
-	{"database/sql", "\x03\nS\x01\x1d\x03\x84\x01\v\a$\x05\b\x02\x03\x01\x0e\x02\x02\x02\x01"},
-	{"database/sql/driver", "\rT\x1d\x03\xba\x01\x0f\x12\a"},
+	{"crypto/internal/impl", "\xcc\x02"},
+	{"crypto/internal/rand", "\x1b\x11 |>]"},
+	{"crypto/internal/randutil", "\x86\x02\x12"},
+	{"crypto/internal/sysrand", "\x7fr!\"\r\r\x01\x01\r\x06"},
+	{"crypto/internal/sysrand/internal/seccomp", "\x7f"},
+	{"crypto/md5", "\x0e:7\x15\x16k"},
+	{"crypto/mldsa", "\x0e&K\x87\x01"},
+	{"crypto/mlkem", "\x0e'"},
+	{"crypto/mlkem/mlkemtest", "5\x13\t."},
+	{"crypto/pbkdf2", "8\x0f\x017\x15"},
+	{"crypto/rand", "\x1b\x11\x1c\x034\x87\x01\rP"},
+	{"crypto/rc4", "( 7\xcc\x01"},
+	{"crypto/rsa", "\x0e\r\x01\r\x10\x0e\x01\x03\t\a$\x03\x124>\f\x01"},
+	{"crypto/sha1", "\x0e\r-\x025\x15\x16\x15V"},
+	{"crypto/sha256", "\x0e\r\x1fZ"},
+	{"crypto/sha3", "\x0e-Y\xcf\x01"},
+	{"crypto/sha512", "\x0e\r!X"},
+	{"crypto/subtle", "\x1f\x1f\xa9\x01|"},
+	{"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\v\x01\x18\x01\x0f\x01\x01\x03\x01\x01\x01\x01\x02\x01\x02\x01\x1f\x02\x03\x12\x16\x15\t>\x16\x18\r\b\x01\x01\x01\x02\x01\x0e\x06\x03\x01\x15"},
+	{"crypto/tls/internal/fips140tls", "\x17\xb8\x02"},
+	{"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x01\x018\x01\x06\x01\x01\x02\x05\x0f\x06\t\x02\x03F\x03;\x01\x02\b\x01\x01\x02\a\x12\x05\x01\x06\a\b\x02\x01\x02\x0f\x02\x01\x01\x02\x04\x01"},
+	{"crypto/x509/pkix", "n\x06\x0e\x91\x019\x11"},
+	{"database/sql", "\x03\nT\x01\x1d\x03\x84\x01\v\a$\x05\b\x02\x03\x01\x0e\x02\x02\x02\x01"},
+	{"database/sql/driver", "\rU\x1d\x03\xba\x01\x0f\x12\a"},
 	{"database/sql/internal", ""},
-	{"debug/buildinfo", "\x03a\x02\x01\x01\b\x0e\x03h\x1a\x02\x01-\x0f "},
-	{"debug/dwarf", "\x03m\x0e\x03\x84\x013\x11\x01\x01"},
-	{"debug/elf", "\x03\x06Z\r\x0e\x03h\x1b\x01\f\"\x17\x01\x17"},
-	{"debug/gosym", "\x03m\x11#\xa5\x01\x01\x01\x02"},
-	{"debug/macho", "\x03\x06Z\r\x11h\x1c.\x17\x01"},
-	{"debug/pe", "\x03\x06Z\r\x0e\x03h\x1c.\x17\x01\x17"},
-	{"debug/plan9obj", "p\x0e\x03h\x1c."},
-	{"embed", "~*D\x19\x01V"},
+	{"debug/buildinfo", "\x03b\x02\x01\x01\b\x0e\x03h\x1a\x02\x01-\x0f "},
+	{"debug/dwarf", "\x03n\x0e\x03\x84\x013\x11\x01\x01"},
+	{"debug/elf", "\x03\x06[\r\x0e\x03h\x1b\x01\f\"\x17\x01\x17"},
+	{"debug/gosym", "\x03n\x11#\xa5\x01\x01\x01\x02"},
+	{"debug/macho", "\x03\x06[\r\x11h\x1c.\x17\x01"},
+	{"debug/pe", "\x03\x06[\r\x0e\x03h\x1c.\x17\x01\x17"},
+	{"debug/plan9obj", "q\x0e\x03h\x1c."},
+	{"embed", "\x7f*D\x19\x01V"},
 	{"embed/internal/embedtest", ""},
 	{"encoding", ""},
-	{"encoding/ascii85", "\x85\x02E"},
-	{"encoding/asn1", "\x03{\x03h(\x01)\r\x02\x01\x11\x03\x01"},
-	{"encoding/base32", "\x85\x02C\x02"},
-	{"encoding/base64", "\xa9\x01\\C\x02"},
-	{"encoding/binary", "~\x87\x01\f*\r\x05"},
-	{"encoding/csv", "\x02\x01{\x03\x84\x01F\x13\x02"},
-	{"encoding/gob", "\x02i\x05\x0e\x03h\x1c\v\x01\x03\x1f\b\x12\x01\x10\x02"},
-	{"encoding/hex", "~\x03\x84\x01C\x03"},
-	{"encoding/json", "\x03\x01g\n\x01\x01\x02\x01\x01\x03\x03\x84\x016\x0f\x01"},
-	{"encoding/json/internal", "~"},
-	{"encoding/json/internal/jsonflags", "u"},
-	{"encoding/json/internal/jsonopts", "u\x01"},
-	{"encoding/json/internal/jsontest", "\x03f\x15\x03\x83\x01\x01\x012\b\b\x03\x02\x0f"},
-	{"encoding/json/internal/jsonwire", "\x04r\b\x87\x01\f7\x02\x01\x13\x01\x01"},
-	{"encoding/json/jsontext", "\x03r\x01\x01\x02\x05\x87\x01\x03\t\x034\x02\x01\x02\x13"},
-	{"encoding/json/v2", "\x03\x01g\x03\x01\x01\x03\x02\x01\x01\x02\x01\x04\x03\x84\x01\f\x03'\r\x02\x01\x02\x0f\x02\x02"},
-	{"encoding/pem", "\x03l\x0f\x87\x01C\x03"},
-	{"encoding/xml", "\x02\x01h\x13\x03\x84\x016\x05\n\x01\x02\x11\x02"},
-	{"errors", "\xdb\x01\x87\x01"},
-	{"expvar", "tSB\b\v\x17\r\b\x02\x03\x01\x12"},
-	{"flag", "k\x13\x03\x84\x01.\b\x05\b\x02\x01\x11"},
-	{"fmt", "~E)\x19\f\"\b\r\x02\x03\x13"},
-	{"go/ast", "\x03\x01}\x0e\x01u\x03+\b\r\x02\x01\x13\x02"},
-	{"go/build", "\x02\x01{\x03\x01\x02\x02\a\x02\x01\x17 \x04\x02\t\x1c\x13\x01-\x01\x04\x01\a\b\x02\x01\x13\x02\x02"},
-	{"go/build/constraint", "~\xcc\x01\x01\x13\x02"},
-	{"go/constant", "\x81\x01\x0f\x81\x01\x01\x026\x01\x02\x13"},
-	{"go/doc", "\x04}\x01\x05\t>73\x10\x02\x01\x13\x02"},
-	{"go/doc/comment", "\x03~\xc7\x01\x01\x01\x01\x13\x02"},
-	{"go/format", "\x03~\x01\v\x01\x02uF"},
-	{"go/importer", "\x83\x01\a\x01\x01\x04\x01t;"},
-	{"go/internal/gccgoimporter", "\x02\x01a\x1a\x03\x04\v\x01r\x02.\x01\x05\x11\x01\r\b"},
-	{"go/internal/gcimporter", "\x02\x7f\x0f\x010\x140.\x15\x03\x02"},
-	{"go/internal/srcimporter", "\x81\x01\x01\x01\n\x03\x01t.\x01\x05\x12\x02\x15"},
-	{"go/parser", "\x03{\x03\x01\x02\v\x01u\x01-\x06\x12"},
-	{"go/printer", "\x81\x01\x01\x02\x03\tu\f\"\x15\x02\x01\x02\f\x05\x02"},
-	{"go/scanner", "\x03~\x0fu4\x10\x01\x14\x02"},
-	{"go/token", "\x04}\x87\x01@\x02\x03\x01\x10\x02"},
-	{"go/types", "\x03\x01\x06t\x03\x01\x03\b\x03\x02\x0654\x04\x03\t\"\x06\a\b\x01\x01\x01\x02\x01\x10\x02\x02"},
-	{"go/version", "\xcc\x01\x7f"},
-	{"hash", "\x85\x02"},
-	{"hash/adler32", "~\x15\x16"},
-	{"hash/crc32", "~\x15\x16\x15\x8f\x01\x01\x14"},
-	{"hash/crc64", "~\x15\x16\xa4\x01"},
-	{"hash/fnv", "~\x15\x16k"},
-	{"hash/maphash", "\x93\x01\x11>\x80\x01"},
-	{"html", "\xcb\x02\x02\x13"},
-	{"html/template", "\x03q\r\x18.>\x01\n#\x05\x01\x02\x03\n\x02\x01\x02\r\x01\x03\x02"},
-	{"image", "\x02|\x1ei\x0f6\x03\x01"},
+	{"encoding/ascii85", "\x86\x02E"},
+	{"encoding/asn1", "\x03|\x03h(\x01)\r\x02\x01\x11\x03\x01"},
+	{"encoding/base32", "\x86\x02C\x02"},
+	{"encoding/base64", "\xaa\x01\\C\x02"},
+	{"encoding/binary", "\x7f\x87\x01\f*\r\x05"},
+	{"encoding/csv", "\x02\x01|\x03\x84\x01F\x13\x02"},
+	{"encoding/gob", "\x02j\x05\x0e\x03h\x1c\v\x01\x03\x1f\b\x12\x01\x10\x02"},
+	{"encoding/hex", "\x7f\x03\x84\x01C\x03"},
+	{"encoding/json", "\x03\x01h\n\x01\x01\x02\x01\x01\x03\x03\x84\x016\x0f\x01"},
+	{"encoding/json/internal", "\x7f"},
+	{"encoding/json/internal/jsonflags", "v"},
+	{"encoding/json/internal/jsonopts", "v\x01"},
+	{"encoding/json/internal/jsontest", "\x03g\x15\x03\x83\x01\x01\x012\b\b\x03\x02\x0f"},
+	{"encoding/json/internal/jsonwire", "\x04s\b\x87\x01\f7\x02\x01\x13\x01\x01"},
+	{"encoding/json/jsontext", "\x03s\x01\x01\x02\x05\x87\x01\x03\t\x034\x02\x01\x02\x13"},
+	{"encoding/json/v2", "\x03\x01h\x03\x01\x01\x03\x02\x01\x01\x02\x01\x04\x03\x84\x01\f\x03'\r\x02\x01\x02\x0f\x02\x02"},
+	{"encoding/pem", "\x03m\x0f\x87\x01C\x03"},
+	{"encoding/xml", "\x02\x01i\x13\x03\x84\x016\x05\n\x01\x02\x11\x02"},
+	{"errors", "\xdc\x01\x87\x01"},
+	{"expvar", "uSB\b\v\x17\r\b\x02\x03\x01\x12"},
+	{"flag", "l\x13\x03\x84\x01.\b\x05\b\x02\x01\x11"},
+	{"fmt", "\x7fE)\x19\f\"\b\r\x02\x03\x13"},
+	{"go/ast", "\x03\x01~\x0e\x01u\x03+\b\r\x02\x01\x13\x02"},
+	{"go/build", "\x02\x01|\x03\x01\x02\x02\a\x02\x01\x17 \x04\x02\t\x1c\x13\x01-\x01\x04\x01\a\b\x02\x01\x13\x02\x02"},
+	{"go/build/constraint", "\x7f\xcc\x01\x01\x13\x02"},
+	{"go/constant", "\x82\x01\x0f\x81\x01\x01\x026\x01\x02\x13"},
+	{"go/doc", "\x04~\x01\x05\t>73\x10\x02\x01\x13\x02"},
+	{"go/doc/comment", "\x03\x7f\xc7\x01\x01\x01\x01\x13\x02"},
+	{"go/format", "\x03\x7f\x01\v\x01\x02uF"},
+	{"go/importer", "\x84\x01\a\x01\x01\x04\x01t;"},
+	{"go/internal/gccgoimporter", "\x02\x01b\x1a\x03\x04\v\x01r\x02.\x01\x05\x11\x01\r\b"},
+	{"go/internal/gcimporter", "\x02\x80\x01\x0f\x010\x140.\x15\x03\x02"},
+	{"go/internal/srcimporter", "\x82\x01\x01\x01\n\x03\x01t.\x01\x05\x12\x02\x15"},
+	{"go/parser", "\x03|\x03\x01\x02\v\x01u\x01-\x06\x12"},
+	{"go/printer", "\x82\x01\x01\x02\x03\tu\f\"\x15\x02\x01\x02\f\x05\x02"},
+	{"go/scanner", "\x03\x7f\x0fu4\x10\x01\x14\x02"},
+	{"go/token", "\x04~\x87\x01@\x02\x03\x01\x10\x02"},
+	{"go/types", "\x03\x01\x06u\x03\x01\x03\b\x03\x02\x0654\x04\x03\t\"\x06\a\b\x01\x01\x01\x02\x01\x10\x02\x02"},
+	{"go/version", "\xcd\x01\x7f"},
+	{"hash", "\x86\x02"},
+	{"hash/adler32", "\x7f\x15\x16"},
+	{"hash/crc32", "\x7f\x15\x16\x15\x8f\x01\x01\x14"},
+	{"hash/crc64", "\x7f\x15\x16\xa4\x01"},
+	{"hash/fnv", "\x7f\x15\x16k"},
+	{"hash/maphash", "\x94\x01\x11>\x80\x01"},
+	{"html", "\xcc\x02\x02\x13"},
+	{"html/template", "\x03r\r\x18.>\x01\n#\x05\x01\x02\x03\n\x02\x01\x02\r\x01\x03\x02"},
+	{"image", "\x02}\x1ei\x0f6\x03\x01"},
 	{"image/color", ""},
-	{"image/color/palette", "\x9c\x01"},
-	{"image/draw", "\x9b\x01\x01\x04"},
-	{"image/gif", "\x02\x01\x05v\x03\x1a\x01\x01\x01\v\\\x0f"},
-	{"image/internal/imageutil", "\x9b\x01"},
-	{"image/jpeg", "\x02|\x1d\x01\x04e"},
-	{"image/png", "\x02\ag\x11\x12\x02\x06\x01iE"},
-	{"index/suffixarray", "\x03m\x0e\x87\x01\f-\n\x01"},
-	{"internal/abi", "\xc6\x01\x9c\x01"},
-	{"internal/asan", "\xe2\x02"},
-	{"internal/bisect", "\xc0\x02\r\x01"},
-	{"internal/buildcfg", "\x81\x01Hj\x06\x02\x05\n\x01"},
-	{"internal/bytealg", "\xbe\x01\xa4\x01"},
+	{"image/color/palette", "\x9d\x01"},
+	{"image/draw", "\x9c\x01\x01\x04"},
+	{"image/gif", "\x02\x01\x05w\x03\x1a\x01\x01\x01\v\\\x0f"},
+	{"image/internal/imageutil", "\x9c\x01"},
+	{"image/jpeg", "\x02}\x1d\x01\x04e"},
+	{"image/png", "\x02\ah\x11\x12\x02\x06\x01iE"},
+	{"index/suffixarray", "\x03n\x0e\x87\x01\f-\n\x01"},
+	{"internal/abi", "\xc7\x01\x9c\x01"},
+	{"internal/asan", "\xe3\x02"},
+	{"internal/bisect", "\xc1\x02\r\x01"},
+	{"internal/buildcfg", "\x82\x01Hj\x06\x02\x05\n\x01"},
+	{"internal/bytealg", "\xbf\x01\xa4\x01"},
 	{"internal/byteorder", ""},
 	{"internal/cfg", ""},
-	{"internal/cgrouptest", "\x81\x01\\V\x06\x0f\x02\x01\x04\x01"},
-	{"internal/chacha8rand", "\xa9\x01\x15\b\x9c\x01"},
+	{"internal/cgrouptest", "\x82\x01\\V\x06\x0f\x02\x01\x04\x01"},
+	{"internal/chacha8rand", "\xaa\x01\x15\b\x9c\x01"},
 	{"internal/copyright", ""},
 	{"internal/coverage", ""},
 	{"internal/coverage/calloc", ""},
-	{"internal/coverage/cfile", "t\r\x16\x17\x01\x02\x01\x01\x01\x01\x01\x01\x01$\x02'.\x06\a\n\x01\x03\x0e\x06"},
-	{"internal/coverage/cformat", "\x04}-\x04S\v8\x01\x02\x0e"},
-	{"internal/coverage/cmerge", "\x81\x01-c"},
-	{"internal/coverage/decodecounter", "p\x11-\v\x02J.\x17\x18"},
-	{"internal/coverage/decodemeta", "\x02n\x11\x16\x17\v\x02J."},
-	{"internal/coverage/encodecounter", "\x02n\x11-\f\x01\x02H\v#\x15"},
-	{"internal/coverage/encodemeta", "\x02\x01m\x11\x12\x04\x17\r\x02H./"},
-	{"internal/coverage/pods", "\x04}-\x85\x01\x06\x05\n\x02\x01"},
-	{"internal/coverage/rtcov", "\xe2\x02"},
-	{"internal/coverage/slicereader", "p\x11\x84\x01]"},
-	{"internal/coverage/slicewriter", "\x81\x01\x84\x01"},
-	{"internal/coverage/stringtab", "\x81\x018\x04H"},
+	{"internal/coverage/cfile", "u\r\x16\x17\x01\x02\x01\x01\x01\x01\x01\x01\x01$\x02'.\x06\a\n\x01\x03\x0e\x06"},
+	{"internal/coverage/cformat", "\x04~-\x04S\v8\x01\x02\x0e"},
+	{"internal/coverage/cmerge", "\x82\x01-c"},
+	{"internal/coverage/decodecounter", "q\x11-\v\x02J.\x17\x18"},
+	{"internal/coverage/decodemeta", "\x02o\x11\x16\x17\v\x02J."},
+	{"internal/coverage/encodecounter", "\x02o\x11-\f\x01\x02H\v#\x15"},
+	{"internal/coverage/encodemeta", "\x02\x01n\x11\x12\x04\x17\r\x02H./"},
+	{"internal/coverage/pods", "\x04~-\x85\x01\x06\x05\n\x02\x01"},
+	{"internal/coverage/rtcov", "\xe3\x02"},
+	{"internal/coverage/slicereader", "q\x11\x84\x01]"},
+	{"internal/coverage/slicewriter", "\x82\x01\x84\x01"},
+	{"internal/coverage/stringtab", "\x82\x018\x04H"},
 	{"internal/coverage/test", ""},
 	{"internal/coverage/uleb128", ""},
-	{"internal/cpu", "\xe2\x02"},
-	{"internal/dag", "\x04}\xc7\x01\x03"},
-	{"internal/diff", "\x03~\xc8\x01\x02"},
-	{"internal/exportdata", "\x02\x01{\x03\x02f\x1c.\x01\x05\x11\x01\x02"},
-	{"internal/filepathlite", "~*D\x1aB"},
-	{"internal/fmtsort", "\x04\xb7\x02\r"},
-	{"internal/fuzz", "\x03\nJ\x19\x04\n\x03\x01\v\x037>\f\x03\x1f\x01\x05\x02\x05\n\x01\x02\x01\x01\r\x04\x02"},
+	{"internal/cpu", "\xe3\x02"},
+	{"internal/dag", "\x04~\xc7\x01\x03"},
+	{"internal/diff", "\x03\x7f\xc8\x01\x02"},
+	{"internal/exportdata", "\x02\x01|\x03\x02f\x1c.\x01\x05\x11\x01\x02"},
+	{"internal/filepathlite", "\x7f*D\x1aB"},
+	{"internal/fmtsort", "\x04\xb8\x02\r"},
+	{"internal/fuzz", "\x03\nK\x19\x04\n\x03\x01\v\x037>\f\x03\x1f\x01\x05\x02\x05\n\x01\x02\x01\x01\r\x04\x02"},
 	{"internal/gate", "\r"},
 	{"internal/goarch", ""},
-	{"internal/godebug", "\xa6\x01\"\x85\x01\x01\x14"},
+	{"internal/godebug", "\xa7\x01\"\x85\x01\x01\x14"},
 	{"internal/godebugs", ""},
 	{"internal/goexperiment", ""},
 	{"internal/goos", ""},
-	{"internal/goroot", "\xb3\x02\x01\x05\x12\x02"},
+	{"internal/goroot", "\xb4\x02\x01\x05\x12\x02"},
 	{"internal/gover", "\x04"},
 	{"internal/goversion", ""},
-	{"internal/lazyregexp", "\xb3\x02\v\r\x02"},
-	{"internal/lazytemplate", "\x85\x02.\x18\x02\r"},
-	{"internal/msan", "\xe2\x02"},
-	{"internal/nettest", "\x03\nqG@\f\n\x12\x06\x15\x05\x0f"},
+	{"internal/lazyregexp", "\xb4\x02\v\r\x02"},
+	{"internal/lazytemplate", "\x86\x02.\x18\x02\r"},
+	{"internal/msan", "\xe3\x02"},
+	{"internal/nettest", "\x03\nrG@\f\n\x12\x06\x15\x05\x0f"},
 	{"internal/nettrace", ""},
-	{"internal/obscuretestdata", "o\x96\x01."},
-	{"internal/oserror", "~"},
-	{"internal/pkgbits", "\x03T\x19\x0e\x03\x04\vu\r!\r\n\x01"},
+	{"internal/obscuretestdata", "p\x96\x01."},
+	{"internal/oserror", "\x7f"},
+	{"internal/pkgbits", "\x03U\x19\x0e\x03\x04\vu\r!\r\n\x01"},
 	{"internal/platform", ""},
-	{"internal/poll", "~m\x05\x15;\r\x01\x01\r\x06"},
-	{"internal/profile", "\x03\x04w\x03\x84\x019\n\x01\x01\x01\x11"},
+	{"internal/poll", "\x7fm\x05\x15;\r\x01\x01\r\x06"},
+	{"internal/profile", "\x03\x04x\x03\x84\x019\n\x01\x01\x01\x11"},
 	{"internal/profilerecord", ""},
-	{"internal/race", "\xa4\x01\xbe\x01"},
-	{"internal/reflectlite", "\xa4\x01\"<>\""},
-	{"internal/runtime/atomic", "\xc6\x01\x9c\x01"},
-	{"internal/runtime/cgroup", "\xa8\x01?\x04w"},
-	{"internal/runtime/exithook", "\xdc\x01\x86\x01"},
-	{"internal/runtime/gc", "\xc6\x01"},
-	{"internal/runtime/gc/internal/gen", "\nf\x11\x17m\x04\v\x1f\b\x10\x02"},
-	{"internal/runtime/gc/scan", "\xbe\x01\b\x19\a|"},
-	{"internal/runtime/maps", "\xa4\x01\x01\x04\x15\b\x03\a\n\t\x03.N"},
-	{"internal/runtime/math", "\xc6\x01"},
+	{"internal/race", "\xa5\x01\xbe\x01"},
+	{"internal/reflectlite", "\xa5\x01\"<>\""},
+	{"internal/runtime/atomic", "\xc7\x01\x9c\x01"},
+	{"internal/runtime/cgroup", "\xa9\x01?\x04w"},
+	{"internal/runtime/exithook", "\xdd\x01\x86\x01"},
+	{"internal/runtime/gc", "\xc7\x01"},
+	{"internal/runtime/gc/internal/gen", "\ng\x11\x17m\x04\v\x1f\b\x10\x02"},
+	{"internal/runtime/gc/scan", "\xbf\x01\b\x19\a|"},
+	{"internal/runtime/maps", "\xa5\x01\x01\x04\x15\b\x03\a\n\t\x03.N"},
+	{"internal/runtime/math", "\xc7\x01"},
 	{"internal/runtime/pprof/label", ""},
 	{"internal/runtime/startlinetest", ""},
-	{"internal/runtime/sys", "\xc6\x01\x04"},
-	{"internal/runtime/syscall/linux", "\xc6\x01\x9c\x01"},
+	{"internal/runtime/sys", "\xc7\x01\x04"},
+	{"internal/runtime/syscall/linux", "\xc7\x01\x9c\x01"},
 	{"internal/runtime/wasitest", ""},
-	{"internal/saferio", "\x85\x02]"},
-	{"internal/singleflight", "\xcd\x02"},
-	{"internal/strconv", "\x94\x02N"},
-	{"internal/stringslite", "\xa8\x01\xba\x01"},
-	{"internal/sync", "\xa4\x01\"\x14t\x14"},
-	{"internal/synctest", "\xa4\x01\xbe\x01"},
-	{"internal/syscall/execenv", "\xcf\x02"},
-	{"internal/syscall/unix", "\xeb\x01U\x0e\x01\x13"},
-	{"internal/sysinfo", "\x02\x01\xbb\x01G.\x18\x02"},
+	{"internal/saferio", "\x86\x02]"},
+	{"internal/singleflight", "\xce\x02"},
+	{"internal/strconv", "\x95\x02N"},
+	{"internal/stringslite", "\xa9\x01\xba\x01"},
+	{"internal/sync", "\xa5\x01\"\x14t\x14"},
+	{"internal/synctest", "\xa5\x01\xbe\x01"},
+	{"internal/syscall/execenv", "\xd0\x02"},
+	{"internal/syscall/unix", "\xec\x01U\x0e\x01\x13"},
+	{"internal/sysinfo", "\x02\x01\xbc\x01G.\x18\x02"},
 	{"internal/syslist", ""},
-	{"internal/testenv", "\x03\nq\x02\x01)\x1c\x100-\x01\x05\a\n\x01\x02\x02\x01\f"},
-	{"internal/testhash", "\x03\x90\x01r\x11:\f"},
-	{"internal/testlog", "\xcd\x02\x01\x14"},
-	{"internal/testpty", "~\x03\xb2\x01"},
-	{"internal/trace", "\x02\x01\x01\x06f\x0e\x03x\x03\x03\x06\x03\t-\n\x01\x01\x01\x11\x06"},
-	{"internal/trace/internal/testgen", "\x03m\x11v\x03\x02\x03\x013\v\r\x11"},
-	{"internal/trace/internal/tracev1", "\x03\x01l\x0e\x03~\x06\f7\x01"},
-	{"internal/trace/raw", "\x02n\x11{\x03\x06E\x01\x13"},
-	{"internal/trace/testtrace", "\x02\x01{\x03r\x04\x03\x05\x01\x05.\v\x02\b\x02\x01\x05"},
+	{"internal/testenv", "\x03\nr\x02\x01)\x1c\x100-\x01\x05\a\n\x01\x02\x02\x01\f"},
+	{"internal/testhash", "\x03\x91\x01r\x11:\f"},
+	{"internal/testlog", "\xce\x02\x01\x14"},
+	{"internal/testpty", "\x7f\x03\xb2\x01"},
+	{"internal/trace", "\x02\x01\x01\x06g\x0e\x03x\x03\x03\x06\x03\t-\n\x01\x01\x01\x11\x06"},
+	{"internal/trace/internal/testgen", "\x03n\x11v\x03\x02\x03\x013\v\r\x11"},
+	{"internal/trace/internal/tracev1", "\x03\x01m\x0e\x03~\x06\f7\x01"},
+	{"internal/trace/raw", "\x02o\x11{\x03\x06E\x01\x13"},
+	{"internal/trace/testtrace", "\x02\x01|\x03r\x04\x03\x05\x01\x05.\v\x02\b\x02\x01\x05"},
 	{"internal/trace/tracev2", ""},
-	{"internal/trace/traceviewer", "\x02g\v\r\x19>\x1f\a\a\x04\b\v\x17\x01\x05\a\n\x01\x02\x0f"},
+	{"internal/trace/traceviewer", "\x02h\v\r\x19>\x1f\a\a\x04\b\v\x17\x01\x05\a\n\x01\x02\x0f"},
 	{"internal/trace/traceviewer/format", ""},
-	{"internal/trace/version", "\x81\x01{\t"},
-	{"internal/txtar", "\x03~\xb2\x01\x18"},
-	{"internal/types/errors", "\xca\x02"},
-	{"internal/unsafeheader", "\xe2\x02"},
-	{"internal/xcoff", "c\r\x0e\x03h\x1c.\x17\x01"},
-	{"internal/zstd", "p\x0e\x03\x84\x01\x0f"},
-	{"io", "~\xcf\x01"},
-	{"io/fs", "~*,13\x10\x14\x04"},
-	{"io/ioutil", "\x85\x02\x01-\x15\x03"},
-	{"iter", "\xda\x01f\""},
-	{"log", "\x81\x01\x84\x01\x05)\r\r\x01\x0e"},
+	{"internal/trace/version", "\x82\x01{\t"},
+	{"internal/txtar", "\x03\x7f\xb2\x01\x18"},
+	{"internal/types/errors", "\xcb\x02"},
+	{"internal/unsafeheader", "\xe3\x02"},
+	{"internal/xcoff", "d\r\x0e\x03h\x1c.\x17\x01"},
+	{"internal/zstd", "q\x0e\x03\x84\x01\x0f"},
+	{"io", "\x7f\xcf\x01"},
+	{"io/fs", "\x7f*,13\x10\x14\x04"},
+	{"io/ioutil", "\x86\x02\x01-\x15\x03"},
+	{"iter", "\xdb\x01f\""},
+	{"log", "\x82\x01\x84\x01\x05)\r\r\x01\x0e"},
 	{"log/internal", ""},
-	{"log/slog", "\x03\n^\t\n\x03H<\x04\x01\x02\x02\x03*\x05\b\x02\x01\x02\x01\x0e\x02\x02\x02"},
+	{"log/slog", "\x03\n_\t\n\x03H<\x04\x01\x02\x02\x03*\x05\b\x02\x01\x02\x01\x0e\x02\x02\x02"},
 	{"log/slog/internal", ""},
-	{"log/slog/internal/benchmarks", "\rq\x03\x84\x01\x06\x03<\x12"},
-	{"log/slog/internal/buffer", "\xcd\x02"},
-	{"log/syslog", "~\x03\x88\x01\x12\x18\x18\x02\x0f"},
-	{"maps", "\x88\x02Z"},
-	{"math", "\xbe\x01VN"},
-	{"math/big", "\x03{\x03(\x15G\f\x03\x022\x02\x01\x02\x15"},
-	{"math/big/internal/asmgen", "\x03\x01}\x93\x014\x03"},
-	{"math/bits", "\xe2\x02"},
-	{"math/cmplx", "\x91\x02\x03"},
-	{"math/rand", "\xc7\x01J<\x01\x14"},
-	{"math/rand/v2", "~+\x03e\x03N"},
-	{"mime", "\x02\x01l\x0f\x03\x84\x01\v#\x15\x03\x02\x11\x02"},
-	{"mime/multipart", "\x02\x01P+\x03F>\v\x01\a\x02\x17\x02\x06\x0f\x02\x01\x17"},
-	{"mime/quotedprintable", "\x02\x01~\x84\x01"},
-	{"net", "\x04\tq*\x1f\v\x05\x13\x01\x01\x04\x15\x01'\x06\r\b\x05\x01\x01\r\x06\t"},
-	{"net/http", "\x02\x01\x03\x01\x04\x02N\x14\x0f\x03F>\x01\x03\a\x01\x06\x01\x01\x02\x06\x02\x01\x01\f\x01\x01\x05\x01\x02\x05\b\x01\x01\x01\x02\x01\x0e\x02\x02\x02\n\x01\x03"},
-	{"net/http/cgi", "\x02Y#\x03\x84\x01\x04\a\v\x01\x15\x01\x01\x01\x04\x01\x05\x02\b\x02\x01\x11\x10"},
-	{"net/http/cookiejar", "\x04z\x03\x9a\x01\x01\b\t\x05\x16\x03\x02\x0f\x04"},
-	{"net/http/fcgi", "\x02\x01\nc\x0e\x03\x84\x01\x16\x01\x01\x16\x18\x02\x0f"},
-	{"net/http/httptest", "\x02\x01\nN\x02#\x01P4\x04\x12\x01\f\t\x02\r\n\x01\x02\x03\f\x06\n"},
-	{"net/http/httptrace", "\rNwI\x16+"},
-	{"net/http/httputil", "\x02\x01\nq\x03F>\x04\x0f\x03\x01\x05\x02\x01\r\x01\x19\x02\x01\x0e\x10"},
-	{"net/http/internal", "\x02\x01m\x0e\x03\x84\x01"},
-	{"net/http/internal/ascii", "\xcb\x02\x13"},
-	{"net/http/internal/http2", "\x02\x01\x03\x01\x06F\b\x15\x0e\x03\x84\x01\x01\x03\b\x03\x02\x03\x02\x06\x02\x03\x01\n\x01\x01\b\x05\b\x02\x01\x02\x01\x0e\x10\x02\x02"},
-	{"net/http/internal/httpcommon", "\rq\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"},
-	{"net/http/internal/httpsfv", "\xc8\x02\x02\x01\x11\x04"},
-	{"net/http/internal/testcert", "\xcb\x02"},
-	{"net/http/pprof", "\x02\x01\nt\x18.\x11-\x04\x13\x16\x01\r\x04\x03\x01\x02\x01\x11"},
+	{"log/slog/internal/benchmarks", "\rr\x03\x84\x01\x06\x03<\x12"},
+	{"log/slog/internal/buffer", "\xce\x02"},
+	{"log/syslog", "\x7f\x03\x88\x01\x12\x18\x18\x02\x0f"},
+	{"maps", "\x89\x02Z"},
+	{"math", "\xbf\x01VN"},
+	{"math/big", "\x03|\x03(\x15G\f\x03\x022\x02\x01\x02\x15"},
+	{"math/big/internal/asmgen", "\x03\x01~\x93\x014\x03"},
+	{"math/bits", "\xe3\x02"},
+	{"math/cmplx", "\x92\x02\x03"},
+	{"math/rand", "\xc8\x01J<\x01\x14"},
+	{"math/rand/v2", "\x7f+\x03e\x03N"},
+	{"mime", "\x02\x01m\x0f\x03\x84\x01\v#\x15\x03\x02\x11\x02"},
+	{"mime/multipart", "\x02\x01Q+\x03F>\v\x01\a\x02\x17\x02\x06\x0f\x02\x01\x17"},
+	{"mime/quotedprintable", "\x02\x01\x7f\x84\x01"},
+	{"net", "\x04\tr*\x1f\v\x05\x13\x01\x01\x04\x15\x01'\x06\r\b\x05\x01\x01\r\x06\t"},
+	{"net/http", "\x02\x01\x03\x01\x04\x02O\x14\x0f\x03F>\x01\x03\a\x01\x06\x01\x01\x02\x06\x02\x01\x01\f\x01\x01\x05\x01\x02\x05\b\x01\x01\x01\x02\x01\x0e\x02\x02\x02\n\x01\x03"},
+	{"net/http/cgi", "\x02Z#\x03\x84\x01\x04\a\v\x01\x15\x01\x01\x01\x04\x01\x05\x02\b\x02\x01\x11\x10"},
+	{"net/http/cookiejar", "\x04{\x03\x9a\x01\x01\b\t\x05\x16\x03\x02\x0f\x04"},
+	{"net/http/fcgi", "\x02\x01\nd\x0e\x03\x84\x01\x16\x01\x01\x16\x18\x02\x0f"},
+	{"net/http/httptest", "\x02\x01\nO\x02#\x01P4\x04\x12\x01\f\t\x02\r\n\x01\x02\x03\f\x06\n"},
+	{"net/http/httptrace", "\rOwI\x16+"},
+	{"net/http/httputil", "\x02\x01\nr\x03F>\x04\x0f\x03\x01\x05\x02\x01\r\x01\x19\x02\x0f\x10"},
+	{"net/http/internal", "\x02\x01n\x0e\x03\x84\x01"},
+	{"net/http/internal/ascii", "\xcc\x02\x13"},
+	{"net/http/internal/http2", "\x02\x01\x03\x01\x06G\b\x15\x0e\x03\x84\x01\x01\x03\b\x03\x02\x03\x02\x06\x02\x03\x01\n\x01\x01\b\x05\b\x02\x01\x02\x01\x0e\x10\x02\x02"},
+	{"net/http/internal/httpcommon", "\rr\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"},
+	{"net/http/internal/httpsfv", "\xc9\x02\x02\x01\x11\x04"},
+	{"net/http/internal/testcert", "\xcc\x02"},
+	{"net/http/pprof", "\x02\x01\nu\x18.\x11-\x04\x13\x16\x01\r\x04\x03\x01\x02\x01\x11"},
 	{"net/internal/cgotest", ""},
-	{"net/internal/socktest", "\x81\x01\xcc\x01\x02"},
-	{"net/mail", "\x02|\x03\x84\x01\x04\x0f\x03\x16\x1a\x02\x0f\x04"},
-	{"net/netip", "\x04z*\x01h\x036\x17"},
-	{"net/rpc", "\x02p\f\x03\x0f\nk\x04\x12\x01\x1f\r\x03\x02"},
-	{"net/rpc/jsonrpc", "t\n\x03\x84\x01\x16\x13\x1f"},
-	{"net/smtp", "\x195\r\x14\x0f\x03\x84\x01\x16\x16\x1a"},
-	{"net/textproto", "\x02\x01{\x03\x84\x01\f\n/\x01\x02\x15"},
-	{"net/url", "~\x03Ff\v\x10\x02\x01\x17"},
-	{"os", "~*\x01\x19\x04\x11\x14\x01\x03\x01\x05\x10\x01:\b\x05\x01\x01\r\x06"},
-	{"os/exec", "\x03\nqI(\x01\x15\x01-\x06\a\n\x01\x03\x01\r"},
-	{"os/exec/internal/fdtest", "\xcf\x02"},
-	{"os/signal", "\r\xa6\x02\x15\x05\x02"},
-	{"os/user", "\x02\x01{\x03\x84\x01.\r\n\x01\x02"},
-	{"path", "~*\xb8\x01"},
-	{"path/filepath", "~*\x1aD-\r\b\x03\x04\x11"},
-	{"plugin", "~"},
-	{"reflect", "~&\x04\x1e\x03\x11\b\x04\x05\x17\x06\t/\n\x03\x11\x02\x02"},
+	{"net/internal/socktest", "\x82\x01\xcc\x01\x02"},
+	{"net/mail", "\x02}\x03\x84\x01\x04\x0f\x03\x16\x1a\x02\x0f\x04"},
+	{"net/netip", "\x04{*\x01h\x036\x17"},
+	{"net/rpc", "\x02q\f\x03\x0f\nk\x04\x12\x01\x1f\r\x03\x02"},
+	{"net/rpc/jsonrpc", "u\n\x03\x84\x01\x16\x13\x1f"},
+	{"net/smtp", "\x196\r\x14\x0f\x03\x84\x01\x16\x16\x1a"},
+	{"net/textproto", "\x02\x01|\x03\x84\x01\f\n/\x01\x02\x15"},
+	{"net/url", "\x7f\x03Ff\v\x10\x02\x01\x17"},
+	{"os", "\x7f*\x01\x19\x04\x11\x14\x01\x03\x01\x05\x10\x01:\b\x05\x01\x01\r\x06"},
+	{"os/exec", "\x03\nrI(\x01\x15\x01-\x06\a\n\x01\x03\x01\r"},
+	{"os/exec/internal/fdtest", "\xd0\x02"},
+	{"os/signal", "\r\xa7\x02\x15\x05\x02"},
+	{"os/user", "\x02\x01|\x03\x84\x01.\r\n\x01\x02"},
+	{"path", "\x7f*\xb8\x01"},
+	{"path/filepath", "\x7f*\x1aD-\r\b\x03\x04\x11"},
+	{"plugin", "\x7f"},
+	{"reflect", "\x7f&\x04\x1e\x03\x11\b\x04\x05\x17\x06\t/\n\x03\x11\x02\x02"},
 	{"reflect/internal/example1", ""},
 	{"reflect/internal/example2", ""},
-	{"regexp", "\x03\x82\x02\x037\t\x02\x01\x02\x11\x02"},
-	{"regexp/syntax", "\xc8\x02\x01\x01\x01\x02\x11\x02"},
-	{"runtime", "\xa4\x01\x04\x01\x03\f\x06\b\x02\x01\x01\x0f\x03\x01\x01\x01\x02\x01\x01\x01\x02\x01\x04\x01\x10\x18N"},
-	{"runtime/coverage", "\xb0\x01U"},
-	{"runtime/debug", "\x81\x01V\\\r\b\x02\x01\x11\x06"},
-	{"runtime/metrics", "\xc8\x01I/\""},
-	{"runtime/pprof", "\x02\x01\x01\x03\x06c\x0e\x03#5\v!\f\"\r\b\x01\x01\x01\x02\x02\n\x03\x06"},
-	{"runtime/race", "\xc6\x02"},
+	{"regexp", "\x03\x83\x02\x037\t\x02\x01\x02\x11\x02"},
+	{"regexp/syntax", "\xc9\x02\x01\x01\x01\x02\x11\x02"},
+	{"runtime", "\xa5\x01\x04\x01\x03\f\x06\b\x02\x01\x01\x0f\x03\x01\x01\x01\x02\x01\x01\x01\x02\x01\x04\x01\x10\x18N"},
+	{"runtime/coverage", "\xb1\x01U"},
+	{"runtime/debug", "\x82\x01V\\\r\b\x02\x01\x11\x06"},
+	{"runtime/metrics", "\xc9\x01I/\""},
+	{"runtime/pprof", "\x02\x01\x01\x03\x06d\x0e\x03#5\v!\f\"\r\b\x01\x01\x01\x02\x02\n\x03\x06"},
+	{"runtime/race", "\xc7\x02"},
 	{"runtime/race/internal/amd64v1", ""},
-	{"runtime/trace", "\rq\x03{\t;\b\x05\x01\x0e\x06"},
-	{"slices", "\x04\x84\x02\fN"},
-	{"sort", "\xdb\x0194"},
-	{"strconv", "~*C\x01t"},
-	{"strings", "~&\x04D\x19\x03\f9\x11\x02\x02"},
+	{"runtime/trace", "\rr\x03{\t;\b\x05\x01\x0e\x06"},
+	{"slices", "\x04\x85\x02\fN"},
+	{"sort", "\xdc\x0194"},
+	{"strconv", "\x7f*C\x01t"},
+	{"strings", "\x7f&\x04D\x19\x03\f9\x11\x02\x02"},
 	{"structs", ""},
-	{"sync", "\xda\x01\x02\x11\x01R\x0e\x14"},
-	{"sync/atomic", "\xe2\x02"},
-	{"syscall", "~'\x03\x01\x1d\n\x04\x06\r\x04U\b\x05\x01\x14"},
-	{"testing", "\x03\nq\x02\x01Y\x17\x14\f\x05\x1d\x06\x02\x05\x02\x05\x01\x02\x01\x02\x01\x0e\x02\x04"},
-	{"testing/cryptotest", "SV\\\x126\x03\x12"},
-	{"testing/fstest", "~\x03\x84\x01\x01\n(\x10\x03\t\b"},
-	{"testing/internal/testdeps", "\x02\v\xb7\x011\x10.\x03\x05\x03\x06\a\x02\x0f"},
-	{"testing/iotest", "\x03{\x03\x84\x01\x04"},
-	{"testing/quick", "\x80\x01\x01\x90\x01\x05%\x10\x11"},
-	{"testing/slogtest", "\rq\x03\x8a\x010\x05\x10\f"},
-	{"testing/synctest", "\xee\x01b\f\x06"},
-	{"text/scanner", "\x03~\x84\x01.+\x02"},
-	{"text/tabwriter", "\x81\x01\x84\x01["},
-	{"text/template", "~\x03BB\x01\n\"\x01\x05\x01\x02\x05\v\x02\x0e\x03\x02"},
-	{"text/template/parse", "\x03~\xbf\x01\n\x01\x13\x02"},
-	{"time", "~*D(,\r\x02\x13"},
-	{"time/tzdata", "~\xd1\x01\x13"},
+	{"sync", "\xdb\x01\x02\x11\x01R\x0e\x14"},
+	{"sync/atomic", "\xe3\x02"},
+	{"syscall", "\x7f'\x03\x01\x1d\n\x04\x06\r\x04U\b\x05\x01\x14"},
+	{"testing", "\x03\nr\x02\x01Y\x17\x14\f\x05\x1d\x06\x02\x05\x02\x05\x01\x02\x01\x02\x01\x0e\x02\x04"},
+	{"testing/cryptotest", "TV\\\x126\x03\x12"},
+	{"testing/fstest", "\x7f\x03\x84\x01\x01\n(\x10\x03\t\b"},
+	{"testing/internal/testdeps", "\x02\v\xb8\x011\x10.\x03\x05\x03\x06\a\x02\x0f"},
+	{"testing/iotest", "\x03|\x03\x84\x01\x04"},
+	{"testing/quick", "\x81\x01\x01\x90\x01\x05%\x10\x11"},
+	{"testing/slogtest", "\rr\x03\x8a\x010\x05\x10\f"},
+	{"testing/synctest", "\xef\x01b\f\x06"},
+	{"text/scanner", "\x03\x7f\x84\x01.+\x02"},
+	{"text/tabwriter", "\x82\x01\x84\x01["},
+	{"text/template", "\x7f\x03BB\x01\n\"\x01\x05\x01\x02\x05\v\x02\x0e\x03\x02"},
+	{"text/template/parse", "\x03\x7f\xbf\x01\n\x01\x13\x02"},
+	{"time", "\x7f*D(,\r\x02\x13"},
+	{"time/tzdata", "\x7f\xd1\x01\x13"},
 	{"unicode", ""},
 	{"unicode/utf16", ""},
 	{"unicode/utf8", ""},
-	{"unique", "\xa4\x01\"&\x01S\r\x01\x14\x19"},
+	{"unique", "\xa5\x01\"&\x01S\r\x01\x14\x19"},
 	{"unsafe", ""},
-	{"uuid", "\x03\x01O\x1d\x03\v\xcf\x01\x0f"},
-	{"vendor/golang.org/x/crypto/chacha20", "\x10`\x0e\x96\x01,)"},
-	{"vendor/golang.org/x/crypto/chacha20poly1305", "\x10\aY\x0e\xe6\x01\x05\x01\f"},
-	{"vendor/golang.org/x/crypto/cryptobyte", "m\x11\x03\x91\x01)!\v"},
+	{"uuid", "\x03\x01P\x1d\x03\v\xcf\x01\x0f"},
+	{"vendor/golang.org/x/crypto/chacha20", "\x10a\x0e\x96\x01,)"},
+	{"vendor/golang.org/x/crypto/chacha20poly1305", "\x10\aZ\x0e\xe6\x01\x05\x01\f"},
+	{"vendor/golang.org/x/crypto/cryptobyte", "n\x11\x03\x91\x01)!\v"},
 	{"vendor/golang.org/x/crypto/cryptobyte/asn1", ""},
-	{"vendor/golang.org/x/crypto/hkdf", "\x18\x01e\x15r"},
-	{"vendor/golang.org/x/crypto/internal/alias", "\xe2\x02"},
-	{"vendor/golang.org/x/crypto/internal/poly1305", "Z\x16\xa4\x01"},
-	{"vendor/golang.org/x/net/dns/dnsmessage", "~\xca\x01"},
-	{"vendor/golang.org/x/net/http/httpguts", "\x9b\x02\x16\x1a\x15\x10"},
-	{"vendor/golang.org/x/net/http/httpproxy", "~\x03\x9a\x01\x12\x05\x01\x18\x15\x10"},
-	{"vendor/golang.org/x/net/http2/hpack", "\x03{\x03\x84\x01H"},
-	{"vendor/golang.org/x/net/http3", "\x9c\x02@\x06\x0f\x04"},
-	{"vendor/golang.org/x/net/idna", "\x81\x01\x90\x01:\x13\x02\x17\x02\x01"},
-	{"vendor/golang.org/x/net/internal/http3", "\rN#\x03\x84\x01\v\x04\a\x01\x05\x10\x01\x16\x02\x01\x02\x0f\x10\x02\x04\x03"},
-	{"vendor/golang.org/x/net/internal/httpcommon", "\rq\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"},
-	{"vendor/golang.org/x/net/internal/quic/quicwire", "p"},
-	{"vendor/golang.org/x/net/nettest", "\x03m\x0e\x03\x84\x01\x11\x05\x18\x01\f\n\x01\x02\x02\x01\f"},
-	{"vendor/golang.org/x/net/quic", "\x03\n\x01\x01\x01\t:\x04\x04\x15\x03\v\x03\x12r\x06\x06\x06\x04\x12\x06\x15\x02\x01\x02\x01\x01\r\x06\x02\x01\x01\x02\v"},
-	{"vendor/golang.org/x/sys/cpu", "\xb3\x02\r\n\x01\x17"},
-	{"vendor/golang.org/x/text/secure/bidirule", "~\xe2\x01\x18\x01"},
-	{"vendor/golang.org/x/text/transform", "\x03{\x87\x01["},
-	{"vendor/golang.org/x/text/unicode/bidi", "\x03\bv\x88\x01@\x17"},
-	{"vendor/golang.org/x/text/unicode/norm", "p\x11\x84\x01H\x13\x18"},
-	{"weak", "\xa4\x01\x9c\x01\""},
+	{"vendor/golang.org/x/crypto/hkdf", "\x18\x01f\x15r"},
+	{"vendor/golang.org/x/crypto/internal/alias", "\xe3\x02"},
+	{"vendor/golang.org/x/crypto/internal/poly1305", "[\x16\xa4\x01"},
+	{"vendor/golang.org/x/net/dns/dnsmessage", "\x7f\xca\x01"},
+	{"vendor/golang.org/x/net/http/httpguts", "\x9c\x02\x16\x1a\x15\x10"},
+	{"vendor/golang.org/x/net/http/httpproxy", "\x7f\x03\x9a\x01\x12\x05\x01\x18\x15\x10"},
+	{"vendor/golang.org/x/net/http2/hpack", "\x03|\x03\x84\x01H"},
+	{"vendor/golang.org/x/net/http3", "\x9d\x02@\x06\x0f\x04"},
+	{"vendor/golang.org/x/net/idna", "\x82\x01\x90\x01:\x13\x02\x17\x02\x01"},
+	{"vendor/golang.org/x/net/internal/http3", "\rO#\x03\x84\x01\v\x04\a\x01\x05\x10\x01\x16\x02\x01\x02\x0f\x10\x02\x04\x03"},
+	{"vendor/golang.org/x/net/internal/httpcommon", "\rr\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"},
+	{"vendor/golang.org/x/net/internal/quic/quicwire", "q"},
+	{"vendor/golang.org/x/net/nettest", "\x03n\x0e\x03\x84\x01\x11\x05\x18\x01\f\n\x01\x02\x02\x01\f"},
+	{"vendor/golang.org/x/net/quic", "\x03\n\x01\x01\x01\t;\x04\x04\x15\x03\v\x03\x12r\x06\x06\x06\x04\x12\x06\x15\x02\x01\x02\x01\x01\r\x06\x02\x01\x01\x02\v"},
+	{"vendor/golang.org/x/sys/cpu", "\xb4\x02\r\n\x01\x17"},
+	{"vendor/golang.org/x/text/secure/bidirule", "\x7f\xe2\x01\x18\x01"},
+	{"vendor/golang.org/x/text/transform", "\x03|\x87\x01["},
+	{"vendor/golang.org/x/text/unicode/bidi", "\x03\bw\x88\x01@\x17"},
+	{"vendor/golang.org/x/text/unicode/norm", "q\x11\x84\x01H\x13\x18"},
+	{"weak", "\xa5\x01\x9c\x01\""},
 }
 
 // bootstrap is the list of bootstrap packages extracted from cmd/dist.
diff --git a/vendor/golang.org/x/tools/internal/stdlib/manifest.go b/vendor/golang.org/x/tools/internal/stdlib/manifest.go
index 2180c296ec..e11abf0455 100644
--- a/vendor/golang.org/x/tools/internal/stdlib/manifest.go
+++ b/vendor/golang.org/x/tools/internal/stdlib/manifest.go
@@ -9153,6 +9153,7 @@ var PackageSymbols = map[string][]Symbol{
 		{"CrossOriginProtection", Type, 25, ""},
 		{"DefaultClient", Var, 0, ""},
 		{"DefaultMaxHeaderBytes", Const, 0, ""},
+		{"DefaultMaxHeaderValueCount", Const, 27, ""},
 		{"DefaultMaxIdleConnsPerHost", Const, 0, ""},
 		{"DefaultServeMux", Var, 0, ""},
 		{"DefaultTransport", Var, 0, ""},
@@ -9317,6 +9318,7 @@ var PackageSymbols = map[string][]Symbol{
 		{"Server.Handler", Field, 0, ""},
 		{"Server.IdleTimeout", Field, 8, ""},
 		{"Server.MaxHeaderBytes", Field, 0, ""},
+		{"Server.MaxHeaderValueCount", Field, 27, ""},
 		{"Server.Protocols", Field, 24, ""},
 		{"Server.ReadHeaderTimeout", Field, 8, ""},
 		{"Server.ReadTimeout", Field, 0, ""},
diff --git a/vendor/modules.txt b/vendor/modules.txt
index be2ceed172..23ce32bc8d 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -609,7 +609,7 @@ github.com/modern-go/reflect2
 github.com/munnerz/goautoneg
 # github.com/nxadm/tail v1.4.11
 ## explicit; go 1.13
-# github.com/onsi/ginkgo/v2 v2.32.0
+# github.com/onsi/ginkgo/v2 v2.32.1
 ## explicit; go 1.25.0
 github.com/onsi/ginkgo/v2/config
 github.com/onsi/ginkgo/v2/formatter
@@ -907,8 +907,8 @@ github.com/sigstore/sigstore/pkg/cryptoutils
 github.com/sigstore/sigstore/pkg/signature
 github.com/sigstore/sigstore/pkg/signature/options
 github.com/sigstore/sigstore/pkg/signature/payload
-# github.com/sirupsen/logrus v1.9.4
-## explicit; go 1.17
+# github.com/sirupsen/logrus v1.10.0
+## explicit; go 1.23
 github.com/sirupsen/logrus
 # github.com/smallstep/pkcs7 v0.2.1
 ## explicit; go 1.14
@@ -941,10 +941,12 @@ github.com/spiffe/go-spiffe/v2/workloadapi
 # github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6
 ## explicit; go 1.19
 github.com/stefanberger/go-pkcs11uri
-# github.com/stretchr/testify v1.11.1
+# github.com/stretchr/testify v1.12.0
 ## explicit; go 1.17
 github.com/stretchr/testify/assert
 github.com/stretchr/testify/assert/yaml
+github.com/stretchr/testify/internal/difflib
+github.com/stretchr/testify/internal/spew
 github.com/stretchr/testify/require
 # github.com/tidwall/btree v1.8.1
 ## explicit; go 1.20
@@ -1094,7 +1096,7 @@ go.podman.io/common/pkg/auth
 go.podman.io/common/pkg/capabilities
 go.podman.io/common/pkg/completion
 go.podman.io/common/pkg/password
-# go.podman.io/image/v5 v5.41.0
+# go.podman.io/image/v5 v5.41.1
 ## explicit; go 1.25.7
 go.podman.io/image/v5/copy
 go.podman.io/image/v5/directory/explicitfilepath
@@ -1193,7 +1195,7 @@ go.yaml.in/yaml/v2
 # go.yaml.in/yaml/v3 v3.0.5
 ## explicit; go 1.16
 go.yaml.in/yaml/v3
-# golang.org/x/crypto v0.54.0
+# golang.org/x/crypto v0.55.0
 ## explicit; go 1.25.0
 golang.org/x/crypto/argon2
 golang.org/x/crypto/blake2b
@@ -1222,7 +1224,7 @@ golang.org/x/mod/internal/lazyregexp
 golang.org/x/mod/modfile
 golang.org/x/mod/module
 golang.org/x/mod/semver
-# golang.org/x/net v0.57.0
+# golang.org/x/net v0.58.0
 ## explicit; go 1.25.0
 golang.org/x/net/context
 golang.org/x/net/http/httpguts
@@ -1256,7 +1258,7 @@ golang.org/x/sys/windows/registry
 # golang.org/x/term v0.45.0
 ## explicit; go 1.25.0
 golang.org/x/term
-# golang.org/x/text v0.40.0
+# golang.org/x/text v0.41.0
 ## explicit; go 1.25.0
 golang.org/x/text/cases
 golang.org/x/text/feature/plural
@@ -1278,7 +1280,7 @@ golang.org/x/text/unicode/norm
 # golang.org/x/time v0.15.0
 ## explicit; go 1.25.0
 golang.org/x/time/rate
-# golang.org/x/tools v0.47.0
+# golang.org/x/tools v0.48.0
 ## explicit; go 1.25.0
 golang.org/x/tools/cover
 golang.org/x/tools/go/analysis
@@ -1299,6 +1301,7 @@ golang.org/x/tools/internal/gcimporter
 golang.org/x/tools/internal/gocommand
 golang.org/x/tools/internal/gopathwalk
 golang.org/x/tools/internal/imports
+golang.org/x/tools/internal/modindex
 golang.org/x/tools/internal/packagesinternal
 golang.org/x/tools/internal/pkgbits
 golang.org/x/tools/internal/stdlib