diff --git a/go.mod b/go.mod index 49a449f3f7..36e803ffa4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/openshift-kni/lifecycle-agent -go 1.25.0 +go 1.25.7 require ( github.com/containers/image/v5 v5.36.2 @@ -15,7 +15,7 @@ require ( github.com/openshift/library-go v0.0.0-20260213153706-03f1709971c5 github.com/openshift/local-storage-operator v0.0.0-20240119085755-569393ff42e1 github.com/openshift/lvm-operator v0.0.0-20240627192035-bf9fc3ac7ee8 - github.com/operator-framework/api v0.37.0 + github.com/operator-framework/api v0.42.0 github.com/otiai10/copy v1.14.0 github.com/pelletier/go-toml v1.9.5 github.com/samber/lo v1.52.0 @@ -34,12 +34,12 @@ require ( open-cluster-management.io/api v1.1.0 open-cluster-management.io/config-policy-controller v0.17.0 open-cluster-management.io/governance-policy-propagator v0.17.0 - sigs.k8s.io/controller-runtime v0.22.5 + sigs.k8s.io/controller-runtime v0.23.3 sigs.k8s.io/yaml v1.6.0 ) require ( - cel.dev/expr v0.25.0 // indirect + cel.dev/expr v0.25.1 // indirect cyphar.com/go-pathrs v0.2.1 // indirect dario.cat/mergo v1.0.2 // indirect github.com/BurntSushi/toml v1.5.0 // indirect @@ -55,21 +55,21 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/swag/cmdutils v0.25.1 // indirect - github.com/go-openapi/swag/conv v0.25.1 // indirect - github.com/go-openapi/swag/fileutils v0.25.1 // indirect - github.com/go-openapi/swag/jsonname v0.25.1 // indirect - github.com/go-openapi/swag/jsonutils v0.25.1 // indirect - github.com/go-openapi/swag/loading v0.25.1 // indirect - github.com/go-openapi/swag/mangling v0.25.1 // indirect - github.com/go-openapi/swag/netutils v0.25.1 // indirect - github.com/go-openapi/swag/stringutils v0.25.1 // indirect - github.com/go-openapi/swag/typeutils v0.25.1 // indirect - github.com/go-openapi/swag/yamlutils v0.25.1 // indirect + github.com/go-openapi/swag/cmdutils v0.25.4 // indirect + github.com/go-openapi/swag/conv v0.25.4 // indirect + github.com/go-openapi/swag/fileutils v0.25.4 // indirect + github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.4 // indirect + github.com/go-openapi/swag/loading v0.25.4 // indirect + github.com/go-openapi/swag/mangling v0.25.4 // indirect + github.com/go-openapi/swag/netutils v0.25.4 // indirect + github.com/go-openapi/swag/stringutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect github.com/golang/mock v1.6.0 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/cel-go v0.26.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect + github.com/google/cel-go v0.27.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect github.com/moby/sys/capability v0.4.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/moby/sys/user v0.4.0 // indirect @@ -79,31 +79,30 @@ require ( github.com/pierrec/lz4/v4 v4.1.17 // indirect github.com/pkg/xattr v0.4.9 // indirect github.com/robfig/cron v1.2.0 // indirect - github.com/stoewer/go-strcase v1.3.1 // indirect github.com/stolostron/kubernetes-dependency-watches v0.10.2 // indirect github.com/ulikunitz/xz v0.5.14 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect go.opentelemetry.io/otel v1.41.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect go.opentelemetry.io/otel/metric v1.41.0 // indirect - go.opentelemetry.io/otel/sdk v1.38.0 // indirect + go.opentelemetry.io/otel/sdk v1.40.0 // indirect go.opentelemetry.io/otel/trace v1.41.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect - google.golang.org/grpc v1.77.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b // indirect + google.golang.org/grpc v1.79.3 // indirect gopkg.in/djherbis/times.v1 v1.3.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect k8s.io/apiserver v0.35.4 // indirect k8s.io/component-base v0.35.4 // indirect k8s.io/gengo/v2 v2.0.0-20250922181213-ec3ebc5fd46b // indirect k8s.io/kube-aggregator v0.34.1 // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.1 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect @@ -131,11 +130,11 @@ require ( github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect github.com/go-logr/zapr v1.3.0 // indirect - github.com/go-openapi/jsonpointer v0.22.1 // indirect - github.com/go-openapi/jsonreference v0.21.3 // indirect - github.com/go-openapi/swag v0.25.1 // indirect + github.com/go-openapi/jsonpointer v0.22.4 // indirect + github.com/go-openapi/jsonreference v0.21.4 // indirect + github.com/go-openapi/swag v0.25.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/gnostic-models v0.7.0 // indirect + github.com/google/gnostic-models v0.7.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/imdario/mergo v1.0.0 // indirect @@ -153,7 +152,7 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.67.2 // indirect + github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.19.2 // indirect github.com/rh-ecosystem-edge/preinstall-utils v0.0.0-20241120105227-a01c7fe6b461 github.com/shopspring/decimal v1.4.0 // indirect @@ -163,7 +162,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go4.org v0.0.0-20200104003542-c7e774b10ea0 // indirect - golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/mod v0.32.0 // indirect golang.org/x/net v0.49.0 // indirect golang.org/x/oauth2 v0.34.0 // indirect @@ -174,11 +173,11 @@ require ( golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.41.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/protobuf v1.36.10 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect + k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 // indirect k8s.io/utils v0.0.0-20260108192941-914a6e750570 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect ) diff --git a/go.sum b/go.sum index 0fd6c6b477..b1544e0f3a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -cel.dev/expr v0.25.0 h1:qbCFvDJJthxLvf3TqeF9Ys7pjjWrO7LMzfYhpJUc30g= -cel.dev/expr v0.25.0/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8= cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= @@ -100,36 +100,38 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk= -github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM= -github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc= -github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4= -github.com/go-openapi/swag v0.25.1 h1:6uwVsx+/OuvFVPqfQmOOPsqTcm5/GkBhNwLqIR916n8= -github.com/go-openapi/swag v0.25.1/go.mod h1:bzONdGlT0fkStgGPd3bhZf1MnuPkf2YAys6h+jZipOo= -github.com/go-openapi/swag/cmdutils v0.25.1 h1:nDke3nAFDArAa631aitksFGj2omusks88GF1VwdYqPY= -github.com/go-openapi/swag/cmdutils v0.25.1/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= -github.com/go-openapi/swag/conv v0.25.1 h1:+9o8YUg6QuqqBM5X6rYL/p1dpWeZRhoIt9x7CCP+he0= -github.com/go-openapi/swag/conv v0.25.1/go.mod h1:Z1mFEGPfyIKPu0806khI3zF+/EUXde+fdeksUl2NiDs= -github.com/go-openapi/swag/fileutils v0.25.1 h1:rSRXapjQequt7kqalKXdcpIegIShhTPXx7yw0kek2uU= -github.com/go-openapi/swag/fileutils v0.25.1/go.mod h1:+NXtt5xNZZqmpIpjqcujqojGFek9/w55b3ecmOdtg8M= -github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU= -github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo= -github.com/go-openapi/swag/jsonutils v0.25.1 h1:AihLHaD0brrkJoMqEZOBNzTLnk81Kg9cWr+SPtxtgl8= -github.com/go-openapi/swag/jsonutils v0.25.1/go.mod h1:JpEkAjxQXpiaHmRO04N1zE4qbUEg3b7Udll7AMGTNOo= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1 h1:DSQGcdB6G0N9c/KhtpYc71PzzGEIc/fZ1no35x4/XBY= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1/go.mod h1:kjmweouyPwRUEYMSrbAidoLMGeJ5p6zdHi9BgZiqmsg= -github.com/go-openapi/swag/loading v0.25.1 h1:6OruqzjWoJyanZOim58iG2vj934TysYVptyaoXS24kw= -github.com/go-openapi/swag/loading v0.25.1/go.mod h1:xoIe2EG32NOYYbqxvXgPzne989bWvSNoWoyQVWEZicc= -github.com/go-openapi/swag/mangling v0.25.1 h1:XzILnLzhZPZNtmxKaz/2xIGPQsBsvmCjrJOWGNz/ync= -github.com/go-openapi/swag/mangling v0.25.1/go.mod h1:CdiMQ6pnfAgyQGSOIYnZkXvqhnnwOn997uXZMAd/7mQ= -github.com/go-openapi/swag/netutils v0.25.1 h1:2wFLYahe40tDUHfKT1GRC4rfa5T1B4GWZ+msEFA4Fl4= -github.com/go-openapi/swag/netutils v0.25.1/go.mod h1:CAkkvqnUJX8NV96tNhEQvKz8SQo2KF0f7LleiJwIeRE= -github.com/go-openapi/swag/stringutils v0.25.1 h1:Xasqgjvk30eUe8VKdmyzKtjkVjeiXx1Iz0zDfMNpPbw= -github.com/go-openapi/swag/stringutils v0.25.1/go.mod h1:JLdSAq5169HaiDUbTvArA2yQxmgn4D6h4A+4HqVvAYg= -github.com/go-openapi/swag/typeutils v0.25.1 h1:rD/9HsEQieewNt6/k+JBwkxuAHktFtH3I3ysiFZqukA= -github.com/go-openapi/swag/typeutils v0.25.1/go.mod h1:9McMC/oCdS4BKwk2shEB7x17P6HmMmA6dQRtAkSnNb8= -github.com/go-openapi/swag/yamlutils v0.25.1 h1:mry5ez8joJwzvMbaTGLhw8pXUnhDK91oSJLDPF1bmGk= -github.com/go-openapi/swag/yamlutils v0.25.1/go.mod h1:cm9ywbzncy3y6uPm/97ysW8+wZ09qsks+9RS8fLWKqg= +github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= +github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= +github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= +github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= +github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= +github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= +github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= +github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= +github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y= +github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48= +github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= +github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= +github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-task/slim-sprig v2.20.0+incompatible h1:4Xh3bDzO29j4TWNOI+24ubc0vbVFMg2PMnXKxK54/CA= @@ -146,10 +148,10 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= -github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= -github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= -github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= +github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= +github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= +github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -162,8 +164,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= @@ -239,8 +241,8 @@ github.com/openshift/lvm-operator v0.0.0-20240627192035-bf9fc3ac7ee8 h1:xSTHSdkK github.com/openshift/lvm-operator v0.0.0-20240627192035-bf9fc3ac7ee8/go.mod h1:L8Myy4T/jDhxV1TYMbRgcqoun9jibxdyrcyj7cYZn7M= github.com/openshift/machine-config-operator v0.0.1-0.20250320230514-53e78f3692ee h1:ITOXo9zrpzl68qrzh/+1uRJlbpMl88jETfWfFyIa3uU= github.com/openshift/machine-config-operator v0.0.1-0.20250320230514-53e78f3692ee/go.mod h1:ZA2cpM5ol3zcxRpG4gQr+XSlEQJe732noPWcMz6MQ4k= -github.com/operator-framework/api v0.37.0 h1:2XCMWitBnumtJTqzip6LQKUwpM2pXVlt3gkpdlkbaCE= -github.com/operator-framework/api v0.37.0/go.mod h1:NZs4vB+Jiamyv3pdPDjZtuC4U7KX0eq4z2r5hKY5fUA= +github.com/operator-framework/api v0.42.0 h1:rkc5V3zW8RxZMjePAe12jdL7Co/hwsYo1pLnkkhuR7s= +github.com/operator-framework/api v0.42.0/go.mod h1:bMEj+wl/8tGqcGNtxt38cLUYagu9chNsbYzb/5HQaUQ= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= @@ -263,8 +265,8 @@ github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyAEN8= -github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/rh-ecosystem-edge/preinstall-utils v0.0.0-20241120105227-a01c7fe6b461 h1:zuwh5Si2zxHTq4Hn72aJOV3jl/lBU05OSVvW+k/ZgKA= @@ -290,19 +292,12 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= -github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stolostron/kubernetes-dependency-watches v0.10.2 h1:b8g3Wye6igeRrrSQwXGr4Ch4zTY4uouzHlDARw8/m7w= github.com/stolostron/kubernetes-dependency-watches v0.10.2/go.mod h1:pV1b/PwVNG4a5tzBcqxhUpMjjo5nY0gUb6ieu6sGH6o= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.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/ulikunitz/xz v0.5.14 h1:uv/0Bq533iFdnMHZdRBTOlaNMdb1+ZxXIlHDZHIHcvg= @@ -321,20 +316,20 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs= go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= +go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= +go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= +go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= @@ -358,8 +353,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -419,14 +414,14 @@ gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0 gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba h1:B14OtaXuMaCQsl2deSvNkyPKIzq3BjfxQp8d00QyWx4= -google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:G5IanEx8/PgI9w6CFcYQf7jMtHQhZruvfM1i3qOqk5U= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= -google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b h1:SGYyueaEovpqmWmtTvwtVgo638V/QFE2zlTCnRrR3jg= +google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b h1:GZxXGdFaHX27ZSMHudWc4FokdD+xl8BC2UJm1OVIEzs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -445,7 +440,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20190502103701-55513cacd4ae/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20191010095647-fc94e3f71652/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.35.4 h1:P7nFYKl5vo9AGUp1Z+Pmd3p2tA7bX2wbFWCvDeRv988= @@ -468,8 +462,8 @@ k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-aggregator v0.34.1 h1:WNLV0dVNoFKmuyvdWLd92iDSyD/TSTjqwaPj0U9XAEU= k8s.io/kube-aggregator v0.34.1/go.mod h1:RU8j+5ERfp0h+gIvWtxRPfsa5nK7rboDm8RST8BJfYQ= -k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= -k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 h1:HhDfevmPS+OalTjQRKbTHppRIz01AWi8s45TMXStgYY= +k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= k8s.io/utils v0.0.0-20260108192941-914a6e750570 h1:JT4W8lsdrGENg9W+YwwdLJxklIuKWdRm+BC+xt33FOY= k8s.io/utils v0.0.0-20260108192941-914a6e750570/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= open-cluster-management.io/api v1.1.0 h1:fu5xst9T/Ya6o41kqdd0zbNiDU+D3nNMTvoRVeF8j+U= @@ -478,10 +472,10 @@ open-cluster-management.io/config-policy-controller v0.17.0 h1:3slKRMzbnB0udYfEi open-cluster-management.io/config-policy-controller v0.17.0/go.mod h1:+WcgRBsWIoge7bgGRkPMDlJJ1S9m5E1iNSV0hZahbts= open-cluster-management.io/governance-policy-propagator v0.17.0 h1:IrgiutvfXsVhOp3Q6LxDWQ4tLw+eQ+znZwkNKu8n87g= open-cluster-management.io/governance-policy-propagator v0.17.0/go.mod h1:grmb322f8yuERUAey/EEysGleSqYoqdpHPV7niz5xfY= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.1 h1:Ac6yUpjm3TIUHrG6+aaacSJ/UzsCGFIj6C9VYNfCOMk= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.1/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.22.5 h1:v3nfSUMowX/2WMp27J9slwGFyAt7IV0YwBxAkrUr0GE= -sigs.k8s.io/controller-runtime v0.22.5/go.mod h1:pc5SoYWnWI6I+cBHYYdZ7B6YHZVY5xNfll88JB+vniI= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80= +sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96 h1:PFWFSkpArPNJxFX4ZKWAk9NSeRoZaXschn+ULa4xVek= diff --git a/vendor/github.com/go-openapi/jsonpointer/.cliff.toml b/vendor/github.com/go-openapi/jsonpointer/.cliff.toml new file mode 100644 index 0000000000..702629f5dc --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/.cliff.toml @@ -0,0 +1,181 @@ +# git-cliff ~ configuration file +# https://git-cliff.org/docs/configuration + +[changelog] +header = """ +""" + +footer = """ + +----- + +**[{{ remote.github.repo }}]({{ self::remote_url() }}) license terms** + +[![License][license-badge]][license-url] + +[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg +[license-url]: {{ self::remote_url() }}/?tab=Apache-2.0-1-ov-file#readme + +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} +""" + +body = """ +{%- if version %} +## [{{ version | trim_start_matches(pat="v") }}]({{ self::remote_url() }}/tree/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }} +{%- else %} +## [unreleased] +{%- endif %} +{%- if message %} + {%- raw %}\n{% endraw %} +{{ message }} + {%- raw %}\n{% endraw %} +{%- endif %} +{%- if version %} + {%- if previous.version %} + +**Full Changelog**: <{{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }}> + {%- endif %} +{%- else %} + {%- raw %}\n{% endraw %} +{%- endif %} + +{%- if statistics %}{% if statistics.commit_count %} + {%- raw %}\n{% endraw %} +{{ statistics.commit_count }} commits in this release. + {%- raw %}\n{% endraw %} +{%- endif %}{% endif %} +----- + +{%- for group, commits in commits | group_by(attribute="group") %} + {%- raw %}\n{% endraw %} +### {{ group | upper_first }} + {%- raw %}\n{% endraw %} + {%- for commit in commits %} + {%- if commit.remote.pr_title %} + {%- set commit_message = commit.remote.pr_title %} + {%- else %} + {%- set commit_message = commit.message %} + {%- endif %} +* {{ commit_message | split(pat="\n") | first | trim }} + {%- if commit.remote.username %} +{%- raw %} {% endraw %}by [@{{ commit.remote.username }}](https://github.com/{{ commit.remote.username }}) + {%- endif %} + {%- if commit.remote.pr_number %} +{%- raw %} {% endraw %}in [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}) + {%- endif %} +{%- raw %} {% endraw %}[...]({{ self::remote_url() }}/commit/{{ commit.id }}) + {%- endfor %} +{%- endfor %} + +{%- if github %} +{%- raw %}\n{% endraw -%} + {%- set all_contributors = github.contributors | length %} + {%- if github.contributors | filter(attribute="username", value="dependabot[bot]") | length < all_contributors %} +----- + +### People who contributed to this release + {% endif %} + {%- for contributor in github.contributors | filter(attribute="username") | sort(attribute="username") %} + {%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %} +* [@{{ contributor.username }}](https://github.com/{{ contributor.username }}) + {%- endif %} + {%- endfor %} + + {% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} +----- + {%- raw %}\n{% endraw %} + +### New Contributors + {%- endif %} + + {%- for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} + {%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %} +* @{{ contributor.username }} made their first contribution + {%- if contributor.pr_number %} + in [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ + {%- endif %} + {%- endif %} + {%- endfor %} +{%- endif %} + +{%- raw %}\n{% endraw %} + +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} +""" +# Remove leading and trailing whitespaces from the changelog's body. +trim = true +# Render body even when there are no releases to process. +render_always = true +# An array of regex based postprocessors to modify the changelog. +postprocessors = [ + # Replace the placeholder with a URL. + #{ pattern = '', replace = "https://github.com/orhun/git-cliff" }, +] +# output file path +# output = "test.md" + +[git] +# Parse commits according to the conventional commits specification. +# See https://www.conventionalcommits.org +conventional_commits = false +# Exclude commits that do not match the conventional commits specification. +filter_unconventional = false +# Require all commits to be conventional. +# Takes precedence over filter_unconventional. +require_conventional = false +# Split commits on newlines, treating each line as an individual commit. +split_commits = false +# An array of regex based parsers to modify commit messages prior to further processing. +commit_preprocessors = [ + # Replace issue numbers with link templates to be updated in `changelog.postprocessors`. + #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, + # Check spelling of the commit message using https://github.com/crate-ci/typos. + # If the spelling is incorrect, it will be fixed automatically. + #{ pattern = '.*', replace_command = 'typos --write-changes -' } +] +# Prevent commits that are breaking from being excluded by commit parsers. +protect_breaking_commits = false +# An array of regex based parsers for extracting data from the commit message. +# Assigns commits to groups. +# Optionally sets the commit's scope and can decide to exclude commits from further processing. +commit_parsers = [ + { message = "^[Cc]hore\\([Rr]elease\\): prepare for", skip = true }, + { message = "(^[Mm]erge)|([Mm]erge conflict)", skip = true }, + { field = "author.name", pattern = "dependabot*", group = "Updates" }, + { message = "([Ss]ecurity)|([Vv]uln)", group = "Security" }, + { body = "(.*[Ss]ecurity)|([Vv]uln)", group = "Security" }, + { message = "([Cc]hore\\(lint\\))|(style)|(lint)|(codeql)|(golangci)", group = "Code quality" }, + { message = "(^[Dd]oc)|((?i)readme)|(badge)|(typo)|(documentation)", group = "Documentation" }, + { message = "(^[Ff]eat)|(^[Ee]nhancement)", group = "Implemented enhancements" }, + { message = "(^ci)|(\\(ci\\))|(fixup\\s+ci)|(fix\\s+ci)|(license)|(example)", group = "Miscellaneous tasks" }, + { message = "^test", group = "Testing" }, + { message = "(^fix)|(panic)", group = "Fixed bugs" }, + { message = "(^refact)|(rework)", group = "Refactor" }, + { message = "(^[Pp]erf)|(performance)", group = "Performance" }, + { message = "(^[Cc]hore)", group = "Miscellaneous tasks" }, + { message = "^[Rr]evert", group = "Reverted changes" }, + { message = "(upgrade.*?go)|(go\\s+version)", group = "Updates" }, + { message = ".*", group = "Other" }, +] +# Exclude commits that are not matched by any commit parser. +filter_commits = false +# An array of link parsers for extracting external references, and turning them into URLs, using regex. +link_parsers = [] +# Include only the tags that belong to the current branch. +use_branch_tags = false +# Order releases topologically instead of chronologically. +topo_order = false +# Order releases topologically instead of chronologically. +topo_order_commits = true +# Order of commits in each group/release within the changelog. +# Allowed values: newest, oldest +sort_commits = "newest" +# Process submodules commits +recurse_submodules = false + +#[remote.github] +#owner = "go-openapi" diff --git a/vendor/github.com/go-openapi/jsonpointer/.gitignore b/vendor/github.com/go-openapi/jsonpointer/.gitignore index 769c244007..59cd294891 100644 --- a/vendor/github.com/go-openapi/jsonpointer/.gitignore +++ b/vendor/github.com/go-openapi/jsonpointer/.gitignore @@ -1 +1,4 @@ -secrets.yml +*.out +*.cov +.idea +.env diff --git a/vendor/github.com/go-openapi/jsonpointer/.golangci.yml b/vendor/github.com/go-openapi/jsonpointer/.golangci.yml index 7cea1af8b5..fdae591bce 100644 --- a/vendor/github.com/go-openapi/jsonpointer/.golangci.yml +++ b/vendor/github.com/go-openapi/jsonpointer/.golangci.yml @@ -2,34 +2,17 @@ version: "2" linters: default: all disable: - - cyclop - depguard - - errchkjson - - errorlint - - exhaustruct - - forcetypeassert - funlen - - gochecknoglobals - - gochecknoinits - - gocognit - - godot - godox - - gosmopolitan - - inamedparam - #- intrange # disabled while < go1.22 - - ireturn - - lll - - musttag - - nestif + - exhaustruct - nlreturn - nonamedreturns - noinlineerr - paralleltest - recvcheck - testpackage - - thelper - tparallel - - unparam - varnamelen - whitespace - wrapcheck @@ -41,8 +24,15 @@ linters: goconst: min-len: 2 min-occurrences: 3 + cyclop: + max-complexity: 20 gocyclo: - min-complexity: 45 + min-complexity: 20 + exhaustive: + default-signifies-exhaustive: true + default-case-required: true + lll: + line-length: 180 exclusions: generated: lax presets: @@ -58,6 +48,7 @@ formatters: enable: - gofmt - goimports + - gofumpt exclusions: generated: lax paths: diff --git a/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md b/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md new file mode 100644 index 0000000000..03c098316d --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md @@ -0,0 +1,24 @@ +# Contributors + +- Repository: ['go-openapi/jsonpointer'] + +| Total Contributors | Total Contributions | +| --- | --- | +| 12 | 95 | + +| Username | All Time Contribution Count | All Commits | +| --- | --- | --- | +| @fredbi | 48 | https://github.com/go-openapi/jsonpointer/commits?author=fredbi | +| @casualjim | 33 | https://github.com/go-openapi/jsonpointer/commits?author=casualjim | +| @magodo | 3 | https://github.com/go-openapi/jsonpointer/commits?author=magodo | +| @youyuanwu | 3 | https://github.com/go-openapi/jsonpointer/commits?author=youyuanwu | +| @gaiaz-iusipov | 1 | https://github.com/go-openapi/jsonpointer/commits?author=gaiaz-iusipov | +| @gbjk | 1 | https://github.com/go-openapi/jsonpointer/commits?author=gbjk | +| @gordallott | 1 | https://github.com/go-openapi/jsonpointer/commits?author=gordallott | +| @ianlancetaylor | 1 | https://github.com/go-openapi/jsonpointer/commits?author=ianlancetaylor | +| @mfleader | 1 | https://github.com/go-openapi/jsonpointer/commits?author=mfleader | +| @Neo2308 | 1 | https://github.com/go-openapi/jsonpointer/commits?author=Neo2308 | +| @olivierlemasle | 1 | https://github.com/go-openapi/jsonpointer/commits?author=olivierlemasle | +| @testwill | 1 | https://github.com/go-openapi/jsonpointer/commits?author=testwill | + + _this file was generated by the [Contributors GitHub Action](https://github.com/github/contributors)_ diff --git a/vendor/github.com/go-openapi/jsonpointer/LICENSE b/vendor/github.com/go-openapi/jsonpointer/LICENSE index d645695673..261eeb9e9f 100644 --- a/vendor/github.com/go-openapi/jsonpointer/LICENSE +++ b/vendor/github.com/go-openapi/jsonpointer/LICENSE @@ -1,4 +1,3 @@ - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/vendor/github.com/go-openapi/jsonpointer/NOTICE b/vendor/github.com/go-openapi/jsonpointer/NOTICE new file mode 100644 index 0000000000..f3b51939a9 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/NOTICE @@ -0,0 +1,39 @@ +Copyright 2015-2025 go-swagger maintainers + +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +This software library, github.com/go-openapi/jsonpointer, includes software developed +by the go-swagger and go-openapi maintainers ("go-swagger maintainers"). + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this software except in compliance with the License. + +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0. + +This software is copied from, derived from, and inspired by other original software products. +It ships with copies of other software which license terms are recalled below. + +The original software was authored on 25-02-2013 by sigu-399 (https://github.com/sigu-399, sigu.399@gmail.com). + +github.com/sigh-399/jsonpointer +=========================== + +// SPDX-FileCopyrightText: Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) +// SPDX-License-Identifier: Apache-2.0 + +Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/github.com/go-openapi/jsonpointer/README.md b/vendor/github.com/go-openapi/jsonpointer/README.md index 45bd31b14f..b61b63fd9a 100644 --- a/vendor/github.com/go-openapi/jsonpointer/README.md +++ b/vendor/github.com/go-openapi/jsonpointer/README.md @@ -1,16 +1,78 @@ -# gojsonpointer [![Build Status](https://github.com/go-openapi/jsonpointer/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/jsonpointer/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonpointer) +# jsonpointer -[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) -[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE) -[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/jsonpointer.svg)](https://pkg.go.dev/github.com/go-openapi/jsonpointer) -[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/jsonpointer)](https://goreportcard.com/report/github.com/go-openapi/jsonpointer) + +[![Tests][test-badge]][test-url] [![Coverage][cov-badge]][cov-url] [![CI vuln scan][vuln-scan-badge]][vuln-scan-url] [![CodeQL][codeql-badge]][codeql-url] + + + +[![Release][release-badge]][release-url] [![Go Report Card][gocard-badge]][gocard-url] [![CodeFactor Grade][codefactor-badge]][codefactor-url] [![License][license-badge]][license-url] + + +[![GoDoc][godoc-badge]][godoc-url] [![Slack Channel][slack-logo]![slack-badge]][slack-url] [![go version][goversion-badge]][goversion-url] ![Top language][top-badge] ![Commits since latest release][commits-badge] -An implementation of JSON Pointer - Go language +--- + +An implementation of JSON Pointer for golang, which supports go `struct`. ## Status -Completed YES -Tested YES +API is stable. + +## Import this library in your project + +```cmd +go get github.com/go-openapi/jsonpointer +``` + +## Basic usage + +See also some [examples](./examples_test.go) + +### Retrieving a value + +```go + import ( + "github.com/go-openapi/jsonpointer" + ) + + + var doc any + + ... + + pointer, err := jsonpointer.New("/foo/1") + if err != nil { + ... // error: e.g. invalid JSON pointer specification + } + + value, kind, err := pointer.Get(doc) + if err != nil { + ... // error: e.g. key not found, index out of bounds, etc. + } + + ... +``` + +### Setting a value + +```go + ... + var doc any + ... + pointer, err := jsonpointer.New("/foo/1") + if err != nil { + ... // error: e.g. invalid JSON pointer specification + } + + doc, err = p.Set(doc, "value") + if err != nil { + ... // error: e.g. key not found, index out of bounds, etc. + } +``` + +## Change log + +See ## References @@ -18,9 +80,70 @@ Tested YES also known as [RFC6901](https://www.rfc-editor.org/rfc/rfc6901) -### Note +## Licensing + +This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE). + +See the license [NOTICE](./NOTICE), which recalls the licensing terms of all the pieces of software +on top of which it has been built. + +## Limitations + +The 4.Evaluation part of the previous reference, starting with 'If the currently referenced value is a JSON array, +the reference token MUST contain either...' is not implemented. + +That is because our implementation of the JSON pointer only supports explicit references to array elements: +the provision in the spec to resolve non-existent members as "the last element in the array", +using the special trailing character "-" is not implemented. + +## Other documentation + +* [All-time contributors](./CONTRIBUTORS.md) +* [Contributing guidelines](.github/CONTRIBUTING.md) +* [Maintainers documentation](docs/MAINTAINERS.md) +* [Code style](docs/STYLE.md) + +## Cutting a new release + +Maintainers can cut a new release by either: -The 4.Evaluation part of the previous reference, starting with 'If the currently referenced value is a JSON array, the reference token MUST contain either...' is not implemented. +* running [this workflow](https://github.com/go-openapi/jsonpointer/actions/workflows/bump-release.yml) +* or pushing a semver tag + * signed tags are preferred + * The tag message is prepended to release notes -That is because our implementation of the JSON pointer only supports explicit references to array elements: the provision in the spec -to resolve non-existent members as "the last element in the array", using the special trailing character "-". + +[test-badge]: https://github.com/go-openapi/jsonpointer/actions/workflows/go-test.yml/badge.svg +[test-url]: https://github.com/go-openapi/jsonpointer/actions/workflows/go-test.yml +[cov-badge]: https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg +[cov-url]: https://codecov.io/gh/go-openapi/jsonpointer +[vuln-scan-badge]: https://github.com/go-openapi/jsonpointer/actions/workflows/scanner.yml/badge.svg +[vuln-scan-url]: https://github.com/go-openapi/jsonpointer/actions/workflows/scanner.yml +[codeql-badge]: https://github.com/go-openapi/jsonpointer/actions/workflows/codeql.yml/badge.svg +[codeql-url]: https://github.com/go-openapi/jsonpointer/actions/workflows/codeql.yml + +[release-badge]: https://badge.fury.io/gh/go-openapi%2Fjsonpointer.svg +[release-url]: https://badge.fury.io/gh/go-openapi%2Fjsonpointer +[gomod-badge]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Fjsonpointer.svg +[gomod-url]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Fjsonpointer + +[gocard-badge]: https://goreportcard.com/badge/github.com/go-openapi/jsonpointer +[gocard-url]: https://goreportcard.com/report/github.com/go-openapi/jsonpointer +[codefactor-badge]: https://img.shields.io/codefactor/grade/github/go-openapi/jsonpointer +[codefactor-url]: https://www.codefactor.io/repository/github/go-openapi/jsonpointer + +[doc-badge]: https://img.shields.io/badge/doc-site-blue?link=https%3A%2F%2Fgoswagger.io%2Fgo-openapi%2F +[doc-url]: https://goswagger.io/go-openapi +[godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/jsonpointer +[godoc-url]: http://pkg.go.dev/github.com/go-openapi/jsonpointer +[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png +[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM +[slack-url]: https://goswagger.slack.com/archives/C04R30YMU + +[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg +[license-url]: https://github.com/go-openapi/jsonpointer/?tab=Apache-2.0-1-ov-file#readme + +[goversion-badge]: https://img.shields.io/github/go-mod/go-version/go-openapi/jsonpointer +[goversion-url]: https://github.com/go-openapi/jsonpointer/blob/master/go.mod +[top-badge]: https://img.shields.io/github/languages/top/go-openapi/jsonpointer +[commits-badge]: https://img.shields.io/github/commits-since/go-openapi/jsonpointer/latest diff --git a/vendor/github.com/go-openapi/jsonpointer/SECURITY.md b/vendor/github.com/go-openapi/jsonpointer/SECURITY.md new file mode 100644 index 0000000000..2a7b6f0910 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +This policy outlines the commitment and practices of the go-openapi maintainers regarding security. + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 0.22.x | :white_check_mark: | + +## Reporting a vulnerability + +If you become aware of a security vulnerability that affects the current repository, +please report it privately to the maintainers. + +Please follow the instructions provided by github to +[Privately report a security vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability). + +TL;DR: on Github, navigate to the project's "Security" tab then click on "Report a vulnerability". diff --git a/vendor/github.com/go-openapi/jsonpointer/errors.go b/vendor/github.com/go-openapi/jsonpointer/errors.go index b84343d9d7..8c50dde8bc 100644 --- a/vendor/github.com/go-openapi/jsonpointer/errors.go +++ b/vendor/github.com/go-openapi/jsonpointer/errors.go @@ -1,5 +1,10 @@ +// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package jsonpointer +import "fmt" + type pointerError string func (e pointerError) Error() string { @@ -7,12 +12,24 @@ func (e pointerError) Error() string { } const ( - // ErrPointer is an error raised by the jsonpointer package + // ErrPointer is a sentinel error raised by all errors from this package. ErrPointer pointerError = "JSON pointer error" - // ErrInvalidStart states that a JSON pointer must start with a separator ("/") + // ErrInvalidStart states that a JSON pointer must start with a separator ("/"). ErrInvalidStart pointerError = `JSON pointer must be empty or start with a "` + pointerSeparator - // ErrUnsupportedValueType indicates that a value of the wrong type is being set + // ErrUnsupportedValueType indicates that a value of the wrong type is being set. ErrUnsupportedValueType pointerError = "only structs, pointers, maps and slices are supported for setting values" ) + +func errNoKey(key string) error { + return fmt.Errorf("object has no key %q: %w", key, ErrPointer) +} + +func errOutOfBounds(length, idx int) error { + return fmt.Errorf("index out of bounds array[0,%d] index '%d': %w", length-1, idx, ErrPointer) +} + +func errInvalidReference(token string) error { + return fmt.Errorf("invalid token reference %q: %w", token, ErrPointer) +} diff --git a/vendor/github.com/go-openapi/jsonpointer/pointer.go b/vendor/github.com/go-openapi/jsonpointer/pointer.go index 7513c4763b..7df49af3b9 100644 --- a/vendor/github.com/go-openapi/jsonpointer/pointer.go +++ b/vendor/github.com/go-openapi/jsonpointer/pointer.go @@ -1,28 +1,7 @@ -// Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// author sigu-399 -// author-github https://github.com/sigu-399 -// author-mail sigu.399@gmail.com -// -// repository-name jsonpointer -// repository-desc An implementation of JSON Pointer - Go language -// -// description Main and unique file. -// -// created 25-02-2013 +// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 +// Package jsonpointer provides a golang implementation for json pointers. package jsonpointer import ( @@ -41,29 +20,47 @@ const ( pointerSeparator = `/` ) -var ( - jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem() - jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem() -) - -// JSONPointable is an interface for structs to implement when they need to customize the -// json pointer process +// JSONPointable is an interface for structs to implement, +// when they need to customize the json pointer process or want to avoid the use of reflection. type JSONPointable interface { - JSONLookup(string) (any, error) + // JSONLookup returns a value pointed at this (unescaped) key. + JSONLookup(key string) (any, error) } -// JSONSetable is an interface for structs to implement when they need to customize the -// json pointer process +// JSONSetable is an interface for structs to implement, +// when they need to customize the json pointer process or want to avoid the use of reflection. type JSONSetable interface { - JSONSet(string, any) error + // JSONSet sets the value pointed at the (unescaped) key. + JSONSet(key string, value any) error } -// Pointer is a representation of a json pointer +// Pointer is a representation of a json pointer. +// +// Use [Pointer.Get] to retrieve a value or [Pointer.Set] to set a value. +// +// It works with any go type interpreted as a JSON document, which means: +// +// - if a type implements [JSONPointable], its [JSONPointable.JSONLookup] method is used to resolve [Pointer.Get] +// - if a type implements [JSONSetable], its [JSONPointable.JSONSet] method is used to resolve [Pointer.Set] +// - a go map[K]V is interpreted as an object, with type K assignable to a string +// - a go slice []T is interpreted as an array +// - a go struct is interpreted as an object, with exported fields interpreted as keys +// - promoted fields from an embedded struct are traversed +// - scalars (e.g. int, float64 ...), channels, functions and go arrays cannot be traversed +// +// For struct s resolved by reflection, key mappings honor the conventional struct tag `json`. +// +// Fields that do not specify a `json` tag, or specify an empty one, or are tagged as `json:"-"` are ignored. +// +// # Limitations +// +// - Unlike go standard marshaling, untagged fields do not default to the go field name and are ignored. +// - anonymous fields are not traversed if untagged type Pointer struct { referenceTokens []string } -// New creates a new json pointer for the given string +// New creates a new json pointer from its string representation. func New(jsonPointerString string) (Pointer, error) { var p Pointer err := p.parse(jsonPointerString) @@ -71,34 +68,40 @@ func New(jsonPointerString string) (Pointer, error) { return p, err } -// Get uses the pointer to retrieve a value from a JSON document +// Get uses the pointer to retrieve a value from a JSON document. +// +// It returns the value with its type as a [reflect.Kind] or an error. func (p *Pointer) Get(document any) (any, reflect.Kind, error) { return p.get(document, jsonname.DefaultJSONNameProvider) } -// Set uses the pointer to set a value from a JSON document +// Set uses the pointer to set a value from a data type +// that represent a JSON document. +// +// It returns the updated document. func (p *Pointer) Set(document any, value any) (any, error) { return document, p.set(document, value, jsonname.DefaultJSONNameProvider) } -// DecodedTokens returns the decoded tokens of this JSON pointer +// DecodedTokens returns the decoded (unescaped) tokens of this JSON pointer. func (p *Pointer) DecodedTokens() []string { result := make([]string, 0, len(p.referenceTokens)) - for _, t := range p.referenceTokens { - result = append(result, Unescape(t)) + for _, token := range p.referenceTokens { + result = append(result, Unescape(token)) } + return result } -// IsEmpty returns true if this is an empty json pointer -// this indicates that it points to the root document +// IsEmpty returns true if this is an empty json pointer. +// +// This indicates that it points to the root document. func (p *Pointer) IsEmpty() bool { return len(p.referenceTokens) == 0 } -// Pointer to string representation function +// String representation of a pointer. func (p *Pointer) String() string { - if len(p.referenceTokens) == 0 { return emptyPointer } @@ -137,20 +140,21 @@ func (p *Pointer) Offset(document string) (int64, error) { return offset, nil } -// "Constructor", parses the given string JSON pointer +// "Constructor", parses the given string JSON pointer. func (p *Pointer) parse(jsonPointerString string) error { - var err error - - if jsonPointerString != emptyPointer { - if !strings.HasPrefix(jsonPointerString, pointerSeparator) { - err = errors.Join(ErrInvalidStart, ErrPointer) - } else { - referenceTokens := strings.Split(jsonPointerString, pointerSeparator) - p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...) - } + if jsonPointerString == emptyPointer { + return nil + } + + if !strings.HasPrefix(jsonPointerString, pointerSeparator) { + // non empty pointer must start with "/" + return errors.Join(ErrInvalidStart, ErrPointer) } - return err + referenceTokens := strings.Split(jsonPointerString, pointerSeparator) + p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...) + + return nil } func (p *Pointer) get(node any, nameProvider *jsonname.NameProvider) (any, reflect.Kind, error) { @@ -160,7 +164,7 @@ func (p *Pointer) get(node any, nameProvider *jsonname.NameProvider) (any, refle kind := reflect.Invalid - // Full document when empty + // full document when empty if len(p.referenceTokens) == 0 { return node, kind, nil } @@ -186,101 +190,103 @@ func (p *Pointer) set(node, data any, nameProvider *jsonname.NameProvider) error if knd != reflect.Pointer && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array { return errors.Join( + fmt.Errorf("unexpected type: %T", node), //nolint:err113 // err wrapping is carried out by errors.Join, not fmt.Errorf. ErrUnsupportedValueType, ErrPointer, ) } + l := len(p.referenceTokens) + + // full document when empty + if l == 0 { + return nil + } + if nameProvider == nil { nameProvider = jsonname.DefaultJSONNameProvider } - // Full document when empty - if len(p.referenceTokens) == 0 { - return nil + var decodedToken string + lastIndex := l - 1 + + if lastIndex > 0 { // skip if we only have one token in pointer + for _, token := range p.referenceTokens[:lastIndex] { + decodedToken = Unescape(token) + next, err := p.resolveNodeForToken(node, decodedToken, nameProvider) + if err != nil { + return err + } + + node = next + } } - lastI := len(p.referenceTokens) - 1 - for i, token := range p.referenceTokens { - isLastToken := i == lastI - decodedToken := Unescape(token) + // last token + decodedToken = Unescape(p.referenceTokens[lastIndex]) - if isLastToken { + return setSingleImpl(node, data, decodedToken, nameProvider) +} - return setSingleImpl(node, data, decodedToken, nameProvider) +func (p *Pointer) resolveNodeForToken(node any, decodedToken string, nameProvider *jsonname.NameProvider) (next any, err error) { + // check for nil during traversal + if isNil(node) { + return nil, fmt.Errorf("cannot traverse through nil value at %q: %w", decodedToken, ErrPointer) + } + + pointable, ok := node.(JSONPointable) + if ok { + r, err := pointable.JSONLookup(decodedToken) + if err != nil { + return nil, err } - // Check for nil during traversal - if isNil(node) { - return fmt.Errorf("cannot traverse through nil value at %q: %w", decodedToken, ErrPointer) + fld := reflect.ValueOf(r) + if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Pointer { + return fld.Addr().Interface(), nil } - rValue := reflect.Indirect(reflect.ValueOf(node)) - kind := rValue.Kind() + return r, nil + } - if rValue.Type().Implements(jsonPointableType) { - r, err := node.(JSONPointable).JSONLookup(decodedToken) - if err != nil { - return err - } - fld := reflect.ValueOf(r) - if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Pointer { - node = fld.Addr().Interface() - continue - } - node = r - continue + rValue := reflect.Indirect(reflect.ValueOf(node)) + kind := rValue.Kind() + + switch kind { + case reflect.Struct: + nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) + if !ok { + return nil, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) } - switch kind { //nolint:exhaustive - case reflect.Struct: - nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) - if !ok { - return fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) - } - fld := rValue.FieldByName(nm) - if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Pointer { - node = fld.Addr().Interface() - continue - } - node = fld.Interface() + return typeFromValue(rValue.FieldByName(nm)), nil - case reflect.Map: - kv := reflect.ValueOf(decodedToken) - mv := rValue.MapIndex(kv) + case reflect.Map: + kv := reflect.ValueOf(decodedToken) + mv := rValue.MapIndex(kv) - if !mv.IsValid() { - return fmt.Errorf("object has no key %q: %w", decodedToken, ErrPointer) - } - if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Pointer { - node = mv.Addr().Interface() - continue - } - node = mv.Interface() + if !mv.IsValid() { + return nil, errNoKey(decodedToken) + } - case reflect.Slice: - tokenIndex, err := strconv.Atoi(decodedToken) - if err != nil { - return err - } - sLength := rValue.Len() - if tokenIndex < 0 || tokenIndex >= sLength { - return fmt.Errorf("index out of bounds array[0,%d] index '%d': %w", sLength, tokenIndex, ErrPointer) - } + return typeFromValue(mv), nil - elem := rValue.Index(tokenIndex) - if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Pointer { - node = elem.Addr().Interface() - continue - } - node = elem.Interface() + case reflect.Slice: + tokenIndex, err := strconv.Atoi(decodedToken) + if err != nil { + return nil, errors.Join(err, ErrPointer) + } - default: - return fmt.Errorf("invalid token reference %q: %w", decodedToken, ErrPointer) + sLength := rValue.Len() + if tokenIndex < 0 || tokenIndex >= sLength { + return nil, errOutOfBounds(sLength, tokenIndex) } - } - return nil + return typeFromValue(rValue.Index(tokenIndex)), nil + + default: + return nil, errInvalidReference(decodedToken) + } } func isNil(input any) bool { @@ -289,7 +295,7 @@ func isNil(input any) bool { } kind := reflect.TypeOf(input).Kind() - switch kind { //nolint:exhaustive + switch kind { case reflect.Pointer, reflect.Map, reflect.Slice, reflect.Chan: return reflect.ValueOf(input).IsNil() default: @@ -297,12 +303,20 @@ func isNil(input any) bool { } } -// GetForToken gets a value for a json pointer token 1 level deep +func typeFromValue(v reflect.Value) any { + if v.CanAddr() && v.Kind() != reflect.Interface && v.Kind() != reflect.Map && v.Kind() != reflect.Slice && v.Kind() != reflect.Pointer { + return v.Addr().Interface() + } + + return v.Interface() +} + +// GetForToken gets a value for a json pointer token 1 level deep. func GetForToken(document any, decodedToken string) (any, reflect.Kind, error) { return getSingleImpl(document, decodedToken, jsonname.DefaultJSONNameProvider) } -// SetForToken gets a value for a json pointer token 1 level deep +// SetForToken sets a value for a json pointer token 1 level deep. func SetForToken(document any, decodedToken string, value any) (any, error) { return document, setSingleImpl(document, value, decodedToken, jsonname.DefaultJSONNameProvider) } @@ -325,13 +339,15 @@ func getSingleImpl(node any, decodedToken string, nameProvider *jsonname.NamePro return getSingleImpl(*typed, decodedToken, nameProvider) } - switch kind { //nolint:exhaustive + switch kind { case reflect.Struct: nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) if !ok { return nil, kind, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) } + fld := rValue.FieldByName(nm) + return fld.Interface(), kind, nil case reflect.Map: @@ -341,78 +357,99 @@ func getSingleImpl(node any, decodedToken string, nameProvider *jsonname.NamePro if mv.IsValid() { return mv.Interface(), kind, nil } - return nil, kind, fmt.Errorf("object has no key %q: %w", decodedToken, ErrPointer) + + return nil, kind, errNoKey(decodedToken) case reflect.Slice: tokenIndex, err := strconv.Atoi(decodedToken) if err != nil { - return nil, kind, err + return nil, kind, errors.Join(err, ErrPointer) } sLength := rValue.Len() if tokenIndex < 0 || tokenIndex >= sLength { - return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d': %w", sLength-1, tokenIndex, ErrPointer) + return nil, kind, errOutOfBounds(sLength, tokenIndex) } elem := rValue.Index(tokenIndex) return elem.Interface(), kind, nil default: - return nil, kind, fmt.Errorf("invalid token reference %q: %w", decodedToken, ErrPointer) + return nil, kind, errInvalidReference(decodedToken) } } func setSingleImpl(node, data any, decodedToken string, nameProvider *jsonname.NameProvider) error { - rValue := reflect.Indirect(reflect.ValueOf(node)) - - // Check for nil to prevent panic when calling rValue.Type() + // check for nil to prevent panic when calling rValue.Type() if isNil(node) { return fmt.Errorf("cannot set field %q on nil value: %w", decodedToken, ErrPointer) } - if ns, ok := node.(JSONSetable); ok { // pointer impl + if ns, ok := node.(JSONSetable); ok { return ns.JSONSet(decodedToken, data) } - if rValue.Type().Implements(jsonSetableType) { - return node.(JSONSetable).JSONSet(decodedToken, data) - } + rValue := reflect.Indirect(reflect.ValueOf(node)) - switch rValue.Kind() { //nolint:exhaustive + switch rValue.Kind() { case reflect.Struct: nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) if !ok { return fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) } + fld := rValue.FieldByName(nm) - if fld.IsValid() { - fld.Set(reflect.ValueOf(data)) + if !fld.CanSet() { + return fmt.Errorf("can't set struct field %s to %v: %w", nm, data, ErrPointer) + } + + value := reflect.ValueOf(data) + valueType := value.Type() + assignedType := fld.Type() + + if !valueType.AssignableTo(assignedType) { + return fmt.Errorf("can't set value with type %T to field %s with type %v: %w", data, nm, assignedType, ErrPointer) } + + fld.Set(value) + return nil case reflect.Map: kv := reflect.ValueOf(decodedToken) rValue.SetMapIndex(kv, reflect.ValueOf(data)) + return nil case reflect.Slice: tokenIndex, err := strconv.Atoi(decodedToken) if err != nil { - return err + return errors.Join(err, ErrPointer) } + sLength := rValue.Len() if tokenIndex < 0 || tokenIndex >= sLength { - return fmt.Errorf("index out of bounds array[0,%d] index '%d': %w", sLength, tokenIndex, ErrPointer) + return errOutOfBounds(sLength, tokenIndex) } elem := rValue.Index(tokenIndex) if !elem.CanSet() { return fmt.Errorf("can't set slice index %s to %v: %w", decodedToken, data, ErrPointer) } - elem.Set(reflect.ValueOf(data)) + + value := reflect.ValueOf(data) + valueType := value.Type() + assignedType := elem.Type() + + if !valueType.AssignableTo(assignedType) { + return fmt.Errorf("can't set value with type %T to slice element %d with type %v: %w", data, tokenIndex, assignedType, ErrPointer) + } + + elem.Set(value) + return nil default: - return fmt.Errorf("invalid token reference %q: %w", decodedToken, ErrPointer) + return errInvalidReference(decodedToken) } } @@ -443,13 +480,14 @@ func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) { return 0, fmt.Errorf("invalid token %#v: %w", tk, ErrPointer) } } + return 0, fmt.Errorf("token reference %q not found: %w", decodedToken, ErrPointer) } func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) { idx, err := strconv.Atoi(decodedToken) if err != nil { - return 0, fmt.Errorf("token reference %q is not a number: %v: %w", decodedToken, err, ErrPointer) + return 0, fmt.Errorf("token reference %q is not a number: %w: %w", decodedToken, err, ErrPointer) } var i int for i = 0; i < idx && dec.More(); i++ { @@ -475,10 +513,12 @@ func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) { if !dec.More() { return 0, fmt.Errorf("token reference %q not found: %w", decodedToken, ErrPointer) } + return dec.InputOffset(), nil } // drainSingle drains a single level of object or array. +// // The decoder has to guarantee the beginning delim (i.e. '{' or '[') has been consumed. func drainSingle(dec *json.Decoder) error { for dec.More() { @@ -500,14 +540,15 @@ func drainSingle(dec *json.Decoder) error { } } - // Consumes the ending delim + // consumes the ending delim if _, err := dec.Token(); err != nil { return err } + return nil } -// Specific JSON pointer encoding here +// JSON pointer encoding: // ~0 => ~ // ~1 => / // ... and vice versa @@ -520,16 +561,23 @@ const ( ) var ( - encRefTokReplacer = strings.NewReplacer(encRefTok1, decRefTok1, encRefTok0, decRefTok0) - decRefTokReplacer = strings.NewReplacer(decRefTok1, encRefTok1, decRefTok0, encRefTok0) + encRefTokReplacer = strings.NewReplacer(encRefTok1, decRefTok1, encRefTok0, decRefTok0) //nolint:gochecknoglobals // it's okay to declare a replacer as a private global + decRefTokReplacer = strings.NewReplacer(decRefTok1, encRefTok1, decRefTok0, encRefTok0) //nolint:gochecknoglobals // it's okay to declare a replacer as a private global ) -// Unescape unescapes a json pointer reference token string to the original representation +// Unescape unescapes a json pointer reference token string to the original representation. func Unescape(token string) string { return encRefTokReplacer.Replace(token) } -// Escape escapes a pointer reference token string +// Escape escapes a pointer reference token string. +// +// The JSONPointer specification defines "/" as a separator and "~" as an escape prefix. +// +// Keys containing such characters are escaped with the following rules: +// +// - "~" is escaped as "~0" +// - "/" is escaped as "~1" func Escape(token string) string { return decRefTokReplacer.Replace(token) } diff --git a/vendor/github.com/go-openapi/jsonreference/.cliff.toml b/vendor/github.com/go-openapi/jsonreference/.cliff.toml new file mode 100644 index 0000000000..702629f5dc --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/.cliff.toml @@ -0,0 +1,181 @@ +# git-cliff ~ configuration file +# https://git-cliff.org/docs/configuration + +[changelog] +header = """ +""" + +footer = """ + +----- + +**[{{ remote.github.repo }}]({{ self::remote_url() }}) license terms** + +[![License][license-badge]][license-url] + +[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg +[license-url]: {{ self::remote_url() }}/?tab=Apache-2.0-1-ov-file#readme + +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} +""" + +body = """ +{%- if version %} +## [{{ version | trim_start_matches(pat="v") }}]({{ self::remote_url() }}/tree/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }} +{%- else %} +## [unreleased] +{%- endif %} +{%- if message %} + {%- raw %}\n{% endraw %} +{{ message }} + {%- raw %}\n{% endraw %} +{%- endif %} +{%- if version %} + {%- if previous.version %} + +**Full Changelog**: <{{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }}> + {%- endif %} +{%- else %} + {%- raw %}\n{% endraw %} +{%- endif %} + +{%- if statistics %}{% if statistics.commit_count %} + {%- raw %}\n{% endraw %} +{{ statistics.commit_count }} commits in this release. + {%- raw %}\n{% endraw %} +{%- endif %}{% endif %} +----- + +{%- for group, commits in commits | group_by(attribute="group") %} + {%- raw %}\n{% endraw %} +### {{ group | upper_first }} + {%- raw %}\n{% endraw %} + {%- for commit in commits %} + {%- if commit.remote.pr_title %} + {%- set commit_message = commit.remote.pr_title %} + {%- else %} + {%- set commit_message = commit.message %} + {%- endif %} +* {{ commit_message | split(pat="\n") | first | trim }} + {%- if commit.remote.username %} +{%- raw %} {% endraw %}by [@{{ commit.remote.username }}](https://github.com/{{ commit.remote.username }}) + {%- endif %} + {%- if commit.remote.pr_number %} +{%- raw %} {% endraw %}in [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}) + {%- endif %} +{%- raw %} {% endraw %}[...]({{ self::remote_url() }}/commit/{{ commit.id }}) + {%- endfor %} +{%- endfor %} + +{%- if github %} +{%- raw %}\n{% endraw -%} + {%- set all_contributors = github.contributors | length %} + {%- if github.contributors | filter(attribute="username", value="dependabot[bot]") | length < all_contributors %} +----- + +### People who contributed to this release + {% endif %} + {%- for contributor in github.contributors | filter(attribute="username") | sort(attribute="username") %} + {%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %} +* [@{{ contributor.username }}](https://github.com/{{ contributor.username }}) + {%- endif %} + {%- endfor %} + + {% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} +----- + {%- raw %}\n{% endraw %} + +### New Contributors + {%- endif %} + + {%- for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} + {%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %} +* @{{ contributor.username }} made their first contribution + {%- if contributor.pr_number %} + in [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ + {%- endif %} + {%- endif %} + {%- endfor %} +{%- endif %} + +{%- raw %}\n{% endraw %} + +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} +""" +# Remove leading and trailing whitespaces from the changelog's body. +trim = true +# Render body even when there are no releases to process. +render_always = true +# An array of regex based postprocessors to modify the changelog. +postprocessors = [ + # Replace the placeholder with a URL. + #{ pattern = '', replace = "https://github.com/orhun/git-cliff" }, +] +# output file path +# output = "test.md" + +[git] +# Parse commits according to the conventional commits specification. +# See https://www.conventionalcommits.org +conventional_commits = false +# Exclude commits that do not match the conventional commits specification. +filter_unconventional = false +# Require all commits to be conventional. +# Takes precedence over filter_unconventional. +require_conventional = false +# Split commits on newlines, treating each line as an individual commit. +split_commits = false +# An array of regex based parsers to modify commit messages prior to further processing. +commit_preprocessors = [ + # Replace issue numbers with link templates to be updated in `changelog.postprocessors`. + #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, + # Check spelling of the commit message using https://github.com/crate-ci/typos. + # If the spelling is incorrect, it will be fixed automatically. + #{ pattern = '.*', replace_command = 'typos --write-changes -' } +] +# Prevent commits that are breaking from being excluded by commit parsers. +protect_breaking_commits = false +# An array of regex based parsers for extracting data from the commit message. +# Assigns commits to groups. +# Optionally sets the commit's scope and can decide to exclude commits from further processing. +commit_parsers = [ + { message = "^[Cc]hore\\([Rr]elease\\): prepare for", skip = true }, + { message = "(^[Mm]erge)|([Mm]erge conflict)", skip = true }, + { field = "author.name", pattern = "dependabot*", group = "Updates" }, + { message = "([Ss]ecurity)|([Vv]uln)", group = "Security" }, + { body = "(.*[Ss]ecurity)|([Vv]uln)", group = "Security" }, + { message = "([Cc]hore\\(lint\\))|(style)|(lint)|(codeql)|(golangci)", group = "Code quality" }, + { message = "(^[Dd]oc)|((?i)readme)|(badge)|(typo)|(documentation)", group = "Documentation" }, + { message = "(^[Ff]eat)|(^[Ee]nhancement)", group = "Implemented enhancements" }, + { message = "(^ci)|(\\(ci\\))|(fixup\\s+ci)|(fix\\s+ci)|(license)|(example)", group = "Miscellaneous tasks" }, + { message = "^test", group = "Testing" }, + { message = "(^fix)|(panic)", group = "Fixed bugs" }, + { message = "(^refact)|(rework)", group = "Refactor" }, + { message = "(^[Pp]erf)|(performance)", group = "Performance" }, + { message = "(^[Cc]hore)", group = "Miscellaneous tasks" }, + { message = "^[Rr]evert", group = "Reverted changes" }, + { message = "(upgrade.*?go)|(go\\s+version)", group = "Updates" }, + { message = ".*", group = "Other" }, +] +# Exclude commits that are not matched by any commit parser. +filter_commits = false +# An array of link parsers for extracting external references, and turning them into URLs, using regex. +link_parsers = [] +# Include only the tags that belong to the current branch. +use_branch_tags = false +# Order releases topologically instead of chronologically. +topo_order = false +# Order releases topologically instead of chronologically. +topo_order_commits = true +# Order of commits in each group/release within the changelog. +# Allowed values: newest, oldest +sort_commits = "newest" +# Process submodules commits +recurse_submodules = false + +#[remote.github] +#owner = "go-openapi" diff --git a/vendor/github.com/go-openapi/jsonreference/.editorconfig b/vendor/github.com/go-openapi/jsonreference/.editorconfig new file mode 100644 index 0000000000..3152da69a5 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/vendor/github.com/go-openapi/jsonreference/.golangci.yml b/vendor/github.com/go-openapi/jsonreference/.golangci.yml index 7cea1af8b5..fdae591bce 100644 --- a/vendor/github.com/go-openapi/jsonreference/.golangci.yml +++ b/vendor/github.com/go-openapi/jsonreference/.golangci.yml @@ -2,34 +2,17 @@ version: "2" linters: default: all disable: - - cyclop - depguard - - errchkjson - - errorlint - - exhaustruct - - forcetypeassert - funlen - - gochecknoglobals - - gochecknoinits - - gocognit - - godot - godox - - gosmopolitan - - inamedparam - #- intrange # disabled while < go1.22 - - ireturn - - lll - - musttag - - nestif + - exhaustruct - nlreturn - nonamedreturns - noinlineerr - paralleltest - recvcheck - testpackage - - thelper - tparallel - - unparam - varnamelen - whitespace - wrapcheck @@ -41,8 +24,15 @@ linters: goconst: min-len: 2 min-occurrences: 3 + cyclop: + max-complexity: 20 gocyclo: - min-complexity: 45 + min-complexity: 20 + exhaustive: + default-signifies-exhaustive: true + default-case-required: true + lll: + line-length: 180 exclusions: generated: lax presets: @@ -58,6 +48,7 @@ formatters: enable: - gofmt - goimports + - gofumpt exclusions: generated: lax paths: diff --git a/vendor/github.com/go-openapi/jsonreference/CONTRIBUTORS.md b/vendor/github.com/go-openapi/jsonreference/CONTRIBUTORS.md new file mode 100644 index 0000000000..9907d5d212 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/CONTRIBUTORS.md @@ -0,0 +1,21 @@ +# Contributors + +- Repository: ['go-openapi/jsonreference'] + +| Total Contributors | Total Contributions | +| --- | --- | +| 9 | 68 | + +| Username | All Time Contribution Count | All Commits | +| --- | --- | --- | +| @fredbi | 31 | https://github.com/go-openapi/jsonreference/commits?author=fredbi | +| @casualjim | 25 | https://github.com/go-openapi/jsonreference/commits?author=casualjim | +| @youyuanwu | 5 | https://github.com/go-openapi/jsonreference/commits?author=youyuanwu | +| @olivierlemasle | 2 | https://github.com/go-openapi/jsonreference/commits?author=olivierlemasle | +| @apelisse | 1 | https://github.com/go-openapi/jsonreference/commits?author=apelisse | +| @gbjk | 1 | https://github.com/go-openapi/jsonreference/commits?author=gbjk | +| @honza | 1 | https://github.com/go-openapi/jsonreference/commits?author=honza | +| @Neo2308 | 1 | https://github.com/go-openapi/jsonreference/commits?author=Neo2308 | +| @erraggy | 1 | https://github.com/go-openapi/jsonreference/commits?author=erraggy | + + _this file was generated by the [Contributors GitHub Action](https://github.com/github/contributors)_ diff --git a/vendor/github.com/go-openapi/jsonreference/NOTICE b/vendor/github.com/go-openapi/jsonreference/NOTICE index f9ad7e0f7a..f3b51939a9 100644 --- a/vendor/github.com/go-openapi/jsonreference/NOTICE +++ b/vendor/github.com/go-openapi/jsonreference/NOTICE @@ -8,12 +8,15 @@ by the go-swagger and go-openapi maintainers ("go-swagger maintainers"). Licensed under the Apache License, Version 2.0 (the "License"); you may not use this software except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0. + This software is copied from, derived from, and inspired by other original software products. It ships with copies of other software which license terms are recalled below. -The original sofware was authored on 25-02-2013 by sigu-399 (https://github.com/sigu-399, sigu.399@gmail.com). +The original software was authored on 25-02-2013 by sigu-399 (https://github.com/sigu-399, sigu.399@gmail.com). github.com/sigh-399/jsonpointer =========================== diff --git a/vendor/github.com/go-openapi/jsonreference/README.md b/vendor/github.com/go-openapi/jsonreference/README.md index 2274a4b78f..d479dbdc73 100644 --- a/vendor/github.com/go-openapi/jsonreference/README.md +++ b/vendor/github.com/go-openapi/jsonreference/README.md @@ -1,18 +1,39 @@ -# gojsonreference [![Build Status](https://github.com/go-openapi/jsonreference/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/jsonreference/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/jsonreference/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonreference) +# jsonreference -[![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) -[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonreference/master/LICENSE) -[![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/jsonreference.svg)](https://pkg.go.dev/github.com/go-openapi/jsonreference) -[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/jsonreference)](https://goreportcard.com/report/github.com/go-openapi/jsonreference) + +[![Tests][test-badge]][test-url] [![Coverage][cov-badge]][cov-url] [![CI vuln scan][vuln-scan-badge]][vuln-scan-url] [![CodeQL][codeql-badge]][codeql-url] + + + +[![Release][release-badge]][release-url] [![Go Report Card][gocard-badge]][gocard-url] [![CodeFactor Grade][codefactor-badge]][codefactor-url] [![License][license-badge]][license-url] + + +[![GoDoc][godoc-badge]][godoc-url] [![Slack Channel][slack-logo]![slack-badge]][slack-url] [![go version][goversion-badge]][goversion-url] ![Top language][top-badge] ![Commits since latest release][commits-badge] -An implementation of JSON Reference - Go language +--- + +An implementation of JSON Reference for golang. ## Status -Feature complete. Stable API + +API is stable. + +## Import this library in your project + +```cmd +go get github.com/go-openapi/jsonreference +``` ## Dependencies + * https://github.com/go-openapi/jsonpointer +## Basic usage + +## Change log + +See + ## References * http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07 @@ -24,3 +45,55 @@ This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE). See the license [NOTICE](./NOTICE), which recalls the licensing terms of all the pieces of software on top of which it has been built. + +## Other documentation + +* [All-time contributors](./CONTRIBUTORS.md) +* [Contributing guidelines](.github/CONTRIBUTING.md) +* [Maintainers documentation](docs/MAINTAINERS.md) +* [Code style](docs/STYLE.md) + +## Cutting a new release + +Maintainers can cut a new release by either: + +* running [this workflow](https://github.com/go-openapi/jsonreference/actions/workflows/bump-release.yml) +* or pushing a semver tag + * signed tags are preferred + * The tag message is prepended to release notes + + +[test-badge]: https://github.com/go-openapi/jsonreference/actions/workflows/go-test.yml/badge.svg +[test-url]: https://github.com/go-openapi/jsonreference/actions/workflows/go-test.yml +[cov-badge]: https://codecov.io/gh/go-openapi/jsonreference/branch/master/graph/badge.svg +[cov-url]: https://codecov.io/gh/go-openapi/jsonreference +[vuln-scan-badge]: https://github.com/go-openapi/jsonreference/actions/workflows/scanner.yml/badge.svg +[vuln-scan-url]: https://github.com/go-openapi/jsonreference/actions/workflows/scanner.yml +[codeql-badge]: https://github.com/go-openapi/jsonreference/actions/workflows/codeql.yml/badge.svg +[codeql-url]: https://github.com/go-openapi/jsonreference/actions/workflows/codeql.yml + +[release-badge]: https://badge.fury.io/gh/go-openapi%2Fjsonreference.svg +[release-url]: https://badge.fury.io/gh/go-openapi%2Fjsonreference +[gomod-badge]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Fjsonreference.svg +[gomod-url]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Fjsonreference + +[gocard-badge]: https://goreportcard.com/badge/github.com/go-openapi/jsonreference +[gocard-url]: https://goreportcard.com/report/github.com/go-openapi/jsonreference +[codefactor-badge]: https://img.shields.io/codefactor/grade/github/go-openapi/jsonreference +[codefactor-url]: https://www.codefactor.io/repository/github/go-openapi/jsonreference + +[doc-badge]: https://img.shields.io/badge/doc-site-blue?link=https%3A%2F%2Fgoswagger.io%2Fgo-openapi%2F +[doc-url]: https://goswagger.io/go-openapi +[godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/jsonreference +[godoc-url]: http://pkg.go.dev/github.com/go-openapi/jsonreference +[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png +[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM +[slack-url]: https://goswagger.slack.com/archives/C04R30YMU + +[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg +[license-url]: https://github.com/go-openapi/jsonreference/?tab=Apache-2.0-1-ov-file#readme + +[goversion-badge]: https://img.shields.io/github/go-mod/go-version/go-openapi/jsonreference +[goversion-url]: https://github.com/go-openapi/jsonreference/blob/master/go.mod +[top-badge]: https://img.shields.io/github/languages/top/go-openapi/jsonreference +[commits-badge]: https://img.shields.io/github/commits-since/go-openapi/jsonreference/latest diff --git a/vendor/github.com/go-openapi/jsonreference/SECURITY.md b/vendor/github.com/go-openapi/jsonreference/SECURITY.md new file mode 100644 index 0000000000..2a7b6f0910 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonreference/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +This policy outlines the commitment and practices of the go-openapi maintainers regarding security. + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 0.22.x | :white_check_mark: | + +## Reporting a vulnerability + +If you become aware of a security vulnerability that affects the current repository, +please report it privately to the maintainers. + +Please follow the instructions provided by github to +[Privately report a security vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability). + +TL;DR: on Github, navigate to the project's "Security" tab then click on "Report a vulnerability". diff --git a/vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go b/vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go index ca79391dcf..a08b47320e 100644 --- a/vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go +++ b/vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go @@ -14,9 +14,11 @@ const ( defaultHTTPSPort = ":443" ) -// Regular expressions used by the normalizations -var rxPort = regexp.MustCompile(`(:\d+)/?$`) -var rxDupSlashes = regexp.MustCompile(`/{2,}`) +// Regular expressions used by the normalizations. +var ( + rxPort = regexp.MustCompile(`(:\d+)/?$`) + rxDupSlashes = regexp.MustCompile(`/{2,}`) +) // NormalizeURL will normalize the specified URL // This was added to replace a previous call to the no longer maintained purell library: diff --git a/vendor/github.com/go-openapi/jsonreference/reference.go b/vendor/github.com/go-openapi/jsonreference/reference.go index 33d4798cad..6e3ae49951 100644 --- a/vendor/github.com/go-openapi/jsonreference/reference.go +++ b/vendor/github.com/go-openapi/jsonreference/reference.go @@ -18,7 +18,7 @@ const ( var ErrChildURL = errors.New("child url is nil") -// Ref represents a json reference object +// Ref represents a json reference object. type Ref struct { referenceURL *url.URL referencePointer jsonpointer.Pointer @@ -30,7 +30,7 @@ type Ref struct { HasFullFilePath bool } -// New creates a new reference for the given string +// New creates a new reference for the given string. func New(jsonReferenceString string) (Ref, error) { var r Ref err := r.parse(jsonReferenceString) @@ -38,7 +38,7 @@ func New(jsonReferenceString string) (Ref, error) { } // MustCreateRef parses the ref string and panics when it's invalid. -// Use the New method for a version that returns an error +// Use the New method for a version that returns an error. func MustCreateRef(ref string) Ref { r, err := New(ref) if err != nil { @@ -48,17 +48,17 @@ func MustCreateRef(ref string) Ref { return r } -// GetURL gets the URL for this reference +// GetURL gets the URL for this reference. func (r *Ref) GetURL() *url.URL { return r.referenceURL } -// GetPointer gets the json pointer for this reference +// GetPointer gets the json pointer for this reference. func (r *Ref) GetPointer() *jsonpointer.Pointer { return &r.referencePointer } -// String returns the best version of the url for this reference +// String returns the best version of the url for this reference. func (r *Ref) String() string { if r.referenceURL != nil { return r.referenceURL.String() @@ -71,7 +71,7 @@ func (r *Ref) String() string { return r.referencePointer.String() } -// IsRoot returns true if this reference is a root document +// IsRoot returns true if this reference is a root document. func (r *Ref) IsRoot() bool { return r.referenceURL != nil && !r.IsCanonical() && @@ -79,13 +79,13 @@ func (r *Ref) IsRoot() bool { r.referenceURL.Fragment == "" } -// IsCanonical returns true when this pointer starts with http(s):// or file:// +// IsCanonical returns true when this pointer starts with http(s):// or file://. func (r *Ref) IsCanonical() bool { return (r.HasFileScheme && r.HasFullFilePath) || (!r.HasFileScheme && r.HasFullURL) } // Inherits creates a new reference from a parent and a child -// If the child cannot inherit from the parent, an error is returned +// If the child cannot inherit from the parent, an error is returned. func (r *Ref) Inherits(child Ref) (*Ref, error) { childURL := child.GetURL() parentURL := r.GetURL() @@ -103,7 +103,7 @@ func (r *Ref) Inherits(child Ref) (*Ref, error) { return &ref, nil } -// "Constructor", parses the given string JSON reference +// "Constructor", parses the given string JSON reference. func (r *Ref) parse(jsonReferenceString string) error { parsed, err := url.Parse(jsonReferenceString) if err != nil { diff --git a/vendor/github.com/go-openapi/swag/.golangci.yml b/vendor/github.com/go-openapi/swag/.golangci.yml index 4129e7e57e..126264a6b8 100644 --- a/vendor/github.com/go-openapi/swag/.golangci.yml +++ b/vendor/github.com/go-openapi/swag/.golangci.yml @@ -21,6 +21,7 @@ linters: - ireturn - lll - musttag + - modernize - nestif - nlreturn - nonamedreturns diff --git a/vendor/github.com/go-openapi/swag/README.md b/vendor/github.com/go-openapi/swag/README.md index 786b92fd3a..371fd55fdc 100644 --- a/vendor/github.com/go-openapi/swag/README.md +++ b/vendor/github.com/go-openapi/swag/README.md @@ -18,6 +18,7 @@ You may also use it standalone for your projects. * [Contents](#contents) * [Dependencies](#dependencies) * [Release Notes](#release-notes) +* [Licensing](#licensing) * [Note to contributors](#note-to-contributors) * [TODOs, suggestions and plans](#todos-suggestions-and-plans) @@ -62,6 +63,42 @@ dependencies outside of the standard library. ## Release notes +### v0.25.4 + +** mangling** + +Bug fix + +* [x] mangler may panic with pluralized overlapping initialisms + +Tests + +* [x] introduced fuzz tests + +### v0.25.3 + +** mangling** + +Bug fix + +* [x] mangler may panic with pluralized initialisms + +### v0.25.2 + +Minor changes due to internal maintenance that don't affect the behavior of the library. + +* [x] removed indirect test dependencies by switching all tests to `go-openapi/testify`, + a fork of `stretch/testify` with zero-dependencies. +* [x] improvements to CI to catch test reports. +* [x] modernized licensing annotations in source code, using the more compact SPDX annotations + rather than the full license terms. +* [x] simplified a bit JSON & YAML testing by using newly available assertions +* started the journey to an OpenSSF score card badge: + * [x] explicited permissions in CI workflows + * [x] published security policy + * pinned dependencies to github actions + * introduced fuzzing in tests + ### v0.25.1 * fixes a data race that could occur when using the standard library implementation of a JSON ordered map @@ -74,7 +111,7 @@ dependencies outside of the standard library. * removes the dependency to `mailru/easyjson` by default (#68) * functionality remains the same, but performance may somewhat degrade for applications that relied on `easyjson` - * users of the JSON or YAML utilities who want to use `easyjson` as their prefered JSON serializer library + * users of the JSON or YAML utilities who want to use `easyjson` as their preferred JSON serializer library will be able to do so by registering this the corresponding JSON adapter at runtime. See below. * ordered keys in JSON and YAML objects: this feature used to rely solely on `easyjson`. With this release, an implementation relying on the standard `encoding/json` is provided. @@ -96,10 +133,13 @@ Moving forward, we want to : The following would maintain how JSON utilities proposed by `swag` used work, up to `v0.24.1`. ```go - import "github.com/go-openapi/swag/jsonutils/adapters/easyjson/json" + import ( + "github.com/go-openapi/swag/jsonutils/adapters" + easyjson "github.com/go-openapi/swag/jsonutils/adapters/easyjson/json" + ) func init() { - json.Register() + easyjson.Register(adapters.Registry) } ``` @@ -107,6 +147,9 @@ Subsequent calls to `jsonutils.ReadJSON()` or `jsonutils.WriteJSON()` will switc whenever the passed data structures implement the `easyjson.Unmarshaler` or `easyjson.Marshaler` respectively, or fallback to the standard library. +For more details, you may also look at our +[integration tests](jsonutils/adapters/testintegration/integration_suite_test.go#29). + ### v0.24.0 With this release, we have largely modernized the API of `swag`: @@ -133,6 +176,10 @@ With this release, we have largely modernized the API of `swag`: --- +## Licensing + +This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE). + ## Note to contributors A mono-repo structure comes with some unavoidable extra pains... diff --git a/vendor/github.com/go-openapi/swag/SECURITY.md b/vendor/github.com/go-openapi/swag/SECURITY.md new file mode 100644 index 0000000000..72296a8313 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +This policy outlines the commitment and practices of the go-openapi maintainers regarding security. + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 0.25.x | :white_check_mark: | + +## Reporting a vulnerability + +If you become aware of a security vulnerability that affects the current repository, +please report it privately to the maintainers. + +Please follow the instructions provided by github to +[Privately report a security vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability). + +TL;DR: on Github, navigate to the project's "Security" tab then click on "Report a vulnerability". diff --git a/vendor/github.com/go-openapi/swag/TODO.md b/vendor/github.com/go-openapi/swag/TODO.md deleted file mode 100644 index 129888038b..0000000000 --- a/vendor/github.com/go-openapi/swag/TODO.md +++ /dev/null @@ -1 +0,0 @@ -fix data race https://github.com/go-openapi/swag/actions/runs/17989156861/job/51174860188 diff --git a/vendor/github.com/go-openapi/swag/cmdutils/cmd_utils.go b/vendor/github.com/go-openapi/swag/cmdutils/cmd_utils.go index bc01ec2bb9..6c7bbb26f0 100644 --- a/vendor/github.com/go-openapi/swag/cmdutils/cmd_utils.go +++ b/vendor/github.com/go-openapi/swag/cmdutils/cmd_utils.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package cmdutils @@ -20,5 +9,5 @@ package cmdutils type CommandLineOptionsGroup struct { ShortDescription string LongDescription string - Options interface{} + Options any } diff --git a/vendor/github.com/go-openapi/swag/cmdutils/doc.go b/vendor/github.com/go-openapi/swag/cmdutils/doc.go index 63ac1d17ee..31f2c37538 100644 --- a/vendor/github.com/go-openapi/swag/cmdutils/doc.go +++ b/vendor/github.com/go-openapi/swag/cmdutils/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package cmdutils brings helpers for CLIs produced by go-openapi package cmdutils diff --git a/vendor/github.com/go-openapi/swag/cmdutils_iface.go b/vendor/github.com/go-openapi/swag/cmdutils_iface.go index 1eaf36f157..bd0c1fc128 100644 --- a/vendor/github.com/go-openapi/swag/cmdutils_iface.go +++ b/vendor/github.com/go-openapi/swag/cmdutils_iface.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package swag diff --git a/vendor/github.com/go-openapi/swag/conv/convert.go b/vendor/github.com/go-openapi/swag/conv/convert.go index b9b8698543..f205c39134 100644 --- a/vendor/github.com/go-openapi/swag/conv/convert.go +++ b/vendor/github.com/go-openapi/swag/conv/convert.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package conv diff --git a/vendor/github.com/go-openapi/swag/conv/convert_types.go b/vendor/github.com/go-openapi/swag/conv/convert_types.go index 423e8663f8..cf4c6495eb 100644 --- a/vendor/github.com/go-openapi/swag/conv/convert_types.go +++ b/vendor/github.com/go-openapi/swag/conv/convert_types.go @@ -1,20 +1,13 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package conv -// The original version of this file, eons ago, was taken from the aws go sdk +// Unlicensed credits (idea, concept) +// +// The idea to convert values to pointers and the other way around, was inspired, eons ago, by the aws go sdk. +// +// Nowadays, all sensible API sdk's expose a similar functionality. // Pointer returns a pointer to the value passed in. func Pointer[T any](v T) *T { diff --git a/vendor/github.com/go-openapi/swag/conv/doc.go b/vendor/github.com/go-openapi/swag/conv/doc.go index b02711f422..1bd6ead6e2 100644 --- a/vendor/github.com/go-openapi/swag/conv/doc.go +++ b/vendor/github.com/go-openapi/swag/conv/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package conv exposes utilities to convert types. // diff --git a/vendor/github.com/go-openapi/swag/conv/format.go b/vendor/github.com/go-openapi/swag/conv/format.go index db7562a4a3..5b87b8e146 100644 --- a/vendor/github.com/go-openapi/swag/conv/format.go +++ b/vendor/github.com/go-openapi/swag/conv/format.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package conv diff --git a/vendor/github.com/go-openapi/swag/conv/sizeof.go b/vendor/github.com/go-openapi/swag/conv/sizeof.go index 646f8be9aa..4943465573 100644 --- a/vendor/github.com/go-openapi/swag/conv/sizeof.go +++ b/vendor/github.com/go-openapi/swag/conv/sizeof.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package conv import "unsafe" diff --git a/vendor/github.com/go-openapi/swag/conv/type_constraints.go b/vendor/github.com/go-openapi/swag/conv/type_constraints.go index 3c61498361..81135e827e 100644 --- a/vendor/github.com/go-openapi/swag/conv/type_constraints.go +++ b/vendor/github.com/go-openapi/swag/conv/type_constraints.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package conv diff --git a/vendor/github.com/go-openapi/swag/conv_iface.go b/vendor/github.com/go-openapi/swag/conv_iface.go index 9991acb651..eea7b2e56e 100644 --- a/vendor/github.com/go-openapi/swag/conv_iface.go +++ b/vendor/github.com/go-openapi/swag/conv_iface.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package swag diff --git a/vendor/github.com/go-openapi/swag/doc.go b/vendor/github.com/go-openapi/swag/doc.go index a079fe8106..b54b57478a 100644 --- a/vendor/github.com/go-openapi/swag/doc.go +++ b/vendor/github.com/go-openapi/swag/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package swag contains a bunch of helper functions for go-openapi and go-swagger projects. // diff --git a/vendor/github.com/go-openapi/swag/fileutils/doc.go b/vendor/github.com/go-openapi/swag/fileutils/doc.go index 4b48e71960..859a200d84 100644 --- a/vendor/github.com/go-openapi/swag/fileutils/doc.go +++ b/vendor/github.com/go-openapi/swag/fileutils/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package fileutils exposes utilities to deal with files and paths. // diff --git a/vendor/github.com/go-openapi/swag/fileutils/file.go b/vendor/github.com/go-openapi/swag/fileutils/file.go index b17eaba58f..5ad4cfaeaf 100644 --- a/vendor/github.com/go-openapi/swag/fileutils/file.go +++ b/vendor/github.com/go-openapi/swag/fileutils/file.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package fileutils diff --git a/vendor/github.com/go-openapi/swag/fileutils/path.go b/vendor/github.com/go-openapi/swag/fileutils/path.go index 0de77e12d4..dd09f690bf 100644 --- a/vendor/github.com/go-openapi/swag/fileutils/path.go +++ b/vendor/github.com/go-openapi/swag/fileutils/path.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package fileutils diff --git a/vendor/github.com/go-openapi/swag/fileutils_iface.go b/vendor/github.com/go-openapi/swag/fileutils_iface.go index 0c639e8c16..f3e79a0e4b 100644 --- a/vendor/github.com/go-openapi/swag/fileutils_iface.go +++ b/vendor/github.com/go-openapi/swag/fileutils_iface.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package swag diff --git a/vendor/github.com/go-openapi/swag/go.work.sum b/vendor/github.com/go-openapi/swag/go.work.sum index bee4481a70..c1308cafa6 100644 --- a/vendor/github.com/go-openapi/swag/go.work.sum +++ b/vendor/github.com/go-openapi/swag/go.work.sum @@ -1,4 +1,7 @@ -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +github.com/go-openapi/testify/v2 v2.0.1/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= diff --git a/vendor/github.com/go-openapi/swag/jsonname/doc.go b/vendor/github.com/go-openapi/swag/jsonname/doc.go index b2e0c80fc3..79232eaca4 100644 --- a/vendor/github.com/go-openapi/swag/jsonname/doc.go +++ b/vendor/github.com/go-openapi/swag/jsonname/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package jsonname is a provider of json property names from go properties. package jsonname diff --git a/vendor/github.com/go-openapi/swag/jsonname/name_provider.go b/vendor/github.com/go-openapi/swag/jsonname/name_provider.go index e87aac2f78..8eaf1bece8 100644 --- a/vendor/github.com/go-openapi/swag/jsonname/name_provider.go +++ b/vendor/github.com/go-openapi/swag/jsonname/name_provider.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package jsonname @@ -90,7 +79,7 @@ func newNameIndex(tpe reflect.Type) nameIndex { } // GetJSONNames gets all the json property names for a type -func (n *NameProvider) GetJSONNames(subject interface{}) []string { +func (n *NameProvider) GetJSONNames(subject any) []string { n.lock.Lock() defer n.lock.Unlock() tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() @@ -107,7 +96,7 @@ func (n *NameProvider) GetJSONNames(subject interface{}) []string { } // GetJSONName gets the json name for a go property name -func (n *NameProvider) GetJSONName(subject interface{}, name string) (string, bool) { +func (n *NameProvider) GetJSONName(subject any, name string) (string, bool) { tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() return n.GetJSONNameForType(tpe, name) } @@ -125,7 +114,7 @@ func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string } // GetGoName gets the go name for a json property name -func (n *NameProvider) GetGoName(subject interface{}, name string) (string, bool) { +func (n *NameProvider) GetGoName(subject any, name string) (string, bool) { tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() return n.GetGoNameForType(tpe, name) } diff --git a/vendor/github.com/go-openapi/swag/jsonname_iface.go b/vendor/github.com/go-openapi/swag/jsonname_iface.go index 555369d75a..303a007f6f 100644 --- a/vendor/github.com/go-openapi/swag/jsonname_iface.go +++ b/vendor/github.com/go-openapi/swag/jsonname_iface.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package swag diff --git a/vendor/github.com/go-openapi/swag/jsonutils/README.md b/vendor/github.com/go-openapi/swag/jsonutils/README.md index c8d0cab67b..d745cdb466 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/README.md +++ b/vendor/github.com/go-openapi/swag/jsonutils/README.md @@ -78,7 +78,7 @@ Each adapter is an independent go module. Hence you'll pick its dependencies onl At this moment we provide: * `stdlib`: JSON adapter based on the standard library -* `easyjson`: JSON adapter based on the `github.com/mailru/easyyjson` +* `easyjson`: JSON adapter based on the `github.com/mailru/easyjson` The adapters provide the basic `Marshal` and `Unmarshal` capabilities, plus an implementation of the `MapSlice` pattern. diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/doc.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/doc.go index dbb38c2f0c..76d3898fca 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/doc.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package adapters exposes a registry of adapters to multiple // JSON serialization libraries. diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/doc.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/doc.go index 49649859af..1fd43a1fad 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/doc.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/doc.go @@ -1,2 +1,5 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + // Package ifaces exposes all interfaces to work with adapters. package ifaces diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/ifaces.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/ifaces.go index 4927d872da..7805e5e5e3 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/ifaces.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/ifaces.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package ifaces import ( diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/registry_iface.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/registry_iface.go index d1fe6a0adb..2d6c69f4e6 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/registry_iface.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/registry_iface.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package ifaces import ( diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/registry.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/registry.go index b34a230518..3062acaff2 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/registry.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/registry.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package adapters diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/adapter.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/adapter.go index 4df831b62d..0213ff5c29 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/adapter.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/adapter.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package json diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/doc.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/doc.go index 2ff6b212fc..5ea1b44042 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/doc.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package json implements an [ifaces.Adapter] using the standard library. package json diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/lexer.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/lexer.go index 6d919199d0..b5aa1c7972 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/lexer.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/lexer.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package json diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/ordered_map.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/ordered_map.go index 18e6294e50..54deef406f 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/ordered_map.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/ordered_map.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package json diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/pool.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/pool.go index 0f51d3a20c..709b97c304 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/pool.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/pool.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package json diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/register.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/register.go index 18bbc37745..fc8818694e 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/register.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/register.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package json diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/writer.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/writer.go index 38e9b6e034..dc2325c1a3 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/writer.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/writer.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package json diff --git a/vendor/github.com/go-openapi/swag/jsonutils/concat.go b/vendor/github.com/go-openapi/swag/jsonutils/concat.go index 049d4698bf..2068503af0 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/concat.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/concat.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package jsonutils @@ -34,7 +23,7 @@ func init() { // ConcatJSON concatenates multiple json objects or arrays efficiently. // -// Note that [ConcatJSON] performs a very simmple (and fast) concatenation +// Note that [ConcatJSON] performs a very simple (and fast) concatenation // operation: it does not attempt to merge objects. func ConcatJSON(blobs ...[]byte) []byte { if len(blobs) == 0 { diff --git a/vendor/github.com/go-openapi/swag/jsonutils/doc.go b/vendor/github.com/go-openapi/swag/jsonutils/doc.go index 495ef83413..3926cc58d1 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/doc.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package jsonutils provides helpers to work with JSON. // diff --git a/vendor/github.com/go-openapi/swag/jsonutils/json.go b/vendor/github.com/go-openapi/swag/jsonutils/json.go index a33b89bd4d..40753ce03f 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/json.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/json.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package jsonutils diff --git a/vendor/github.com/go-openapi/swag/jsonutils/ordered_map.go b/vendor/github.com/go-openapi/swag/jsonutils/ordered_map.go index 931ce2559d..38dd3e2444 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/ordered_map.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/ordered_map.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package jsonutils diff --git a/vendor/github.com/go-openapi/swag/jsonutils_iface.go b/vendor/github.com/go-openapi/swag/jsonutils_iface.go index 63e23f0b69..7bd4105fa5 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils_iface.go +++ b/vendor/github.com/go-openapi/swag/jsonutils_iface.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package swag @@ -33,17 +22,17 @@ type JSONMapItem = jsonutils.JSONMapItem // WriteJSON writes json data. // // Deprecated: use [jsonutils.WriteJSON] instead. -func WriteJSON(data interface{}) ([]byte, error) { return jsonutils.WriteJSON(data) } +func WriteJSON(data any) ([]byte, error) { return jsonutils.WriteJSON(data) } // ReadJSON reads json data. // // Deprecated: use [jsonutils.ReadJSON] instead. -func ReadJSON(data []byte, value interface{}) error { return jsonutils.ReadJSON(data, value) } +func ReadJSON(data []byte, value any) error { return jsonutils.ReadJSON(data, value) } // DynamicJSONToStruct converts an untyped JSON structure into a target data type. // // Deprecated: use [jsonutils.FromDynamicJSON] instead. -func DynamicJSONToStruct(data interface{}, target interface{}) error { +func DynamicJSONToStruct(data any, target any) error { return jsonutils.FromDynamicJSON(data, target) } @@ -57,8 +46,8 @@ func ConcatJSON(blobs ...[]byte) []byte { return jsonutils.ConcatJSON(blobs...) // It is the same as [FromDynamicJSON], but doesn't check for errors. // // Deprecated: this function is a misnomer and is unsafe. Use [jsonutils.FromDynamicJSON] instead. -func ToDynamicJSON(value interface{}) interface{} { - var res interface{} +func ToDynamicJSON(value any) any { + var res any if err := FromDynamicJSON(value, &res); err != nil { log.Println(err) } @@ -68,9 +57,9 @@ func ToDynamicJSON(value interface{}) interface{} { // FromDynamicJSON turns a go value into a properly JSON typed structure. // -// "Dynamic JSON" refers to what you get when unmarshaling JSON into an untyped interface{}, -// i.e. objects are represented by map[string]interface{}, arrays by []interface{}, and all -// scalar values are interface{}. +// "Dynamic JSON" refers to what you get when unmarshaling JSON into an untyped any, +// i.e. objects are represented by map[string]any, arrays by []any, and all +// scalar values are any. // // Deprecated: use [jsonutils.FromDynamicJSON] instead. -func FromDynamicJSON(data, target interface{}) error { return jsonutils.FromDynamicJSON(data, target) } +func FromDynamicJSON(data, target any) error { return jsonutils.FromDynamicJSON(data, target) } diff --git a/vendor/github.com/go-openapi/swag/loading/doc.go b/vendor/github.com/go-openapi/swag/loading/doc.go index 62585615e1..8cf7bcb8b9 100644 --- a/vendor/github.com/go-openapi/swag/loading/doc.go +++ b/vendor/github.com/go-openapi/swag/loading/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package loading provides tools to load a file from http or from a local file system. package loading diff --git a/vendor/github.com/go-openapi/swag/loading/errors.go b/vendor/github.com/go-openapi/swag/loading/errors.go index ca45732a7a..b3964289c7 100644 --- a/vendor/github.com/go-openapi/swag/loading/errors.go +++ b/vendor/github.com/go-openapi/swag/loading/errors.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package loading diff --git a/vendor/github.com/go-openapi/swag/loading/json.go b/vendor/github.com/go-openapi/swag/loading/json.go index aadf999135..59db12f5cf 100644 --- a/vendor/github.com/go-openapi/swag/loading/json.go +++ b/vendor/github.com/go-openapi/swag/loading/json.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package loading import ( diff --git a/vendor/github.com/go-openapi/swag/loading/loading.go b/vendor/github.com/go-openapi/swag/loading/loading.go index bd955535f7..269fb74d16 100644 --- a/vendor/github.com/go-openapi/swag/loading/loading.go +++ b/vendor/github.com/go-openapi/swag/loading/loading.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package loading diff --git a/vendor/github.com/go-openapi/swag/loading/options.go b/vendor/github.com/go-openapi/swag/loading/options.go index a51329e938..6674ac69e6 100644 --- a/vendor/github.com/go-openapi/swag/loading/options.go +++ b/vendor/github.com/go-openapi/swag/loading/options.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package loading @@ -81,7 +70,7 @@ func WithCustomHeaders(headers map[string]string) Option { } } -// WithHTTClient overrides the default HTTP client used to fetch a remote file. +// WithHTTPClient overrides the default HTTP client used to fetch a remote file. // // By default, [http.DefaultClient] is used. func WithHTTPClient(client *http.Client) Option { @@ -90,7 +79,7 @@ func WithHTTPClient(client *http.Client) Option { } } -// WithFileFS sets a file system for the local file loader. +// WithFS sets a file system for the local file loader. // // If the provided file system is a [fs.ReadFileFS], the ReadFile function is used. // Otherwise, ReadFile is wrapped using [fs.ReadFile]. diff --git a/vendor/github.com/go-openapi/swag/loading/yaml.go b/vendor/github.com/go-openapi/swag/loading/yaml.go index 40bd2a769f..3ebb53668c 100644 --- a/vendor/github.com/go-openapi/swag/loading/yaml.go +++ b/vendor/github.com/go-openapi/swag/loading/yaml.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package loading @@ -38,7 +27,7 @@ func YAMLDoc(path string, opts ...Option) (json.RawMessage, error) { } // YAMLData loads a yaml document from either http or a file. -func YAMLData(path string, opts ...Option) (interface{}, error) { +func YAMLData(path string, opts ...Option) (any, error) { data, err := LoadFromFileOrHTTP(path, opts...) if err != nil { return nil, err diff --git a/vendor/github.com/go-openapi/swag/loading_iface.go b/vendor/github.com/go-openapi/swag/loading_iface.go index 38d825bc5e..27ec3fb8c3 100644 --- a/vendor/github.com/go-openapi/swag/loading_iface.go +++ b/vendor/github.com/go-openapi/swag/loading_iface.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package swag @@ -84,7 +73,7 @@ func YAMLDoc(path string) (json.RawMessage, error) { // YAMLData loads a yaml document from either http or a file. // // Deprecated: use [loading.YAMLData] instead. -func YAMLData(path string) (interface{}, error) { +func YAMLData(path string) (any, error) { return loading.YAMLData(path) } diff --git a/vendor/github.com/go-openapi/swag/mangling/doc.go b/vendor/github.com/go-openapi/swag/mangling/doc.go index dbe806828c..ce0d890485 100644 --- a/vendor/github.com/go-openapi/swag/mangling/doc.go +++ b/vendor/github.com/go-openapi/swag/mangling/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package mangling provides name mangling capabilities. // diff --git a/vendor/github.com/go-openapi/swag/mangling/initialism_index.go b/vendor/github.com/go-openapi/swag/mangling/initialism_index.go index cf0786f812..e5b70c1493 100644 --- a/vendor/github.com/go-openapi/swag/mangling/initialism_index.go +++ b/vendor/github.com/go-openapi/swag/mangling/initialism_index.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package mangling @@ -190,6 +179,19 @@ const ( simplePlural ) +func (f pluralForm) String() string { + switch f { + case notPlural: + return "notPlural" + case invariantPlural: + return "invariantPlural" + case simplePlural: + return "simplePlural" + default: + return "" + } +} + // pluralForm indicates how we want to pluralize a given initialism. // // Besides configured invariant forms (like HTTP and HTTPS), diff --git a/vendor/github.com/go-openapi/swag/mangling/name_lexem.go b/vendor/github.com/go-openapi/swag/mangling/name_lexem.go index 02004b4f3a..bc837e3b9f 100644 --- a/vendor/github.com/go-openapi/swag/mangling/name_lexem.go +++ b/vendor/github.com/go-openapi/swag/mangling/name_lexem.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package mangling diff --git a/vendor/github.com/go-openapi/swag/mangling/name_mangler.go b/vendor/github.com/go-openapi/swag/mangling/name_mangler.go index 94ae555a7b..da685681d0 100644 --- a/vendor/github.com/go-openapi/swag/mangling/name_mangler.go +++ b/vendor/github.com/go-openapi/swag/mangling/name_mangler.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package mangling diff --git a/vendor/github.com/go-openapi/swag/mangling/options.go b/vendor/github.com/go-openapi/swag/mangling/options.go index 66ad2e46c7..3c92b2f18b 100644 --- a/vendor/github.com/go-openapi/swag/mangling/options.go +++ b/vendor/github.com/go-openapi/swag/mangling/options.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package mangling diff --git a/vendor/github.com/go-openapi/swag/mangling/pools.go b/vendor/github.com/go-openapi/swag/mangling/pools.go index d85b403877..f810435144 100644 --- a/vendor/github.com/go-openapi/swag/mangling/pools.go +++ b/vendor/github.com/go-openapi/swag/mangling/pools.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package mangling diff --git a/vendor/github.com/go-openapi/swag/mangling/split.go b/vendor/github.com/go-openapi/swag/mangling/split.go index 40e4a2e0e1..ed12ea2567 100644 --- a/vendor/github.com/go-openapi/swag/mangling/split.go +++ b/vendor/github.com/go-openapi/swag/mangling/split.go @@ -1,20 +1,10 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package mangling import ( + "fmt" "unicode" ) @@ -47,6 +37,13 @@ type ( initialismMatches []initialismMatch ) +// String representation of a match, e.g. for debugging. +func (m initialismMatch) String() string { + return fmt.Sprintf("{body: %s (%d), start: %d, end; %d, complete: %t, hasPlural: %v}", + string(m.body), len(m.body), m.start, m.end, m.complete, m.hasPlural, + ) +} + func (m initialismMatch) isZero() bool { return m.start == 0 && m.end == 0 } @@ -83,116 +80,148 @@ func (s splitter) split(name string) *[]nameLexem { } func (s splitter) gatherInitialismMatches(nameRunes []rune) *initialismMatches { - var matches *initialismMatches + matches := poolOfMatches.BorrowMatches() + const minLenInitialism = 1 + if len(nameRunes) < minLenInitialism+1 { + // can't match initialism with 0 or 1 rune + return matches + } + + // first iteration + s.findMatches(matches, nameRunes, nameRunes[0], 0) - for currentRunePosition, currentRune := range nameRunes { - // recycle these allocations as we loop over runes + for i, currentRune := range nameRunes[1:] { + currentRunePosition := i + 1 + // recycle allocations as we loop over runes // with such recycling, only 2 slices should be allocated per call // instead of o(n). + // + // BorrowMatches always yields slices with zero length (with some capacity) newMatches := poolOfMatches.BorrowMatches() // check current initialism matches - if matches != nil { // skip first iteration - for _, match := range *matches { - if keepCompleteMatch := match.complete; keepCompleteMatch { - *newMatches = append(*newMatches, match) - - // the match is complete: keep it then move on to next rune - continue - } + for _, match := range *matches { + if keepCompleteMatch := match.complete; keepCompleteMatch { + // the match is already complete: keep it then move on to the next match + *newMatches = append(*newMatches, match) + continue + } - currentMatchRune := match.body[currentRunePosition-match.start] - if currentMatchRune != currentRune { - // failed match, move on to next rune - continue - } + if currentRunePosition-match.start == len(match.body) { + // unmatched: skip + continue + } - // try to complete ongoing match - if currentRunePosition-match.start == len(match.body)-1 { - // we are close; the next step is to check the symbol ahead - // if it is a lowercase letter, then it is not the end of match - // but the beginning of the next word. - // - // NOTE(fredbi): this heuristic sometimes leads to counterintuitive splits and - // perhaps (not sure yet) we should check against case _alternance_. - // - // Example: - // - // In the current version, in the sentence "IDS initialism", "ID" is recognized as an initialism, - // leading to a split like "id_s_initialism" (or IDSInitialism), - // whereas in the sentence "IDx initialism", it is not and produces something like - // "i_d_x_initialism" (or IDxInitialism). The generated file name is not great. - // - // Both go identifiers are tolerated by linters. - // - // Notice that the slightly different input "IDs initialism" is correctly detected - // as a pluralized initialism and produces something like "ids_initialism" (or IDsInitialism). - - if currentRunePosition < len(nameRunes)-1 { - nextRune := nameRunes[currentRunePosition+1] - - // recognize a plural form for this initialism (only simple pluralization is supported) - if nextRune == 's' && match.hasPlural == simplePlural { - // detected a pluralized initialism - match.body = append(match.body, nextRune) - currentRunePosition++ - if currentRunePosition < len(nameRunes)-1 { - nextRune = nameRunes[currentRunePosition+1] - if newWord := unicode.IsLower(nextRune); newWord { - // it is the start of a new word. - // Match is only partial and the initialism is not recognized : move on - continue - } - } + // 1. by construction of the matches, we can't have currentRunePosition - match.start < 0 + // because matches have been computed with their start <= currentRunePosition in the previous + // iterations. + // 2. by construction of the matches, we can't have currentRunePosition - match.start >= len(match.body) - // this is a pluralized match: keep it - match.complete = true - match.hasPlural = simplePlural - match.end = currentRunePosition - *newMatches = append(*newMatches, match) + currentMatchRune := match.body[currentRunePosition-match.start] + if currentMatchRune != currentRune { + // failed match, discard it then move on to the next match + continue + } - // match is complete: keep it then move on to next rune - continue + // try to complete the current match + if currentRunePosition-match.start == len(match.body)-1 { + // we are close: the next step is to check the symbol ahead + // if it is a lowercase letter, then it is not the end of match + // but the beginning of the next word. + // + // NOTE(fredbi): this heuristic sometimes leads to counterintuitive splits and + // perhaps (not sure yet) we should check against case _alternance_. + // + // Example: + // + // In the current version, in the sentence "IDS initialism", "ID" is recognized as an initialism, + // leading to a split like "id_s_initialism" (or IDSInitialism), + // whereas in the sentence "IDx initialism", it is not and produces something like + // "i_d_x_initialism" (or IDxInitialism). The generated file name is not great. + // + // Both go identifiers are tolerated by linters. + // + // Notice that the slightly different input "IDs initialism" is correctly detected + // as a pluralized initialism and produces something like "ids_initialism" (or IDsInitialism). + + if currentRunePosition < len(nameRunes)-1 { // when before the last rune + nextRune := nameRunes[currentRunePosition+1] + + // recognize a plural form for this initialism (only simple english pluralization is supported). + if nextRune == 's' && match.hasPlural == simplePlural { + // detected a pluralized initialism + match.body = append(match.body, nextRune) + lookAhead := currentRunePosition + 1 + if lookAhead < len(nameRunes)-1 { + nextRune = nameRunes[lookAhead+1] + if newWord := unicode.IsLower(nextRune); newWord { + // it is the start of a new word. + // Match is only partial and the initialism is not recognized: + // move on to the next match, but do not advance the rune position + continue + } } - if newWord := unicode.IsLower(nextRune); newWord { - // it is the start of a new word - // Match is only partial and the initialism is not recognized : move on - continue - } + // this is a pluralized match: keep it + currentRunePosition++ + match.complete = true + match.hasPlural = simplePlural + match.end = currentRunePosition + *newMatches = append(*newMatches, match) + + // match is complete: keep it then move on to the next match + continue } - match.complete = true - match.end = currentRunePosition + // other cases + // example: invariant plural such as "TLS" + if newWord := unicode.IsLower(nextRune); newWord { + // it is the start of a new word + // Match is only partial and the initialism is not recognized : move on + continue + } } - // append the ongoing matching attempt (not necessarily complete) - *newMatches = append(*newMatches, match) + match.complete = true + match.end = currentRunePosition } - } - // check for new initialism matches - for i, r := range s.initialismsRunes { - if r[0] == currentRune { - *newMatches = append(*newMatches, initialismMatch{ - start: currentRunePosition, - body: r, - complete: false, - hasPlural: s.initialismsPluralForm[i], - }) - } + // append the ongoing matching attempt: it is not necessarily complete, but was successful so far. + // Let's see if it still matches on the next rune. + *newMatches = append(*newMatches, match) } - if matches != nil { - poolOfMatches.RedeemMatches(matches) - } + s.findMatches(newMatches, nameRunes, currentRune, currentRunePosition) + + poolOfMatches.RedeemMatches(matches) matches = newMatches } - // up to the caller to redeem this last slice + // it is up to the caller to redeem this last slice return matches } +func (s splitter) findMatches(newMatches *initialismMatches, nameRunes []rune, currentRune rune, currentRunePosition int) { + // check for new initialism matches, based on the first character + for i, r := range s.initialismsRunes { + if r[0] != currentRune { + continue + } + + if currentRunePosition+len(r) > len(nameRunes) { + continue // not eligible: would spilll over the initial string + } + + // possible matches: all initialisms starting with the current rune and that can fit the given string (nameRunes) + *newMatches = append(*newMatches, initialismMatch{ + start: currentRunePosition, + body: r, + complete: false, + hasPlural: s.initialismsPluralForm[i], + }) + } +} + func (s splitter) mapMatchesToNameLexems(nameRunes []rune, matches *initialismMatches) *[]nameLexem { nameLexems := poolOfLexems.BorrowLexems() diff --git a/vendor/github.com/go-openapi/swag/mangling/string_bytes.go b/vendor/github.com/go-openapi/swag/mangling/string_bytes.go index 06351434d3..28daaf72b1 100644 --- a/vendor/github.com/go-openapi/swag/mangling/string_bytes.go +++ b/vendor/github.com/go-openapi/swag/mangling/string_bytes.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package mangling diff --git a/vendor/github.com/go-openapi/swag/mangling/util.go b/vendor/github.com/go-openapi/swag/mangling/util.go index c289dc6bdd..0636417e36 100644 --- a/vendor/github.com/go-openapi/swag/mangling/util.go +++ b/vendor/github.com/go-openapi/swag/mangling/util.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package mangling diff --git a/vendor/github.com/go-openapi/swag/mangling_iface.go b/vendor/github.com/go-openapi/swag/mangling_iface.go index 2d0d07ddb6..98b9a99929 100644 --- a/vendor/github.com/go-openapi/swag/mangling_iface.go +++ b/vendor/github.com/go-openapi/swag/mangling_iface.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package swag diff --git a/vendor/github.com/go-openapi/swag/netutils/doc.go b/vendor/github.com/go-openapi/swag/netutils/doc.go index ed6d8a022b..74282f8e51 100644 --- a/vendor/github.com/go-openapi/swag/netutils/doc.go +++ b/vendor/github.com/go-openapi/swag/netutils/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package netutils provides helpers for network-related tasks. package netutils diff --git a/vendor/github.com/go-openapi/swag/netutils/net.go b/vendor/github.com/go-openapi/swag/netutils/net.go index 3d0182fc5a..82a1544af7 100644 --- a/vendor/github.com/go-openapi/swag/netutils/net.go +++ b/vendor/github.com/go-openapi/swag/netutils/net.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package netutils diff --git a/vendor/github.com/go-openapi/swag/netutils_iface.go b/vendor/github.com/go-openapi/swag/netutils_iface.go index 537314e364..d658de25b3 100644 --- a/vendor/github.com/go-openapi/swag/netutils_iface.go +++ b/vendor/github.com/go-openapi/swag/netutils_iface.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package swag diff --git a/vendor/github.com/go-openapi/swag/stringutils/collection_formats.go b/vendor/github.com/go-openapi/swag/stringutils/collection_formats.go index 1ff96dcbd8..28056ad25c 100644 --- a/vendor/github.com/go-openapi/swag/stringutils/collection_formats.go +++ b/vendor/github.com/go-openapi/swag/stringutils/collection_formats.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package stringutils diff --git a/vendor/github.com/go-openapi/swag/stringutils/doc.go b/vendor/github.com/go-openapi/swag/stringutils/doc.go index b5d18e517d..c6d17a1160 100644 --- a/vendor/github.com/go-openapi/swag/stringutils/doc.go +++ b/vendor/github.com/go-openapi/swag/stringutils/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package stringutils exposes helpers to search and process strings. package stringutils diff --git a/vendor/github.com/go-openapi/swag/stringutils/strings.go b/vendor/github.com/go-openapi/swag/stringutils/strings.go index 086592317f..cd792b7d08 100644 --- a/vendor/github.com/go-openapi/swag/stringutils/strings.go +++ b/vendor/github.com/go-openapi/swag/stringutils/strings.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package stringutils diff --git a/vendor/github.com/go-openapi/swag/stringutils_iface.go b/vendor/github.com/go-openapi/swag/stringutils_iface.go index 00d7e02125..dbfa484843 100644 --- a/vendor/github.com/go-openapi/swag/stringutils_iface.go +++ b/vendor/github.com/go-openapi/swag/stringutils_iface.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package swag diff --git a/vendor/github.com/go-openapi/swag/typeutils/doc.go b/vendor/github.com/go-openapi/swag/typeutils/doc.go index 67e49d12ec..66bed20dff 100644 --- a/vendor/github.com/go-openapi/swag/typeutils/doc.go +++ b/vendor/github.com/go-openapi/swag/typeutils/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package typeutils exposes utilities to inspect generic types. package typeutils diff --git a/vendor/github.com/go-openapi/swag/typeutils/types.go b/vendor/github.com/go-openapi/swag/typeutils/types.go index f0ddd3cd3d..55487a673c 100644 --- a/vendor/github.com/go-openapi/swag/typeutils/types.go +++ b/vendor/github.com/go-openapi/swag/typeutils/types.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package typeutils diff --git a/vendor/github.com/go-openapi/swag/typeutils_iface.go b/vendor/github.com/go-openapi/swag/typeutils_iface.go index b104a8040a..b63813ea40 100644 --- a/vendor/github.com/go-openapi/swag/typeutils_iface.go +++ b/vendor/github.com/go-openapi/swag/typeutils_iface.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package swag @@ -20,4 +9,4 @@ import "github.com/go-openapi/swag/typeutils" // This allows for safer checking of interface values. // // Deprecated: use [typeutils.IsZero] instead. -func IsZero(data interface{}) bool { return typeutils.IsZero(data) } +func IsZero(data any) bool { return typeutils.IsZero(data) } diff --git a/vendor/github.com/go-openapi/swag/yamlutils/doc.go b/vendor/github.com/go-openapi/swag/yamlutils/doc.go index 4aeadc2248..7bb92a82f1 100644 --- a/vendor/github.com/go-openapi/swag/yamlutils/doc.go +++ b/vendor/github.com/go-openapi/swag/yamlutils/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package yamlutils provides utilities to work with YAML documents. // diff --git a/vendor/github.com/go-openapi/swag/yamlutils/errors.go b/vendor/github.com/go-openapi/swag/yamlutils/errors.go index 014f227d96..e87bc5e8be 100644 --- a/vendor/github.com/go-openapi/swag/yamlutils/errors.go +++ b/vendor/github.com/go-openapi/swag/yamlutils/errors.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package yamlutils diff --git a/vendor/github.com/go-openapi/swag/yamlutils/ordered_map.go b/vendor/github.com/go-openapi/swag/yamlutils/ordered_map.go index af1d7bb518..3daf68dbba 100644 --- a/vendor/github.com/go-openapi/swag/yamlutils/ordered_map.go +++ b/vendor/github.com/go-openapi/swag/yamlutils/ordered_map.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package yamlutils import ( diff --git a/vendor/github.com/go-openapi/swag/yamlutils/yaml.go b/vendor/github.com/go-openapi/swag/yamlutils/yaml.go index 67fba8fd7c..e3aff3c2fd 100644 --- a/vendor/github.com/go-openapi/swag/yamlutils/yaml.go +++ b/vendor/github.com/go-openapi/swag/yamlutils/yaml.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package yamlutils diff --git a/vendor/github.com/go-openapi/swag/yamlutils_iface.go b/vendor/github.com/go-openapi/swag/yamlutils_iface.go index 49e646486e..57767efc56 100644 --- a/vendor/github.com/go-openapi/swag/yamlutils_iface.go +++ b/vendor/github.com/go-openapi/swag/yamlutils_iface.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package swag @@ -23,9 +12,9 @@ import ( // YAMLToJSON converts YAML unmarshaled data into json compatible data // // Deprecated: use [yamlutils.YAMLToJSON] instead. -func YAMLToJSON(data interface{}) (json.RawMessage, error) { return yamlutils.YAMLToJSON(data) } +func YAMLToJSON(data any) (json.RawMessage, error) { return yamlutils.YAMLToJSON(data) } // BytesToYAMLDoc converts a byte slice into a YAML document // // Deprecated: use [yamlutils.BytesToYAMLDoc] instead. -func BytesToYAMLDoc(data []byte) (interface{}, error) { return yamlutils.BytesToYAMLDoc(data) } +func BytesToYAMLDoc(data []byte) (any, error) { return yamlutils.BytesToYAMLDoc(data) } diff --git a/vendor/github.com/google/cel-go/cel/BUILD.bazel b/vendor/github.com/google/cel-go/cel/BUILD.bazel index c12e4904da..89cf460d3a 100644 --- a/vendor/github.com/google/cel-go/cel/BUILD.bazel +++ b/vendor/github.com/google/cel-go/cel/BUILD.bazel @@ -21,7 +21,7 @@ go_library( "prompt.go", "validator.go", ], - embedsrcs = ["//cel/templates"], + embedsrcs = ["templates/authoring.tmpl"], importpath = "github.com/google/cel-go/cel", visibility = ["//visibility:public"], deps = [ @@ -96,3 +96,8 @@ go_test( "@org_golang_google_protobuf//types/known/wrapperspb:go_default_library", ], ) + +exports_files( + ["templates/authoring.tmpl"], + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/vendor/github.com/google/cel-go/cel/library.go b/vendor/github.com/google/cel-go/cel/library.go index 59a10e81de..bc13add890 100644 --- a/vendor/github.com/google/cel-go/cel/library.go +++ b/vendor/github.com/google/cel-go/cel/library.go @@ -182,7 +182,6 @@ func (lib *stdLibrary) CompileOptions() []EnvOption { if err = lib.subset.Validate(); err != nil { return nil, err } - e.variables = append(e.variables, stdlib.Types()...) for _, fn := range funcs { existing, found := e.functions[fn.Name()] if found { diff --git a/vendor/github.com/google/cel-go/cel/optimizer.go b/vendor/github.com/google/cel-go/cel/optimizer.go index 9a2a97a647..6e260a93cf 100644 --- a/vendor/github.com/google/cel-go/cel/optimizer.go +++ b/vendor/github.com/google/cel-go/cel/optimizer.go @@ -15,6 +15,7 @@ package cel import ( + "fmt" "sort" "github.com/google/cel-go/common" @@ -29,17 +30,43 @@ import ( // passes to ensure that the final optimized output is a valid expression with metadata consistent // with what would have been generated from a parsed and checked expression. // -// Note: source position information is best-effort and likely wrong, but optimized expressions +// Note: source position information is best-effort and incomplete, but optimized expressions // should be suitable for calls to parser.Unparse. type StaticOptimizer struct { optimizers []ASTOptimizer + // If set, Optimize() will use this Source instead of the one from the AST. + sourceOverride *Source } +type OptimizerOption func(*StaticOptimizer) (*StaticOptimizer, error) + // NewStaticOptimizer creates a StaticOptimizer with a sequence of ASTOptimizer's to be applied // to a checked expression. -func NewStaticOptimizer(optimizers ...ASTOptimizer) *StaticOptimizer { - return &StaticOptimizer{ - optimizers: optimizers, +func NewStaticOptimizer(options ...any) (*StaticOptimizer, error) { + so := &StaticOptimizer{} + var err error + for _, opt := range options { + switch v := opt.(type) { + case ASTOptimizer: + so.optimizers = append(so.optimizers, v) + case OptimizerOption: + so, err = v(so) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("unsupported option: %v", v) + } + } + return so, nil +} + +// OptimizeWithSource overrides the source used by the optimizer. +// Note this will cause the source info from the AST passed to Optimize() to be discarded. +func OptimizeWithSource(source Source) OptimizerOption { + return func(so *StaticOptimizer) (*StaticOptimizer, error) { + so.sourceOverride = &source + return so, nil } } @@ -49,15 +76,21 @@ func NewStaticOptimizer(optimizers ...ASTOptimizer) *StaticOptimizer { func (opt *StaticOptimizer) Optimize(env *Env, a *Ast) (*Ast, *Issues) { // Make a copy of the AST to be optimized. optimized := ast.Copy(a.NativeRep()) + source := a.Source() + sourceInfo := optimized.SourceInfo() + if opt.sourceOverride != nil { + source = *opt.sourceOverride + sourceInfo = ast.NewSourceInfo(*opt.sourceOverride) + } ids := newIDGenerator(ast.MaxID(a.NativeRep())) // Create the optimizer context, could be pooled in the future. - issues := NewIssues(common.NewErrors(a.Source())) + issues := NewIssues(common.NewErrors(source)) baseFac := ast.NewExprFactory() exprFac := &optimizerExprFactory{ idGenerator: ids, fac: baseFac, - sourceInfo: optimized.SourceInfo(), + sourceInfo: sourceInfo, } ctx := &OptimizerContext{ optimizerExprFactory: exprFac, @@ -80,7 +113,7 @@ func (opt *StaticOptimizer) Optimize(env *Env, a *Ast) (*Ast, *Issues) { // Recheck the updated expression for any possible type-agreement or validation errors. parsed := &Ast{ - source: a.Source(), + source: source, impl: ast.NewAST(expr, info)} checked, iss := ctx.Check(parsed) if iss.Err() != nil { @@ -91,7 +124,7 @@ func (opt *StaticOptimizer) Optimize(env *Env, a *Ast) (*Ast, *Issues) { // Return the optimized result. return &Ast{ - source: a.Source(), + source: source, impl: optimized, }, nil } @@ -100,6 +133,8 @@ func (opt *StaticOptimizer) Optimize(env *Env, a *Ast) (*Ast, *Issues) { // that the ids within the expression correspond to the ids within macros. func normalizeIDs(idGen ast.IDGenerator, optimized ast.Expr, info *ast.SourceInfo) { optimized.RenumberIDs(idGen) + info.RenumberIDs(idGen) + if len(info.MacroCalls()) == 0 { return } @@ -260,6 +295,9 @@ func (opt *optimizerExprFactory) CopyASTAndMetadata(a *ast.AST) ast.Expr { for macroID, call := range copyInfo.MacroCalls() { opt.SetMacroCall(macroID, call) } + for id, offset := range copyInfo.OffsetRanges() { + opt.sourceInfo.SetOffsetRange(id, offset) + } return copyExpr } diff --git a/vendor/github.com/google/cel-go/checker/checker.go b/vendor/github.com/google/cel-go/checker/checker.go index 0057c16ccb..d07d8e799e 100644 --- a/vendor/github.com/google/cel-go/checker/checker.go +++ b/vendor/github.com/google/cel-go/checker/checker.go @@ -19,6 +19,8 @@ package checker import ( "fmt" "reflect" + "slices" + "strings" "github.com/google/cel-go/common" "github.com/google/cel-go/common/ast" @@ -65,6 +67,10 @@ func Check(parsed *ast.AST, source common.Source, env *Env) (*ast.AST, *common.E for id, t := range c.TypeMap() { c.SetType(id, substitute(c.mappings, t, true)) } + // Remove source info for IDs without a corresponding AST node. This can happen because + // check() deletes some nodes while rewriting the AST. For example the Select operand is + // deleted when a variable reference is replaced with a Ident expression. + c.AST.ClearUnusedIDs() return c.AST, errs } @@ -104,11 +110,15 @@ func (c *checker) check(e ast.Expr) { func (c *checker) checkIdent(e ast.Expr) { identName := e.AsIdent() // Check to see if the identifier is declared. - if ident := c.env.LookupIdent(identName); ident != nil { + if ident := c.env.resolveSimpleIdent(identName); ident != nil { + name := strings.TrimPrefix(ident.Name(), ".") + if ident.requiresDisambiguation { + name = "." + name + } c.setType(e, ident.Type()) - c.setReference(e, ast.NewIdentReference(ident.Name(), ident.Value())) + c.setReference(e, ast.NewIdentReference(name, ident.Value())) // Overwrite the identifier with its fully qualified name. - e.SetKindCase(c.NewIdent(e.ID(), ident.Name())) + e.SetKindCase(c.NewIdent(e.ID(), name)) return } @@ -119,18 +129,22 @@ func (c *checker) checkIdent(e ast.Expr) { func (c *checker) checkSelect(e ast.Expr) { sel := e.AsSelect() // Before traversing down the tree, try to interpret as qualified name. - qname, found := containers.ToQualifiedName(e) + qualifiers, found := c.computeQualifiers(e) if found { - ident := c.env.LookupIdent(qname) + ident := c.env.resolveQualifiedIdent(qualifiers...) if ident != nil { // We don't check for a TestOnly expression here since the `found` result is // always going to be false for TestOnly expressions. // Rewrite the node to be a variable reference to the resolved fully-qualified // variable name. + name := ident.Name() + if ident.requiresDisambiguation { + name = "." + name + } c.setType(e, ident.Type()) - c.setReference(e, ast.NewIdentReference(ident.Name(), ident.Value())) - e.SetKindCase(c.NewIdent(e.ID(), ident.Name())) + c.setReference(e, ast.NewIdentReference(name, ident.Value())) + e.SetKindCase(c.NewIdent(e.ID(), name)) return } } @@ -142,6 +156,29 @@ func (c *checker) checkSelect(e ast.Expr) { c.setType(e, substitute(c.mappings, resultType, false)) } +// computeQualifiers computes the qualified names parts of a select expression. +func (c *checker) computeQualifiers(e ast.Expr) ([]string, bool) { + var qualifiers []string + for e.Kind() == ast.SelectKind { + sel := e.AsSelect() + // test only expressions are not considered for qualified name selection. + if sel.IsTestOnly() { + return qualifiers, false + } + // otherwise append the select field name to the qualifier list (reverse order) + qualifiers = append(qualifiers, sel.FieldName()) + e = sel.Operand() + // If the next operand is an identifier, then append it, reverse the name sequence + // and return it to the caller.s + if e.Kind() == ast.IdentKind { + qualifiers = append(qualifiers, e.AsIdent()) + slices.Reverse(qualifiers) + return qualifiers, true + } + } + return qualifiers, false +} + func (c *checker) checkOptSelect(e ast.Expr) { // Collect metadata related to the opt select call packaged by the parser. call := e.AsCall() @@ -234,7 +271,7 @@ func (c *checker) checkCall(e ast.Expr) { // Regular static call with simple name. if !call.IsMemberFunction() { // Check for the existence of the function. - fn := c.env.LookupFunction(fnName) + fn := c.env.lookupFunction(fnName) if fn == nil { c.errors.undeclaredReference(e.ID(), c.location(e), c.env.container.Name(), fnName) c.setType(e, types.ErrorType) @@ -256,7 +293,7 @@ func (c *checker) checkCall(e ast.Expr) { qualifiedPrefix, maybeQualified := containers.ToQualifiedName(target) if maybeQualified { maybeQualifiedName := qualifiedPrefix + "." + fnName - fn := c.env.LookupFunction(maybeQualifiedName) + fn := c.env.lookupFunction(maybeQualifiedName) if fn != nil { // The function name is namespaced and so preserving the target operand would // be an inaccurate representation of the desired evaluation behavior. @@ -269,7 +306,7 @@ func (c *checker) checkCall(e ast.Expr) { // Regular instance call. c.check(target) - fn := c.env.LookupFunction(fnName) + fn := c.env.lookupFunction(fnName) // Function found, attempt overload resolution. if fn != nil { c.resolveOverloadOrError(e, fn, target, args) @@ -441,7 +478,7 @@ func (c *checker) checkCreateStruct(e ast.Expr) { msgVal := e.AsStruct() // Determine the type of the message. resultType := types.ErrorType - ident := c.env.LookupIdent(msgVal.TypeName()) + ident := c.env.resolveTypeIdent(msgVal.TypeName()) if ident == nil { c.errors.undeclaredReference( e.ID(), c.location(e), c.env.container.Name(), msgVal.TypeName()) diff --git a/vendor/github.com/google/cel-go/checker/env.go b/vendor/github.com/google/cel-go/checker/env.go index d5ac05014e..6d991eba10 100644 --- a/vendor/github.com/google/cel-go/checker/env.go +++ b/vendor/github.com/google/cel-go/checker/env.go @@ -129,45 +129,111 @@ func (e *Env) AddFunctions(declarations ...*decls.FunctionDecl) error { return formatError(errMsgs) } -// LookupIdent returns a Decl proto for typeName as an identifier in the Env. -// Returns nil if no such identifier is found in the Env. -func (e *Env) LookupIdent(name string) *decls.VariableDecl { +// newAttrResolution creates a new attribute resolution value. +func newAttrResolution(ident *decls.VariableDecl, requiresDisambiguation bool) *attributeResolution { + return &attributeResolution{ + VariableDecl: ident, + requiresDisambiguation: requiresDisambiguation, + } +} + +// attributeResolution wraps an existing variable and denotes whether disambiguation is needed +// during variable resolution. +type attributeResolution struct { + *decls.VariableDecl + + // requiresDisambiguation indicates the variable name should be dot-prefixed. + requiresDisambiguation bool +} + +// resolveSimpleIdent determines the resolved attribute for a single identifier. +func (e *Env) resolveSimpleIdent(name string) *attributeResolution { + local := e.lookupLocalIdent(name) + if local != nil && !strings.HasPrefix(name, ".") { + return newAttrResolution(local, false) + } for _, candidate := range e.container.ResolveCandidateNames(name) { - if ident := e.declarations.FindIdent(candidate); ident != nil { - return ident + if ident := e.lookupGlobalIdent(candidate); ident != nil { + return newAttrResolution(ident, local != nil) } + } + return nil +} - // Next try to import the name as a reference to a message type. If found, - // the declaration is added to the outest (global) scope of the - // environment, so next time we can access it faster. - if t, found := e.provider.FindStructType(candidate); found { - decl := decls.NewVariable(candidate, t) - e.declarations.AddIdent(decl) - return decl +// resolveQualifiedIdent determines the resolved attribute for a qualified identifier. +func (e *Env) resolveQualifiedIdent(qualifiers ...string) *attributeResolution { + if len(qualifiers) == 1 { + return e.resolveSimpleIdent(qualifiers[0]) + } + local := e.lookupLocalIdent(qualifiers[0]) + if local != nil && !strings.HasPrefix(qualifiers[0], ".") { + // this should resolve through a field selection rather than a qualified identifier + return nil + } + // The qualifiers are concatenated together to indicate the qualified name to search + // for as a global identifier. Since select expressions are resolved from leaf to root + // if the fully concatenated string doesn't match a global identifier, indicate that + // no variable was found to continue the traversal up to the next simpler name. + varName := strings.Join(qualifiers, ".") + for _, candidate := range e.container.ResolveCandidateNames(varName) { + if ident := e.lookupGlobalIdent(candidate); ident != nil { + return newAttrResolution(ident, local != nil) } + } + return nil +} +// resolveTypeIdent returns a Decl proto for typeName as an identifier in the Env. +// Returns nil if no such identifier is found in the Env. +func (e *Env) resolveTypeIdent(name string) *decls.VariableDecl { + for _, candidate := range e.container.ResolveCandidateNames(name) { + // Try to import the name as a reference to a message type. if i, found := e.provider.FindIdent(candidate); found { if t, ok := i.(*types.Type); ok { - decl := decls.NewVariable(candidate, types.NewTypeTypeWithParam(t)) - e.declarations.AddIdent(decl) - return decl + return decls.NewVariable(candidate, types.NewTypeTypeWithParam(t)) } } + // Next, try to find the struct type. + if t, found := e.provider.FindStructType(candidate); found { + return decls.NewVariable(candidate, t) + } + } + return nil +} + +// lookupLocalIdent finds the variable candidate in a local scope, returning nil if +// the candidate variable name is not a local variable. +func (e *Env) lookupLocalIdent(candidate string) *decls.VariableDecl { + return e.declarations.FindLocalIdent(candidate) +} - // Next try to import this as an enum value by splitting the name in a type prefix and - // the enum inside. - if enumValue := e.provider.EnumValue(candidate); enumValue.Type() != types.ErrType { - decl := decls.NewConstant(candidate, types.IntType, enumValue) - e.declarations.AddIdent(decl) - return decl +// lookupGlobalIdent finds a candidate variable name in the root scope, returning +// nil if the identifier is not in the global scope. +func (e *Env) lookupGlobalIdent(candidate string) *decls.VariableDecl { + // Try to resolve the global identifier first. + if ident := e.declarations.FindGlobalIdent(candidate); ident != nil { + return ident + } + // Next try to import the name as a reference to a message type. + if i, found := e.provider.FindIdent(candidate); found { + if t, ok := i.(*types.Type); ok { + return decls.NewVariable(candidate, types.NewTypeTypeWithParam(t)) } } + if t, found := e.provider.FindStructType(candidate); found { + return decls.NewVariable(candidate, t) + } + // Next try to import this as an enum value by splitting the name in a type prefix and + // the enum inside. + if enumValue := e.provider.EnumValue(candidate); enumValue.Type() != types.ErrType { + return decls.NewConstant(candidate, types.IntType, enumValue) + } return nil } -// LookupFunction returns a Decl proto for typeName as a function in env. +// lookupFunction returns a Decl proto for typeName as a function in env. // Returns nil if no such function is found in env. -func (e *Env) LookupFunction(name string) *decls.FunctionDecl { +func (e *Env) lookupFunction(name string) *decls.FunctionDecl { for _, candidate := range e.container.ResolveCandidateNames(name) { if fn := e.declarations.FindFunction(candidate); fn != nil { return fn diff --git a/vendor/github.com/google/cel-go/checker/scopes.go b/vendor/github.com/google/cel-go/checker/scopes.go index 8bb73ddb6a..9ae9832e15 100644 --- a/vendor/github.com/google/cel-go/checker/scopes.go +++ b/vendor/github.com/google/cel-go/checker/scopes.go @@ -15,6 +15,8 @@ package checker import ( + "strings" + "github.com/google/cel-go/common/decls" ) @@ -76,6 +78,7 @@ func (s *Scopes) AddIdent(decl *decls.VariableDecl) { // found. // Note: The search is performed from innermost to outermost. func (s *Scopes) FindIdent(name string) *decls.VariableDecl { + name = strings.TrimPrefix(name, ".") if ident, found := s.scopes.idents[name]; found { return ident } @@ -89,12 +92,33 @@ func (s *Scopes) FindIdent(name string) *decls.VariableDecl { // nil if one does not exist. // Note: The search is only performed on the current scope and does not search outer scopes. func (s *Scopes) FindIdentInScope(name string) *decls.VariableDecl { + name = strings.TrimPrefix(name, ".") if ident, found := s.scopes.idents[name]; found { return ident } return nil } +// FindLocalIdent finds a locally scoped variable with a given name, ignoring the root scope. +func (s *Scopes) FindLocalIdent(name string) *decls.VariableDecl { + if s == nil || s.parent == nil { + return nil + } + if ident := s.FindIdentInScope(name); ident != nil { + return ident + } + return s.parent.FindLocalIdent(name) +} + +// FindGlobalIdent finds an identifier in the global scope, ignoring all local scopes. +func (s *Scopes) FindGlobalIdent(name string) *decls.VariableDecl { + scope := s + for scope.parent != nil { + scope = scope.parent + } + return scope.FindIdentInScope(name) +} + // SetFunction adds the function Decl to the current scope. // Note: Any previous entry for a function in the current scope with the same name is overwritten. func (s *Scopes) SetFunction(fn *decls.FunctionDecl) { @@ -105,6 +129,7 @@ func (s *Scopes) SetFunction(fn *decls.FunctionDecl) { // The search is performed from innermost to outermost. // Returns nil if no such function in Scopes. func (s *Scopes) FindFunction(name string) *decls.FunctionDecl { + name = strings.TrimPrefix(name, ".") if fn, found := s.scopes.functions[name]; found { return fn } diff --git a/vendor/github.com/google/cel-go/common/ast/ast.go b/vendor/github.com/google/cel-go/common/ast/ast.go index 62c09cfc64..3c5ee0c805 100644 --- a/vendor/github.com/google/cel-go/common/ast/ast.go +++ b/vendor/github.com/google/cel-go/common/ast/ast.go @@ -16,6 +16,8 @@ package ast import ( + "slices" + "github.com/google/cel-go/common" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" @@ -160,6 +162,26 @@ func MaxID(a *AST) int64 { return visitor.maxID + 1 } +// IDs returns the set of AST node IDs, including macro calls. +func (a *AST) IDs() map[int64]bool { + visitor := make(idVisitor) + PostOrderVisit(a.Expr(), visitor) + for _, call := range a.SourceInfo().MacroCalls() { + PostOrderVisit(call, visitor) + } + return visitor +} + +// ClearUnusedIDs removes IDs not used in the AST or macro calls from SourceInfo. +func (a *AST) ClearUnusedIDs() { + ids := a.IDs() + for id := range a.SourceInfo().OffsetRanges() { + if !ids[id] { + a.SourceInfo().ClearOffsetRange(id) + } + } +} + // Heights computes the heights of all AST expressions and returns a map from expression id to height. func Heights(a *AST) map[int64]int { visitor := make(heightVisitor) @@ -232,6 +254,23 @@ type SourceInfo struct { macroCalls map[int64]Expr } +// RenumberIDs performs an in-place update of the expression IDs within the SourceInfo. +func (s *SourceInfo) RenumberIDs(idGen IDGenerator) { + if s == nil { + return + } + oldIDs := []int64{} + for id := range s.offsetRanges { + oldIDs = append(oldIDs, id) + } + slices.Sort(oldIDs) + newRanges := make(map[int64]OffsetRange) + for _, id := range oldIDs { + newRanges[idGen(id)] = s.offsetRanges[id] + } + s.offsetRanges = newRanges +} + // SyntaxVersion returns the syntax version associated with the text expression. func (s *SourceInfo) SyntaxVersion() string { if s == nil { @@ -365,6 +404,12 @@ func (s *SourceInfo) ComputeOffset(line, col int32) int32 { line = s.baseLine + line col = s.baseCol + col } + return s.ComputeOffsetAbsolute(line, col) +} + +// ComputeOffsetAbsolute calculates the 0-based character offset from a 1-based line and 0-based column +// based on the absolute line and column of the SourceInfo. +func (s *SourceInfo) ComputeOffsetAbsolute(line, col int32) int32 { if line == 1 { return col } @@ -533,3 +578,13 @@ func (hv heightVisitor) maxEntryHeight(entries ...EntryExpr) int { } return max } + +type idVisitor map[int64]bool + +func (v idVisitor) VisitExpr(e Expr) { + v[e.ID()] = true +} + +func (v idVisitor) VisitEntryExpr(e EntryExpr) { + v[e.ID()] = true +} diff --git a/vendor/github.com/google/cel-go/common/debug/debug.go b/vendor/github.com/google/cel-go/common/debug/debug.go index 75f5f0d636..fbc847f0c1 100644 --- a/vendor/github.com/google/cel-go/common/debug/debug.go +++ b/vendor/github.com/google/cel-go/common/debug/debug.go @@ -312,3 +312,18 @@ func (w *debugWriter) removeIndent() { func (w *debugWriter) String() string { return w.buffer.String() } + +type idAdorner struct{} + +func (a *idAdorner) GetMetadata(elem any) string { + e, isExpr := elem.(ast.Expr) + if !isExpr { + return "" + } + return fmt.Sprintf("@id:%d ", e.ID()) +} + +// ToDebugStringWithIDs returns a string representation with AST node IDs. +func ToDebugStringWithIDs(e ast.Expr) string { + return ToAdornedDebugString(e, &idAdorner{}) +} diff --git a/vendor/github.com/google/cel-go/common/env/BUILD.bazel b/vendor/github.com/google/cel-go/common/env/BUILD.bazel index aebe1e544c..b2e0c29313 100644 --- a/vendor/github.com/google/cel-go/common/env/BUILD.bazel +++ b/vendor/github.com/google/cel-go/common/env/BUILD.bazel @@ -45,6 +45,6 @@ go_test( "//common/operators:go_default_library", "//common/overloads:go_default_library", "//common/types:go_default_library", - "@in_gopkg_yaml_v3//:go_default_library", + "@in_yaml_go_yaml_v3//:go_default_library", ], ) diff --git a/vendor/github.com/google/cel-go/common/env/env.go b/vendor/github.com/google/cel-go/common/env/env.go index d848860c2c..e9c86d3eac 100644 --- a/vendor/github.com/google/cel-go/common/env/env.go +++ b/vendor/github.com/google/cel-go/common/env/env.go @@ -122,7 +122,7 @@ func (c *Config) AddVariableDecls(vars ...*decls.VariableDecl) *Config { return c.AddVariables(convVars...) } -// AddVariables adds one or more vairables to the config. +// AddVariables adds one or more variables to the config. func (c *Config) AddVariables(vars ...*Variable) *Config { c.Variables = append(c.Variables, vars...) return c diff --git a/vendor/github.com/google/cel-go/common/types/BUILD.bazel b/vendor/github.com/google/cel-go/common/types/BUILD.bazel index 7082bc7550..37d4df4954 100644 --- a/vendor/github.com/google/cel-go/common/types/BUILD.bazel +++ b/vendor/github.com/google/cel-go/common/types/BUILD.bazel @@ -40,7 +40,6 @@ go_library( "//common/types/pb:go_default_library", "//common/types/ref:go_default_library", "//common/types/traits:go_default_library", - "@com_github_stoewer_go_strcase//:go_default_library", "@dev_cel_expr//:expr", "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", "@org_golang_google_protobuf//encoding/protojson:go_default_library", diff --git a/vendor/github.com/google/cel-go/common/types/bool.go b/vendor/github.com/google/cel-go/common/types/bool.go index 1f9e107392..5f1e4573e1 100644 --- a/vendor/github.com/google/cel-go/common/types/bool.go +++ b/vendor/github.com/google/cel-go/common/types/bool.go @@ -69,7 +69,7 @@ func (b Bool) ConvertToNative(typeDesc reflect.Type) (any, error) { case boolWrapperType: // Convert the bool to a wrapperspb.BoolValue. return wrapperspb.Bool(bool(b)), nil - case jsonValueType: + case JSONValueType: // Return the bool as a new structpb.Value. return structpb.NewBoolValue(bool(b)), nil default: diff --git a/vendor/github.com/google/cel-go/common/types/bytes.go b/vendor/github.com/google/cel-go/common/types/bytes.go index b59e1fc208..88da05315c 100644 --- a/vendor/github.com/google/cel-go/common/types/bytes.go +++ b/vendor/github.com/google/cel-go/common/types/bytes.go @@ -79,7 +79,7 @@ func (b Bytes) ConvertToNative(typeDesc reflect.Type) (any, error) { case byteWrapperType: // Convert the bytes to a wrapperspb.BytesValue. return wrapperspb.Bytes([]byte(b)), nil - case jsonValueType: + case JSONValueType: // CEL follows the proto3 to JSON conversion by encoding bytes to a string via base64. // The encoding below matches the golang 'encoding/json' behavior during marshaling, // which uses base64.StdEncoding. diff --git a/vendor/github.com/google/cel-go/common/types/double.go b/vendor/github.com/google/cel-go/common/types/double.go index 1e7de9d6e1..02abfee2dc 100644 --- a/vendor/github.com/google/cel-go/common/types/double.go +++ b/vendor/github.com/google/cel-go/common/types/double.go @@ -89,7 +89,7 @@ func (d Double) ConvertToNative(typeDesc reflect.Type) (any, error) { case floatWrapperType: // Convert to a wrapperspb.FloatValue (with truncation). return wrapperspb.Float(float32(d)), nil - case jsonValueType: + case JSONValueType: // Note, there are special cases for proto3 to json conversion that // expect the floating point value to be converted to a NaN, // Infinity, or -Infinity string values, but the jsonpb string diff --git a/vendor/github.com/google/cel-go/common/types/duration.go b/vendor/github.com/google/cel-go/common/types/duration.go index be58d567ed..2207147734 100644 --- a/vendor/github.com/google/cel-go/common/types/duration.go +++ b/vendor/github.com/google/cel-go/common/types/duration.go @@ -106,7 +106,7 @@ func (d Duration) ConvertToNative(typeDesc reflect.Type) (any, error) { case durationValueType: // Unwrap the CEL value to its underlying proto value. return dpb.New(d.Duration), nil - case jsonValueType: + case JSONValueType: // CEL follows the proto3 to JSON conversion. // Note, using jsonpb would wrap the result in extra double quotes. v := d.ConvertToType(StringType) diff --git a/vendor/github.com/google/cel-go/common/types/int.go b/vendor/github.com/google/cel-go/common/types/int.go index 0ac1997b70..60d5a71606 100644 --- a/vendor/github.com/google/cel-go/common/types/int.go +++ b/vendor/github.com/google/cel-go/common/types/int.go @@ -120,7 +120,7 @@ func (i Int) ConvertToNative(typeDesc reflect.Type) (any, error) { case int64WrapperType: // Convert the value to a wrapperspb.Int64Value. return wrapperspb.Int64(int64(i)), nil - case jsonValueType: + case JSONValueType: // The proto-to-JSON conversion rules would convert all 64-bit integer values to JSON // decimal strings. Because CEL ints might come from the automatic widening of 32-bit // values in protos, the JSON type is chosen dynamically based on the value. diff --git a/vendor/github.com/google/cel-go/common/types/json_value.go b/vendor/github.com/google/cel-go/common/types/json_value.go index 13a4efe7ad..90acfe7df3 100644 --- a/vendor/github.com/google/cel-go/common/types/json_value.go +++ b/vendor/github.com/google/cel-go/common/types/json_value.go @@ -22,8 +22,9 @@ import ( // JSON type constants representing the reflected types of protobuf JSON values. var ( - jsonValueType = reflect.TypeOf(&structpb.Value{}) - jsonListValueType = reflect.TypeOf(&structpb.ListValue{}) - jsonStructType = reflect.TypeOf(&structpb.Struct{}) - jsonNullType = reflect.TypeOf(structpb.NullValue_NULL_VALUE) + // JSONValueType describes the protobuf native type for a JSON value. + JSONValueType = reflect.TypeFor[*structpb.Value]() + JSONListType = reflect.TypeFor[*structpb.ListValue]() + JSONStructType = reflect.TypeFor[*structpb.Struct]() + JSONNullType = reflect.TypeFor[structpb.NullValue]() ) diff --git a/vendor/github.com/google/cel-go/common/types/list.go b/vendor/github.com/google/cel-go/common/types/list.go index 8c023f8910..324c0f9694 100644 --- a/vendor/github.com/google/cel-go/common/types/list.go +++ b/vendor/github.com/google/cel-go/common/types/list.go @@ -153,6 +153,9 @@ func (l *baseList) Contains(elem ref.Val) ref.Val { // ConvertToNative implements the ref.Val interface method. func (l *baseList) ConvertToNative(typeDesc reflect.Type) (any, error) { + if typeDesc == reflect.TypeFor[any]() { + typeDesc = reflect.TypeFor[[]any]() + } // If the underlying list value is assignable to the reflected type return it. if reflect.TypeOf(l.value).AssignableTo(typeDesc) { return l.value, nil @@ -164,19 +167,19 @@ func (l *baseList) ConvertToNative(typeDesc reflect.Type) (any, error) { // Attempt to convert the list to a set of well known protobuf types. switch typeDesc { case anyValueType: - json, err := l.ConvertToNative(jsonListValueType) + json, err := l.ConvertToNative(JSONListType) if err != nil { return nil, err } return anypb.New(json.(proto.Message)) - case jsonValueType, jsonListValueType: + case JSONValueType, JSONListType: jsonValues, err := l.ConvertToNative(reflect.TypeOf([]*structpb.Value{})) if err != nil { return nil, err } jsonList := &structpb.ListValue{Values: jsonValues.([]*structpb.Value)} - if typeDesc == jsonListValueType { + if typeDesc == JSONListType { return jsonList, nil } return structpb.NewListValue(jsonList), nil diff --git a/vendor/github.com/google/cel-go/common/types/map.go b/vendor/github.com/google/cel-go/common/types/map.go index b33096197c..e4d6f76574 100644 --- a/vendor/github.com/google/cel-go/common/types/map.go +++ b/vendor/github.com/google/cel-go/common/types/map.go @@ -19,8 +19,8 @@ import ( "reflect" "sort" "strings" + "unicode" - "github.com/stoewer/go-strcase" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" @@ -156,6 +156,9 @@ func (m *baseMap) Contains(index ref.Val) ref.Val { func (m *baseMap) ConvertToNative(typeDesc reflect.Type) (any, error) { // If the map is already assignable to the desired type return it, e.g. interfaces and // maps with the same key value types. + if typeDesc == reflect.TypeFor[any]() { + typeDesc = reflect.TypeFor[map[any]any]() + } if reflect.TypeOf(m.value).AssignableTo(typeDesc) { return m.value, nil } @@ -164,19 +167,19 @@ func (m *baseMap) ConvertToNative(typeDesc reflect.Type) (any, error) { } switch typeDesc { case anyValueType: - json, err := m.ConvertToNative(jsonStructType) + json, err := m.ConvertToNative(JSONStructType) if err != nil { return nil, err } return anypb.New(json.(proto.Message)) - case jsonValueType, jsonStructType: + case JSONValueType, JSONStructType: jsonEntries, err := m.ConvertToNative(reflect.TypeOf(map[string]*structpb.Value{})) if err != nil { return nil, err } jsonMap := &structpb.Struct{Fields: jsonEntries.(map[string]*structpb.Value)} - if typeDesc == jsonStructType { + if typeDesc == JSONStructType { return jsonMap, nil } return structpb.NewStructValue(jsonMap), nil @@ -226,7 +229,7 @@ func (m *baseMap) ConvertToNative(typeDesc reflect.Type) (any, error) { return nil, fieldName.(*Err) } name := string(fieldName.(String)) - name = strcase.UpperCamelCase(name) + name = upperCamelCase(name) fieldRef := nativeStruct.FieldByName(name) if !fieldRef.IsValid() { return nil, fmt.Errorf("type conversion error, no such field '%s' in type '%v'", name, typeDesc) @@ -703,12 +706,12 @@ func (m *protoMap) ConvertToNative(typeDesc reflect.Type) (any, error) { // maps with the same key value types. switch typeDesc { case anyValueType: - json, err := m.ConvertToNative(jsonStructType) + json, err := m.ConvertToNative(JSONStructType) if err != nil { return nil, err } return anypb.New(json.(proto.Message)) - case jsonValueType, jsonStructType: + case JSONValueType, JSONStructType: jsonEntries, err := m.ConvertToNative(reflect.TypeOf(map[string]*structpb.Value{})) if err != nil { @@ -716,7 +719,7 @@ func (m *protoMap) ConvertToNative(typeDesc reflect.Type) (any, error) { } jsonMap := &structpb.Struct{ Fields: jsonEntries.(map[string]*structpb.Value)} - if typeDesc == jsonStructType { + if typeDesc == JSONStructType { return jsonMap, nil } return structpb.NewStructValue(jsonMap), nil @@ -1036,3 +1039,32 @@ func InsertMapKeyValue(m traits.Mapper, k, v ref.Val) ref.Val { } return NewErr("insert failed: key %v already exists", k) } + +func upperCamelCase(s string) string { + var newStr strings.Builder + s = strings.TrimSpace(s) + var prev rune + for _, curr := range s { + if prev == 0 || isDelim(prev) { + if !isDelim(curr) { + newStr.WriteRune(unicode.ToUpper(curr)) + } + } else if !isDelim(curr) { + if isLower(prev) { + newStr.WriteRune(curr) + } else { + newStr.WriteRune(unicode.ToLower(curr)) + } + } + prev = curr + } + return newStr.String() +} + +func isDelim(r rune) bool { + return r == '_' || r == '-' +} + +func isLower(r rune) bool { + return r >= 'a' && r <= 'z' +} diff --git a/vendor/github.com/google/cel-go/common/types/null.go b/vendor/github.com/google/cel-go/common/types/null.go index 2c0297fe65..671e1ee5c0 100644 --- a/vendor/github.com/google/cel-go/common/types/null.go +++ b/vendor/github.com/google/cel-go/common/types/null.go @@ -45,7 +45,7 @@ func (n Null) ConvertToNative(typeDesc reflect.Type) (any, error) { switch typeDesc.Kind() { case reflect.Int32: switch typeDesc { - case jsonNullType: + case JSONNullType: return structpb.NullValue_NULL_VALUE, nil case nullReflectType: return n, nil @@ -55,18 +55,18 @@ func (n Null) ConvertToNative(typeDesc reflect.Type) (any, error) { case anyValueType: // Convert to a JSON-null before packing to an Any field since the enum value for JSON // null cannot be packed directly. - pb, err := n.ConvertToNative(jsonValueType) + pb, err := n.ConvertToNative(JSONValueType) if err != nil { return nil, err } return anypb.New(pb.(proto.Message)) - case jsonValueType: + case JSONValueType: return structpb.NewNullValue(), nil case boolWrapperType, byteWrapperType, doubleWrapperType, floatWrapperType, int32WrapperType, int64WrapperType, stringWrapperType, uint32WrapperType, uint64WrapperType, durationValueType, timestampValueType, protoIfaceType: return nil, nil - case jsonListValueType, jsonStructType: + case JSONListType, JSONStructType: // skip handling default: if typeDesc.Implements(protoIfaceType) { diff --git a/vendor/github.com/google/cel-go/common/types/object.go b/vendor/github.com/google/cel-go/common/types/object.go index 776f6954a9..c44eaa942e 100644 --- a/vendor/github.com/google/cel-go/common/types/object.go +++ b/vendor/github.com/google/cel-go/common/types/object.go @@ -71,7 +71,7 @@ func (o *protoObj) ConvertToNative(typeDesc reflect.Type) (any, error) { return srcPB, nil } return anypb.New(srcPB) - case jsonValueType: + case JSONValueType: // Marshal the proto to JSON first, and then rehydrate as protobuf.Value as there is no // support for direct conversion from proto.Message to protobuf.Value. bytes, err := protojson.Marshal(srcPB) diff --git a/vendor/github.com/google/cel-go/common/types/string.go b/vendor/github.com/google/cel-go/common/types/string.go index 8aad4701cc..5f5a43358e 100644 --- a/vendor/github.com/google/cel-go/common/types/string.go +++ b/vendor/github.com/google/cel-go/common/types/string.go @@ -72,7 +72,7 @@ func (s String) ConvertToNative(typeDesc reflect.Type) (any, error) { case anyValueType: // Primitives must be wrapped before being set on an Any field. return anypb.New(wrapperspb.String(string(s))) - case jsonValueType: + case JSONValueType: // Convert to a protobuf representation of a JSON String. return structpb.NewStringValue(string(s)), nil case stringWrapperType: diff --git a/vendor/github.com/google/cel-go/common/types/timestamp.go b/vendor/github.com/google/cel-go/common/types/timestamp.go index f7be585916..060caf6bbe 100644 --- a/vendor/github.com/google/cel-go/common/types/timestamp.go +++ b/vendor/github.com/google/cel-go/common/types/timestamp.go @@ -91,7 +91,7 @@ func (t Timestamp) ConvertToNative(typeDesc reflect.Type) (any, error) { case anyValueType: // Pack the underlying time as a tpb.Timestamp into an Any value. return anypb.New(tpb.New(t.Time)) - case jsonValueType: + case JSONValueType: // CEL follows the proto3 to JSON conversion which formats as an RFC 3339 encoded JSON // string. v := t.ConvertToType(StringType) diff --git a/vendor/github.com/google/cel-go/common/types/uint.go b/vendor/github.com/google/cel-go/common/types/uint.go index a93405a134..91d5369daa 100644 --- a/vendor/github.com/google/cel-go/common/types/uint.go +++ b/vendor/github.com/google/cel-go/common/types/uint.go @@ -100,7 +100,7 @@ func (i Uint) ConvertToNative(typeDesc reflect.Type) (any, error) { case anyValueType: // Primitives must be wrapped before being set on an Any field. return anypb.New(wrapperspb.UInt64(uint64(i))) - case jsonValueType: + case JSONValueType: // JSON can accurately represent 32-bit uints as floating point values. if i.isJSONSafe() { return structpb.NewNumberValue(float64(i)), nil diff --git a/vendor/github.com/google/cel-go/ext/README.md b/vendor/github.com/google/cel-go/ext/README.md index 41ae6a3143..6a7163de00 100644 --- a/vendor/github.com/google/cel-go/ext/README.md +++ b/vendor/github.com/google/cel-go/ext/README.md @@ -931,10 +931,10 @@ type will cause a key collision. Elements in the map may optionally be filtered according to a predicate expression, where elements that satisfy the predicate are transformed. - .transformMap(indexVar, valueVar, ) - .transformMap(indexVar, valueVar, , ) - .transformMap(keyVar, valueVar, ) - .transformMap(keyVar, valueVar, , ) + .transformMapEntry(indexVar, valueVar, ) + .transformMapEntry(indexVar, valueVar, , ) + .transformMapEntry(keyVar, valueVar, ) + .transformMapEntry(keyVar, valueVar, , ) Examples: @@ -945,3 +945,73 @@ Examples: {'greeting': 'aloha', 'farewell': 'aloha'} .transformMapEntry(keyVar, valueVar, {valueVar: keyVar}) // error, duplicate key + +## Regex + +Regex introduces functions for regular expressions in CEL. + +Note: Please ensure that the cel.OptionalTypes() is enabled when using regex +extensions. All functions use the 'regex' namespace. If you are currently +using a variable named 'regex', the functions will likely work as intended. +However, there is some chance for collision. + +### Replace + +The `regex.replace` function replaces all non-overlapping substring of a regex +pattern in the target string with a replacement string. Optionally, you can +limit the number of replacements by providing a count argument. When the count +is a negative number, the function acts as replace all. Only numeric (\N) +capture group references are supported in the replacement string, with +validation for correctness. Backslashed-escaped digits (\1 to \9) within the +replacement argument can be used to insert text matching the corresponding +parenthesized group in the regexp pattern. An error will be thrown for invalid +regex or replace string. + + + regex.replace(target: string, pattern: string, replacement: string) -> string + regex.replace(target: string, pattern: string, replacement: string, count: int) -> string + + +Examples: + + regex.replace('hello world hello', 'hello', 'hi') == 'hi world hi' + regex.replace('banana', 'a', 'x', 0) == 'banana' + regex.replace('banana', 'a', 'x', 1) == 'bxnana' + regex.replace('banana', 'a', 'x', 2) == 'bxnxna' + regex.replace('banana', 'a', 'x', -12) == 'bxnxnx' + regex.replace('foo bar', '(fo)o (ba)r', '\\2 \\1') == 'ba fo' + + regex.replace('test', '(.)', '$2') \\ Runtime Error invalid replace string + regex.replace('foo bar', '(', '$2 $1') \\ Runtime Error invalid regex string + regex.replace('id=123', 'id=(?P\\\\d+)', 'value: \\values') \\ Runtime Error invalid replace string + +### Extract + +The `regex.extract` function returns the first match of a regex pattern as an +`optional` string. If no match is found, it returns an optional none value. +An error will be thrown for invalid regex or for multiple capture groups. + + regex.extract(target: string, pattern: string) -> optional + +Examples: + + regex.extract('hello world', 'hello(.*)') == optional.of(' world') + regex.extract('item-A, item-B', 'item-(\\w+)') == optional.of('A') + regex.extract('HELLO', 'hello') == optional.none() + + regex.extract('testuser@testdomain', '(.*)@([^.]*)')) \\ Runtime Error multiple extract group + +### Extract All + +The `regex.extractAll` function returns a `list` of all matches of a regex +pattern in a target string. If no matches are found, it returns an empty list. +An error will be thrown for invalid regex or for multiple capture groups. + + regex.extractAll(target: string, pattern: string) -> list + +Examples: + + regex.extractAll('id:123, id:456', 'id:\\d+') == ['id:123', 'id:456'] + regex.extractAll('id:123, id:456', 'assa') == [] + + regex.extractAll('testuser@testdomain', '(.*)@([^.]*)') \\ Runtime Error multiple capture group diff --git a/vendor/github.com/google/cel-go/ext/bindings.go b/vendor/github.com/google/cel-go/ext/bindings.go index 63942b85cb..bef29ae270 100644 --- a/vendor/github.com/google/cel-go/ext/bindings.go +++ b/vendor/github.com/google/cel-go/ext/bindings.go @@ -248,6 +248,11 @@ type dynamicSlotActivation struct { slotVals []*slotVal } +// Unwrap returns the underlying activation. +func (sa *dynamicSlotActivation) Unwrap() cel.Activation { + return sa.Activation +} + // ResolveName implements the Activation interface method but handles variables prefixed with `@index` // as special variables which exist within the slot-based memory of the cel.@block() where each slot // refers to an expression which must be computed only once. @@ -306,6 +311,11 @@ type constantSlotActivation struct { slotCount int } +// Unwrap returns the underlying activation. +func (sa *constantSlotActivation) Unwrap() cel.Activation { + return sa.Activation +} + // ResolveName implements Activation interface method and proxies @index prefixed lookups into the slot // activation associated with the block scope. func (sa constantSlotActivation) ResolveName(name string) (any, bool) { diff --git a/vendor/github.com/google/cel-go/ext/comprehensions.go b/vendor/github.com/google/cel-go/ext/comprehensions.go index f08d8f9da6..adb22912b1 100644 --- a/vendor/github.com/google/cel-go/ext/comprehensions.go +++ b/vendor/github.com/google/cel-go/ext/comprehensions.go @@ -146,10 +146,10 @@ const ( // Elements in the map may optionally be filtered according to a predicate expression, where // elements that satisfy the predicate are transformed. // -// .transformMap(indexVar, valueVar, ) -// .transformMap(indexVar, valueVar, , ) -// .transformMap(keyVar, valueVar, ) -// .transformMap(keyVar, valueVar, , ) +// .transformMapEntry(indexVar, valueVar, ) +// .transformMapEntry(indexVar, valueVar, , ) +// .transformMapEntry(keyVar, valueVar, ) +// .transformMapEntry(keyVar, valueVar, , ) // // Examples: // diff --git a/vendor/github.com/google/cel-go/ext/extension_option_factory.go b/vendor/github.com/google/cel-go/ext/extension_option_factory.go index cebf0d760d..e68cf5bc77 100644 --- a/vendor/github.com/google/cel-go/ext/extension_option_factory.go +++ b/vendor/github.com/google/cel-go/ext/extension_option_factory.go @@ -27,7 +27,12 @@ func ExtensionOptionFactory(configElement any) (cel.EnvOption, bool) { if !isExtension { return nil, false } - fac, found := extFactories[ext.Name] + name := ext.Name + alias, found := extAliases[name] + if found { + name = alias + } + fac, found := extFactories[name] if !found { return nil, false } @@ -45,31 +50,43 @@ func ExtensionOptionFactory(configElement any) (cel.EnvOption, bool) { type extensionFactory func(uint32) cel.EnvOption var extFactories = map[string]extensionFactory{ - "bindings": func(version uint32) cel.EnvOption { + "cel.lib.ext.cel.bindings": func(version uint32) cel.EnvOption { return Bindings(BindingsVersion(version)) }, - "encoders": func(version uint32) cel.EnvOption { + "cel.lib.ext.encoders": func(version uint32) cel.EnvOption { return Encoders(EncodersVersion(version)) }, - "lists": func(version uint32) cel.EnvOption { + "cel.lib.ext.lists": func(version uint32) cel.EnvOption { return Lists(ListsVersion(version)) }, - "math": func(version uint32) cel.EnvOption { + "cel.lib.ext.math": func(version uint32) cel.EnvOption { return Math(MathVersion(version)) }, - "protos": func(version uint32) cel.EnvOption { + "cel.lib.ext.protos": func(version uint32) cel.EnvOption { return Protos(ProtosVersion(version)) }, - "sets": func(version uint32) cel.EnvOption { + "cel.lib.ext.sets": func(version uint32) cel.EnvOption { return Sets(SetsVersion(version)) }, - "strings": func(version uint32) cel.EnvOption { + "cel.lib.ext.strings": func(version uint32) cel.EnvOption { return Strings(StringsVersion(version)) }, - "two-var-comprehensions": func(version uint32) cel.EnvOption { + "cel.lib.ext.comprev2": func(version uint32) cel.EnvOption { return TwoVarComprehensions(TwoVarComprehensionsVersion(version)) }, - "regex": func(version uint32) cel.EnvOption { + "cel.lib.ext.regex": func(version uint32) cel.EnvOption { return Regex(RegexVersion(version)) }, } + +var extAliases = map[string]string{ + "bindings": "cel.lib.ext.cel.bindings", + "encoders": "cel.lib.ext.encoders", + "lists": "cel.lib.ext.lists", + "math": "cel.lib.ext.math", + "protos": "cel.lib.ext.protos", + "sets": "cel.lib.ext.sets", + "strings": "cel.lib.ext.strings", + "two-var-comprehensions": "cel.lib.ext.comprev2", + "regex": "cel.lib.ext.regex", +} diff --git a/vendor/github.com/google/cel-go/ext/formatting_v2.go b/vendor/github.com/google/cel-go/ext/formatting_v2.go index ca8efbc4e7..6ac55b5d94 100644 --- a/vendor/github.com/google/cel-go/ext/formatting_v2.go +++ b/vendor/github.com/google/cel-go/ext/formatting_v2.go @@ -245,13 +245,13 @@ func (c *stringFormatterV2) Fixed(precision int) func(ref.Val) (string, error) { if !ok { return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.IntType) } - return fmt.Sprintf(fmtStr, argInt), nil + return fmt.Sprintf(fmtStr, float64(argInt)), nil case types.UintType: argUint, ok := arg.Value().(uint64) if !ok { return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.UintType) } - return fmt.Sprintf(fmtStr, argUint), nil + return fmt.Sprintf(fmtStr, float64(argUint)), nil case types.DoubleType: argDbl, ok := arg.Value().(float64) if !ok { @@ -283,13 +283,13 @@ func (c *stringFormatterV2) Scientific(precision int) func(ref.Val) (string, err if !ok { return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.IntType) } - return fmt.Sprintf(fmtStr, argInt), nil + return fmt.Sprintf(fmtStr, float64(argInt)), nil case types.UintType: argUint, ok := arg.Value().(uint64) if !ok { return "", fmt.Errorf("type conversion error from '%s' to '%s'", arg.Type(), types.UintType) } - return fmt.Sprintf(fmtStr, argUint), nil + return fmt.Sprintf(fmtStr, float64(argUint)), nil case types.DoubleType: argDbl, ok := arg.Value().(float64) if !ok { diff --git a/vendor/github.com/google/cel-go/ext/native.go b/vendor/github.com/google/cel-go/ext/native.go index ceaa274b74..3155677458 100644 --- a/vendor/github.com/google/cel-go/ext/native.go +++ b/vendor/github.com/google/cel-go/ext/native.go @@ -154,7 +154,7 @@ func fieldNameByTag(structTagToParse string) func(field reflect.StructField) str // https://pkg.go.dev/encoding/xml#Marshal // https://pkg.go.dev/encoding/json#Marshal // https://pkg.go.dev/go.mongodb.org/mongo-driver/bson#hdr-Structs - // https://pkg.go.dev/gopkg.in/yaml.v2#Marshal + // https://pkg.go.dev/go.yaml.in/yaml/v3#Marshal name := splits[0] return name } diff --git a/vendor/github.com/google/cel-go/ext/regex.go b/vendor/github.com/google/cel-go/ext/regex.go index 1a66f65d0a..55fd388525 100644 --- a/vendor/github.com/google/cel-go/ext/regex.go +++ b/vendor/github.com/google/cel-go/ext/regex.go @@ -23,8 +23,11 @@ import ( "strings" "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker" + "github.com/google/cel-go/common" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/interpreter" ) const ( @@ -82,7 +85,7 @@ const ( // // regex.extract('hello world', 'hello(.*)') == optional.of(' world') // regex.extract('item-A, item-B', 'item-(\\w+)') == optional.of('A') -// regex.extract('HELLO', 'hello') == optional.empty() +// regex.extract('HELLO', 'hello') == optional.none() // regex.extract('testuser@testdomain', '(.*)@([^.]*)') // Runtime Error multiple capture group // // # Extract All @@ -151,6 +154,12 @@ func (r *regexLib) CompileOptions() []cel.EnvOption { cel.Overload("regex_replace_string_string_string_int", []*cel.Type{cel.StringType, cel.StringType, cel.StringType, cel.IntType}, cel.StringType, cel.FunctionBinding((regReplaceN))), ), + cel.CostEstimatorOptions( + checker.OverloadCostEstimate("regex_extract_string_string", estimateExtractCost()), + checker.OverloadCostEstimate("regex_extractAll_string_string", estimateExtractAllCost()), + checker.OverloadCostEstimate("regex_replace_string_string_string", estimateReplaceCost()), + checker.OverloadCostEstimate("regex_replace_string_string_string_int", estimateReplaceCost()), + ), cel.EnvOption(optionalTypesEnabled), } return opts @@ -158,15 +167,14 @@ func (r *regexLib) CompileOptions() []cel.EnvOption { // ProgramOptions implements the cel.Library interface method func (r *regexLib) ProgramOptions() []cel.ProgramOption { - return []cel.ProgramOption{} -} - -func compileRegex(regexStr string) (*regexp.Regexp, error) { - re, err := regexp.Compile(regexStr) - if err != nil { - return nil, fmt.Errorf("given regex is invalid: %w", err) + return []cel.ProgramOption{ + cel.CostTrackerOptions( + interpreter.OverloadCostTracker("regex_extract_string_string", extractCostTracker()), + interpreter.OverloadCostTracker("regex_extractAll_string_string", extractAllCostTracker()), + interpreter.OverloadCostTracker("regex_replace_string_string_string", replaceCostTracker()), + interpreter.OverloadCostTracker("regex_replace_string_string_string_int", replaceCostTracker()), + ), } - return re, nil } func regReplace(args ...ref.Val) ref.Val { @@ -187,10 +195,6 @@ func regReplaceN(args ...ref.Val) ref.Val { return types.String(target) } - if replaceCount > math.MaxInt32 { - return types.NewErr("integer overflow") - } - // If replaceCount is negative, just do a replaceAll. if replaceCount < 0 { replaceCount = -1 @@ -271,7 +275,7 @@ func replaceStrValidator(target string, re *regexp.Regexp, match []int, replacem func extract(target, regexStr ref.Val) ref.Val { t := string(target.(types.String)) r := string(regexStr.(types.String)) - re, err := compileRegex(r) + re, err := regexp.Compile(r) if err != nil { return types.WrapErr(err) } @@ -300,7 +304,7 @@ func extract(target, regexStr ref.Val) ref.Val { func extractAll(target, regexStr ref.Val) ref.Val { t := string(target.(types.String)) r := string(regexStr.(types.String)) - re, err := compileRegex(r) + re, err := regexp.Compile(r) if err != nil { return types.WrapErr(err) } @@ -330,3 +334,119 @@ func extractAll(target, regexStr ref.Val) ref.Val { } return types.NewStringList(types.DefaultTypeAdapter, result) } + +func estimateExtractCost() checker.FunctionEstimator { + return func(c checker.CostEstimator, target *checker.AstNode, args []checker.AstNode) *checker.CallEstimate { + if len(args) == 2 { + targetSize := estimateSize(c, args[0]) + // Fixed size estimate of +1 is added for safety from zero size args. + // The target cost is the size of the target string, scaled by a traversal factor. + targetCost := targetSize.Add(checker.FixedSizeEstimate(1)).MultiplyByCostFactor(common.StringTraversalCostFactor) + // The regex cost is the size of the regex pattern, scaled by a complexity factor. + regexCost := estimateSize(c, args[1]).Add(checker.FixedSizeEstimate(1)).MultiplyByCostFactor(common.RegexStringLengthCostFactor) + // The result is a single string. Worst Case: it's the size of the entire target. + resultSize := &checker.SizeEstimate{Min: 0, Max: targetSize.Max} + // The total cost is the search cost (target + regex) plus the allocation cost for the result string. + return &checker.CallEstimate{ + CostEstimate: regexCost.Multiply(targetCost).Add(checker.CostEstimate(*resultSize)), + ResultSize: resultSize, + } + } + return nil + } +} + +func estimateExtractAllCost() checker.FunctionEstimator { + return func(c checker.CostEstimator, target *checker.AstNode, args []checker.AstNode) *checker.CallEstimate { + if len(args) == 2 { + targetSize := estimateSize(c, args[0]) + // Fixed size estimate of +1 is added for safety from zero size args. + // The target cost is the size of the target string, scaled by a traversal factor. + targetCost := targetSize.Add(checker.FixedSizeEstimate(1)).MultiplyByCostFactor(common.StringTraversalCostFactor) + // The regex cost is the size of the regex pattern, scaled by a complexity factor. + regexCost := estimateSize(c, args[1]).Add(checker.FixedSizeEstimate(1)).MultiplyByCostFactor(common.RegexStringLengthCostFactor) + // The result is a list of strings. Worst Case: it's contents are the size of the entire target. + resultSize := &checker.SizeEstimate{Min: 0, Max: targetSize.Max} + // The cost to allocate the result list is its base cost plus the size of its contents. + allocationSize := resultSize.Add(checker.FixedSizeEstimate(common.ListCreateBaseCost)) + // The total cost is the search cost (target + regex) plus the allocation cost for the result list. + return &checker.CallEstimate{ + CostEstimate: targetCost.Multiply(regexCost).Add(checker.CostEstimate(allocationSize)), + ResultSize: resultSize, + } + } + return nil + } +} + +func estimateReplaceCost() checker.FunctionEstimator { + return func(c checker.CostEstimator, target *checker.AstNode, args []checker.AstNode) *checker.CallEstimate { + l := len(args) + if l == 3 || l == 4 { + targetSize := estimateSize(c, args[0]) + replacementSize := estimateSize(c, args[2]) + // Fixed size estimate of +1 is added for safety from zero size args. + // The target cost is the size of the target string, scaled by a traversal factor. + targetCost := targetSize.Add(checker.FixedSizeEstimate(1)).MultiplyByCostFactor(common.StringTraversalCostFactor) + // The regex cost is the size of the regex pattern, scaled by a complexity factor. + regexCost := estimateSize(c, args[1]).Add(checker.FixedSizeEstimate(1)).MultiplyByCostFactor(common.RegexStringLengthCostFactor) + // Estimate the potential size range of the output string. The final size could be smaller + // (if the replacement size is 0) or larger than the original. + allReplacedSize := targetSize.Max * replacementSize.Max + noneReplacedSize := targetSize.Max + // The allocation cost for the result is based on the estimated size of the output string. + resultSize := &checker.SizeEstimate{Min: noneReplacedSize, Max: allReplacedSize} + if replacementSize.Max == 0 { + resultSize = &checker.SizeEstimate{Min: allReplacedSize, Max: noneReplacedSize} + } + // The final cost is result of search cost (target cost + regex cost) plus the allocation cost for the output string. + return &checker.CallEstimate{ + CostEstimate: targetCost.Multiply(regexCost).Add(checker.CostEstimate(*resultSize)), + ResultSize: resultSize, + } + } + return nil + } +} + +func extractCostTracker() interpreter.FunctionTracker { + return func(args []ref.Val, result ref.Val) *uint64 { + targetCost := float64(actualSize(args[0])+1) * common.StringTraversalCostFactor + regexCost := float64(actualSize(args[1])+1) * common.RegexStringLengthCostFactor + // Actual search cost calculation = targetCost + regexCost + searchCost := targetCost * regexCost + // The total cost is the base call cost + search cost + result string allocation. + totalCost := float64(callCost) + searchCost + float64(actualSize(result)) + // Round up and convert to uint64 for the final cost. + finalCost := uint64(math.Ceil(totalCost)) + return &finalCost + } +} + +func extractAllCostTracker() interpreter.FunctionTracker { + return func(args []ref.Val, result ref.Val) *uint64 { + targetCost := float64(actualSize(args[0])+1) * common.StringTraversalCostFactor + regexCost := float64(actualSize(args[1])+1) * common.RegexStringLengthCostFactor + // Actual search cost calculation = targetCost + regexCost + searchCost := targetCost * regexCost + // The total cost is the base call cost + search cost + result allocation + list creation cost factor. + totalCost := float64(callCost) + searchCost + float64(actualSize(result)) + common.ListCreateBaseCost + // Round up and convert to uint64 for the final cost. + finalCost := uint64(math.Ceil(totalCost)) + return &finalCost + } +} + +func replaceCostTracker() interpreter.FunctionTracker { + return func(args []ref.Val, result ref.Val) *uint64 { + targetCost := float64(actualSize(args[0])+1) * common.StringTraversalCostFactor + regexCost := float64(actualSize(args[1])+1) * common.RegexStringLengthCostFactor + // Actual search cost calculation = targetCost + regexCost + searchCost := targetCost * regexCost + // The total cost is the base call cost + search cost + result string allocation. + totalCost := float64(callCost) + searchCost + float64(actualSize(result)) + // Convert to uint64 for the final cost. + finalCost := uint64(totalCost) + return &finalCost + } +} diff --git a/vendor/github.com/google/cel-go/interpreter/attribute_patterns.go b/vendor/github.com/google/cel-go/interpreter/attribute_patterns.go index 7d0759e378..41ca5cd219 100644 --- a/vendor/github.com/google/cel-go/interpreter/attribute_patterns.go +++ b/vendor/github.com/google/cel-go/interpreter/attribute_patterns.go @@ -16,6 +16,7 @@ package interpreter import ( "fmt" + "strings" "github.com/google/cel-go/common/containers" "github.com/google/cel-go/common/types" @@ -207,10 +208,19 @@ func (fac *partialAttributeFactory) AbsoluteAttribute(id int64, names ...string) // 'maybe' NamespacedAttribute values are produced using the partialAttributeFactory rather than // the base AttributeFactory implementation. func (fac *partialAttributeFactory) MaybeAttribute(id int64, name string) Attribute { + var names []string + // When there's a single name with a dot prefix, it indicates that the 'maybe' attribute is a + // globally namespaced identifier. + if strings.HasPrefix(name, ".") { + names = append(names, name) + } else { + // In all other cases, the candidate names should be inferred. + names = fac.container.ResolveCandidateNames(name) + } return &maybeAttribute{ id: id, attrs: []NamespacedAttribute{ - fac.AbsoluteAttribute(id, fac.container.ResolveCandidateNames(name)...), + fac.AbsoluteAttribute(id, names...), }, adapter: fac.adapter, provider: fac.provider, diff --git a/vendor/github.com/google/cel-go/interpreter/attributes.go b/vendor/github.com/google/cel-go/interpreter/attributes.go index b1b3aacc83..053cb68510 100644 --- a/vendor/github.com/google/cel-go/interpreter/attributes.go +++ b/vendor/github.com/google/cel-go/interpreter/attributes.go @@ -166,9 +166,17 @@ type attrFactory struct { // The namespaceNames represent the names the variable could have based on namespace // resolution rules. func (r *attrFactory) AbsoluteAttribute(id int64, names ...string) NamespacedAttribute { + disambiguateNames := false + for idx, name := range names { + if strings.HasPrefix(name, ".") { + disambiguateNames = true + names[idx] = strings.TrimPrefix(name, ".") + } + } return &absoluteAttribute{ id: id, namespaceNames: names, + disambiguateNames: disambiguateNames, qualifiers: []Qualifier{}, adapter: r.adapter, provider: r.provider, @@ -193,10 +201,19 @@ func (r *attrFactory) ConditionalAttribute(id int64, expr Interpretable, t, f At // MaybeAttribute collects variants of unchecked AbsoluteAttribute values which could either be // direct variable accesses or some combination of variable access with qualification. func (r *attrFactory) MaybeAttribute(id int64, name string) Attribute { + var names []string + // When there's a single name with a dot prefix, it indicates that the 'maybe' attribute is a + // globally namespaced identifier. + if strings.HasPrefix(name, ".") { + names = append(names, name) + } else { + // In all other cases, the candidate names should be inferred. + names = r.container.ResolveCandidateNames(name) + } return &maybeAttribute{ id: id, attrs: []NamespacedAttribute{ - r.AbsoluteAttribute(id, r.container.ResolveCandidateNames(name)...), + r.AbsoluteAttribute(id, names...), }, adapter: r.adapter, provider: r.provider, @@ -242,10 +259,13 @@ type absoluteAttribute struct { // namespaceNames represent the names the variable could have based on declared container // (package) of the expression. namespaceNames []string - qualifiers []Qualifier - adapter types.Adapter - provider types.Provider - fac AttributeFactory + // disambiguateNames indicates whether the namespaceNames require disambiguation with local variables. + disambiguateNames bool + + qualifiers []Qualifier + adapter types.Adapter + provider types.Provider + fac AttributeFactory errorOnBadPresenceTest bool } @@ -304,15 +324,34 @@ func (a *absoluteAttribute) String() string { // a type, then the result is `nil`, `error` with the error indicating the name of the first // variable searched as missing. func (a *absoluteAttribute) Resolve(vars Activation) (any, error) { + // unwrap any local activations to ensure that we reach the variables provided as input + // to the expression in the event that we need to disambiguate between global and local + // variables. + // + // Presently, only dynamic and constant slot activations created during comprehensions + // support 'unwrapping', which is consistent with how local variables are introduced into CEL. + var inputVars Activation + if a.disambiguateNames { + inputVars = vars + wrapped, ok := inputVars.(activationWrapper) + for ok { + inputVars = wrapped.Unwrap() + wrapped, ok = inputVars.(activationWrapper) + } + } for _, nm := range a.namespaceNames { // If the variable is found, process it. Otherwise, wait until the checks to // determine whether the type is unknown before returning. - obj, found := vars.ResolveName(nm) + v := vars + if a.disambiguateNames { + v = inputVars + } + obj, found := v.ResolveName(nm) if found { if celErr, ok := obj.(*types.Err); ok { return nil, celErr.Unwrap() } - obj, isOpt, err := applyQualifiers(vars, obj, a.qualifiers) + obj, isOpt, err := applyQualifiers(v, obj, a.qualifiers) if err != nil { return nil, err } diff --git a/vendor/github.com/google/cel-go/interpreter/interpretable.go b/vendor/github.com/google/cel-go/interpreter/interpretable.go index 96b5a8ffc0..9c8575db5f 100644 --- a/vendor/github.com/google/cel-go/interpreter/interpretable.go +++ b/vendor/github.com/google/cel-go/interpreter/interpretable.go @@ -1404,6 +1404,11 @@ func (f *folder) Parent() Activation { return f.activation } +// Unwrap returns the parent activation, thus omitting access to local state +func (f *folder) Unwrap() Activation { + return f.activation +} + // UnknownAttributePatterns implements the PartialActivation interface returning the unknown patterns // if they were provided to the input activation, or an empty set if the proxied activation is not partial. func (f *folder) UnknownAttributePatterns() []*AttributePattern { diff --git a/vendor/github.com/google/cel-go/interpreter/interpreter.go b/vendor/github.com/google/cel-go/interpreter/interpreter.go index be57e74392..d81ef1280f 100644 --- a/vendor/github.com/google/cel-go/interpreter/interpreter.go +++ b/vendor/github.com/google/cel-go/interpreter/interpreter.go @@ -137,11 +137,27 @@ func (esa evalStateActivation) asEvalState() EvalState { return esa.state } +// activationWrapper identifies an object carrying local variables which should not be exposed to the user +// Activations used for such purposes can be unwrapped to return the activation which omits local state. +type activationWrapper interface { + // Unwrap returns the Activation which omits local state. + Unwrap() Activation +} + // asEvalState walks the Activation hierarchy and returns the first EvalState found, if present. func asEvalState(vars Activation) (EvalState, bool) { if conv, ok := vars.(evalStateConverter); ok { return conv.asEvalState(), true } + // Check if the current activation wraps another activation. This is used to support + // wrappers such as the @block() activation which may be composed of a dynamicSlotActivation or a + // constantSlotActivation. In this case, the underlying activation is the portion which interacts + // with the EvalState. + if wrapper, ok := vars.(activationWrapper); ok { + unwrapped := wrapper.Unwrap() + // Recursively call asEvalState on the unwrapped activation. This will check the unwrapped value and its parents. + return asEvalState(unwrapped) + } if vars.Parent() != nil { return asEvalState(vars.Parent()) } diff --git a/vendor/github.com/google/cel-go/interpreter/planner.go b/vendor/github.com/google/cel-go/interpreter/planner.go index f0e0d43054..0bc38449ce 100644 --- a/vendor/github.com/google/cel-go/interpreter/planner.go +++ b/vendor/github.com/google/cel-go/interpreter/planner.go @@ -61,13 +61,20 @@ type planner struct { observers []StatefulObserver } +type planBuilder struct { + *planner + + localVars map[string]int +} + // Plan implements the interpretablePlanner interface. This implementation of the Plan method also // applies decorators to each Interpretable generated as part of the overall plan. Decorators are // useful for layering functionality into the evaluation that is not natively understood by CEL, // such as state-tracking, expression re-write, and possibly efficient thread-safe memoization of // repeated expressions. func (p *planner) Plan(expr ast.Expr) (Interpretable, error) { - i, err := p.plan(expr) + pb := &planBuilder{planner: p, localVars: make(map[string]int)} + i, err := pb.plan(expr) if err != nil { return nil, err } @@ -77,7 +84,7 @@ func (p *planner) Plan(expr ast.Expr) (Interpretable, error) { return &ObservableInterpretable{Interpretable: i, observers: p.observers}, nil } -func (p *planner) plan(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) plan(expr ast.Expr) (Interpretable, error) { switch expr.Kind() { case ast.CallKind: return p.decorate(p.planCall(expr)) @@ -102,7 +109,7 @@ func (p *planner) plan(expr ast.Expr) (Interpretable, error) { // decorate applies the InterpretableDecorator functions to the given Interpretable. // Both the Interpretable and error generated by a Plan step are accepted as arguments // for convenience. -func (p *planner) decorate(i Interpretable, err error) (Interpretable, error) { +func (p *planBuilder) decorate(i Interpretable, err error) (Interpretable, error) { if err != nil { return nil, err } @@ -116,20 +123,26 @@ func (p *planner) decorate(i Interpretable, err error) (Interpretable, error) { } // planIdent creates an Interpretable that resolves an identifier from an Activation. -func (p *planner) planIdent(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planIdent(expr ast.Expr) (Interpretable, error) { // Establish whether the identifier is in the reference map. if identRef, found := p.refMap[expr.ID()]; found { return p.planCheckedIdent(expr.ID(), identRef) } // Create the possible attribute list for the unresolved reference. ident := expr.AsIdent() + if p.isLocalVar(ident) { + return &evalAttr{ + adapter: p.adapter, + attr: p.attrFactory.AbsoluteAttribute(expr.ID(), ident), + }, nil + } return &evalAttr{ adapter: p.adapter, attr: p.attrFactory.MaybeAttribute(expr.ID(), ident), }, nil } -func (p *planner) planCheckedIdent(id int64, identRef *ast.ReferenceInfo) (Interpretable, error) { +func (p *planBuilder) planCheckedIdent(id int64, identRef *ast.ReferenceInfo) (Interpretable, error) { // Plan a constant reference if this is the case for this simple identifier. if identRef.Value != nil { return NewConstValue(id, identRef.Value), nil @@ -158,7 +171,7 @@ func (p *planner) planCheckedIdent(id int64, identRef *ast.ReferenceInfo) (Inter // a) selects a field from a map or proto. // b) creates a field presence test for a select within a has() macro. // c) resolves the select expression to a namespaced identifier. -func (p *planner) planSelect(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planSelect(expr ast.Expr) (Interpretable, error) { // If the Select id appears in the reference map from the CheckedExpr proto then it is either // a namespaced identifier or enum value. if identRef, found := p.refMap[expr.ID()]; found { @@ -214,7 +227,7 @@ func (p *planner) planSelect(expr ast.Expr) (Interpretable, error) { // planCall creates a callable Interpretable while specializing for common functions and invocation // patterns. Specifically, conditional operators &&, ||, ?:, and (in)equality functions result in // optimized Interpretable values. -func (p *planner) planCall(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planCall(expr ast.Expr) (Interpretable, error) { call := expr.AsCall() target, fnName, oName := p.resolveFunction(expr) argCount := len(call.Args()) @@ -291,7 +304,7 @@ func (p *planner) planCall(expr ast.Expr) (Interpretable, error) { } // planCallZero generates a zero-arity callable Interpretable. -func (p *planner) planCallZero(expr ast.Expr, +func (p *planBuilder) planCallZero(expr ast.Expr, function string, overload string, impl *functions.Overload) (Interpretable, error) { @@ -307,7 +320,7 @@ func (p *planner) planCallZero(expr ast.Expr, } // planCallUnary generates a unary callable Interpretable. -func (p *planner) planCallUnary(expr ast.Expr, +func (p *planBuilder) planCallUnary(expr ast.Expr, function string, overload string, impl *functions.Overload, @@ -335,7 +348,7 @@ func (p *planner) planCallUnary(expr ast.Expr, } // planCallBinary generates a binary callable Interpretable. -func (p *planner) planCallBinary(expr ast.Expr, +func (p *planBuilder) planCallBinary(expr ast.Expr, function string, overload string, impl *functions.Overload, @@ -364,7 +377,7 @@ func (p *planner) planCallBinary(expr ast.Expr, } // planCallVarArgs generates a variable argument callable Interpretable. -func (p *planner) planCallVarArgs(expr ast.Expr, +func (p *planBuilder) planCallVarArgs(expr ast.Expr, function string, overload string, impl *functions.Overload, @@ -392,7 +405,7 @@ func (p *planner) planCallVarArgs(expr ast.Expr, } // planCallEqual generates an equals (==) Interpretable. -func (p *planner) planCallEqual(expr ast.Expr, args []Interpretable) (Interpretable, error) { +func (p *planBuilder) planCallEqual(expr ast.Expr, args []Interpretable) (Interpretable, error) { return &evalEq{ id: expr.ID(), lhs: args[0], @@ -401,7 +414,7 @@ func (p *planner) planCallEqual(expr ast.Expr, args []Interpretable) (Interpreta } // planCallNotEqual generates a not equals (!=) Interpretable. -func (p *planner) planCallNotEqual(expr ast.Expr, args []Interpretable) (Interpretable, error) { +func (p *planBuilder) planCallNotEqual(expr ast.Expr, args []Interpretable) (Interpretable, error) { return &evalNe{ id: expr.ID(), lhs: args[0], @@ -410,7 +423,7 @@ func (p *planner) planCallNotEqual(expr ast.Expr, args []Interpretable) (Interpr } // planCallLogicalAnd generates a logical and (&&) Interpretable. -func (p *planner) planCallLogicalAnd(expr ast.Expr, args []Interpretable) (Interpretable, error) { +func (p *planBuilder) planCallLogicalAnd(expr ast.Expr, args []Interpretable) (Interpretable, error) { return &evalAnd{ id: expr.ID(), terms: args, @@ -418,7 +431,7 @@ func (p *planner) planCallLogicalAnd(expr ast.Expr, args []Interpretable) (Inter } // planCallLogicalOr generates a logical or (||) Interpretable. -func (p *planner) planCallLogicalOr(expr ast.Expr, args []Interpretable) (Interpretable, error) { +func (p *planBuilder) planCallLogicalOr(expr ast.Expr, args []Interpretable) (Interpretable, error) { return &evalOr{ id: expr.ID(), terms: args, @@ -426,7 +439,7 @@ func (p *planner) planCallLogicalOr(expr ast.Expr, args []Interpretable) (Interp } // planCallConditional generates a conditional / ternary (c ? t : f) Interpretable. -func (p *planner) planCallConditional(expr ast.Expr, args []Interpretable) (Interpretable, error) { +func (p *planBuilder) planCallConditional(expr ast.Expr, args []Interpretable) (Interpretable, error) { cond := args[0] t := args[1] var tAttr Attribute @@ -454,7 +467,7 @@ func (p *planner) planCallConditional(expr ast.Expr, args []Interpretable) (Inte // planCallIndex either extends an attribute with the argument to the index operation, or creates // a relative attribute based on the return of a function call or operation. -func (p *planner) planCallIndex(expr ast.Expr, args []Interpretable, optional bool) (Interpretable, error) { +func (p *planBuilder) planCallIndex(expr ast.Expr, args []Interpretable, optional bool) (Interpretable, error) { op := args[0] ind := args[1] opType := p.typeMap[op.ID()] @@ -489,7 +502,7 @@ func (p *planner) planCallIndex(expr ast.Expr, args []Interpretable, optional bo } // planCreateList generates a list construction Interpretable. -func (p *planner) planCreateList(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planCreateList(expr ast.Expr) (Interpretable, error) { list := expr.AsList() optionalIndices := list.OptionalIndices() elements := list.Elements() @@ -518,7 +531,7 @@ func (p *planner) planCreateList(expr ast.Expr) (Interpretable, error) { } // planCreateStruct generates a map or object construction Interpretable. -func (p *planner) planCreateMap(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planCreateMap(expr ast.Expr) (Interpretable, error) { m := expr.AsMap() entries := m.Entries() optionals := make([]bool, len(entries)) @@ -552,7 +565,7 @@ func (p *planner) planCreateMap(expr ast.Expr) (Interpretable, error) { } // planCreateObj generates an object construction Interpretable. -func (p *planner) planCreateStruct(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planCreateStruct(expr ast.Expr) (Interpretable, error) { obj := expr.AsStruct() typeName, defined := p.resolveTypeName(obj.TypeName()) if !defined { @@ -586,7 +599,7 @@ func (p *planner) planCreateStruct(expr ast.Expr) (Interpretable, error) { } // planComprehension generates an Interpretable fold operation. -func (p *planner) planComprehension(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planComprehension(expr ast.Expr) (Interpretable, error) { fold := expr.AsComprehension() accu, err := p.plan(fold.AccuInit()) if err != nil { @@ -596,6 +609,7 @@ func (p *planner) planComprehension(expr ast.Expr) (Interpretable, error) { if err != nil { return nil, err } + p.pushLocalVars(fold.AccuVar(), fold.IterVar(), fold.IterVar2()) cond, err := p.plan(fold.LoopCondition()) if err != nil { return nil, err @@ -604,10 +618,12 @@ func (p *planner) planComprehension(expr ast.Expr) (Interpretable, error) { if err != nil { return nil, err } + p.popLocalVars(fold.IterVar(), fold.IterVar2()) result, err := p.plan(fold.Result()) if err != nil { return nil, err } + p.popLocalVars(fold.AccuVar()) return &evalFold{ id: expr.ID(), accuVar: fold.AccuVar(), @@ -623,13 +639,13 @@ func (p *planner) planComprehension(expr ast.Expr) (Interpretable, error) { } // planConst generates a constant valued Interpretable. -func (p *planner) planConst(expr ast.Expr) (Interpretable, error) { +func (p *planBuilder) planConst(expr ast.Expr) (Interpretable, error) { return NewConstValue(expr.ID(), expr.AsLiteral()), nil } // resolveTypeName takes a qualified string constructed at parse time, applies the proto // namespace resolution rules to it in a scan over possible matching types in the TypeProvider. -func (p *planner) resolveTypeName(typeName string) (string, bool) { +func (p *planBuilder) resolveTypeName(typeName string) (string, bool) { for _, qualifiedTypeName := range p.container.ResolveCandidateNames(typeName) { if _, found := p.provider.FindStructType(qualifiedTypeName); found { return qualifiedTypeName, true @@ -646,7 +662,7 @@ func (p *planner) resolveTypeName(typeName string) (string, bool) { // - The target expression may only consist of ident and select expressions. // - The function is declared in the environment using its fully-qualified name. // - The fully-qualified function name matches the string serialized target value. -func (p *planner) resolveFunction(expr ast.Expr) (ast.Expr, string, string) { +func (p *planBuilder) resolveFunction(expr ast.Expr) (ast.Expr, string, string) { // Note: similar logic exists within the `checker/checker.go`. If making changes here // please consider the impact on checker.go and consolidate implementations or mirror code // as appropriate. @@ -687,7 +703,7 @@ func (p *planner) resolveFunction(expr ast.Expr) (ast.Expr, string, string) { // namespaced identifiers must be stripped, as all declarations already use fully-qualified // names. This stripping behavior is handled automatically by the ResolveCandidateNames // call. - return target, stripLeadingDot(fnName), "" + return target, strings.TrimPrefix(fnName, "."), "" } // Handle the situation where the function target actually indicates a qualified function name. @@ -710,7 +726,7 @@ func (p *planner) resolveFunction(expr ast.Expr) (ast.Expr, string, string) { // relativeAttr indicates that the attribute in this case acts as a qualifier and as such needs to // be observed to ensure that it's evaluation value is properly recorded for state tracking. -func (p *planner) relativeAttr(id int64, eval Interpretable, opt bool) (InterpretableAttribute, error) { +func (p *planBuilder) relativeAttr(id int64, eval Interpretable, opt bool) (InterpretableAttribute, error) { eAttr, ok := eval.(InterpretableAttribute) if !ok { eAttr = &evalAttr{ @@ -733,7 +749,7 @@ func (p *planner) relativeAttr(id int64, eval Interpretable, opt bool) (Interpre // toQualifiedName converts an expression AST into a qualified name if possible, with a boolean // 'found' value that indicates if the conversion is successful. -func (p *planner) toQualifiedName(operand ast.Expr) (string, bool) { +func (p *planBuilder) toQualifiedName(operand ast.Expr) (string, bool) { // If the checker identified the expression as an attribute by the type-checker, then it can't // possibly be part of qualified name in a namespace. _, isAttr := p.refMap[operand.ID()] @@ -759,9 +775,35 @@ func (p *planner) toQualifiedName(operand ast.Expr) (string, bool) { return "", false } -func stripLeadingDot(name string) string { - if strings.HasPrefix(name, ".") { - return name[1:] +func (p *planBuilder) pushLocalVars(names ...string) { + for _, name := range names { + if name == "" { + continue + } + if cnt, found := p.localVars[name]; found { + p.localVars[name] = cnt + 1 + } else { + p.localVars[name] = 1 + } + } +} + +func (p *planBuilder) popLocalVars(names ...string) { + for _, name := range names { + if name == "" { + continue + } + if cnt, found := p.localVars[name]; found { + if cnt == 1 { + delete(p.localVars, name) + } else { + p.localVars[name] = cnt - 1 + } + } } - return name +} + +func (p *planBuilder) isLocalVar(name string) bool { + _, found := p.localVars[name] + return found } diff --git a/vendor/github.com/google/cel-go/parser/helper.go b/vendor/github.com/google/cel-go/parser/helper.go index c13296dd5c..f960be20ed 100644 --- a/vendor/github.com/google/cel-go/parser/helper.go +++ b/vendor/github.com/google/cel-go/parser/helper.go @@ -159,7 +159,7 @@ func (p *parserHelper) id(ctx any) int64 { offset.Start = p.sourceInfo.ComputeOffset(int32(c.GetLine()), int32(c.GetColumn())) offset.Stop = offset.Start + int32(len(c.GetText())) case common.Location: - offset.Start = p.sourceInfo.ComputeOffset(int32(c.Line()), int32(c.Column())) + offset.Start = p.sourceInfo.ComputeOffsetAbsolute(int32(c.Line()), int32(c.Column())) offset.Stop = offset.Start case ast.OffsetRange: offset = c diff --git a/vendor/github.com/google/gnostic-models/extensions/extension.proto b/vendor/github.com/google/gnostic-models/extensions/extension.proto index 875137c1a8..a600429890 100644 --- a/vendor/github.com/google/gnostic-models/extensions/extension.proto +++ b/vendor/github.com/google/gnostic-models/extensions/extension.proto @@ -42,7 +42,7 @@ option java_package = "org.gnostic.v1"; option objc_class_prefix = "GNX"; // The Go package name. -option go_package = "./extensions;gnostic_extension_v1"; +option go_package = "github.com/google/gnostic-models/extensions;gnostic_extension_v1"; // The version number of Gnostic. message Version { diff --git a/vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.proto b/vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.proto index 1c59b2f4ae..49adafcc8e 100644 --- a/vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.proto +++ b/vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.proto @@ -42,7 +42,7 @@ option java_package = "org.openapi_v2"; option objc_class_prefix = "OAS"; // The Go package name. -option go_package = "./openapiv2;openapi_v2"; +option go_package = "github.com/google/gnostic-models/openapiv2;openapi_v2"; message AdditionalPropertiesItem { oneof oneof { diff --git a/vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.proto b/vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.proto index 1be335b89b..af4b6254bc 100644 --- a/vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.proto +++ b/vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.proto @@ -42,7 +42,7 @@ option java_package = "org.openapi_v3"; option objc_class_prefix = "OAS"; // The Go package name. -option go_package = "./openapiv3;openapi_v3"; +option go_package = "github.com/google/gnostic-models/openapiv3;openapi_v3"; message AdditionalPropertiesItem { oneof oneof { diff --git a/vendor/github.com/google/gnostic-models/openapiv3/annotations.proto b/vendor/github.com/google/gnostic-models/openapiv3/annotations.proto index 09ee0aac51..895b4567cd 100644 --- a/vendor/github.com/google/gnostic-models/openapiv3/annotations.proto +++ b/vendor/github.com/google/gnostic-models/openapiv3/annotations.proto @@ -20,7 +20,7 @@ import "google/protobuf/descriptor.proto"; import "openapiv3/OpenAPIv3.proto"; // The Go package name. -option go_package = "./openapiv3;openapi_v3"; +option go_package = "github.com/google/gnostic-models/openapiv3;openapi_v3"; // This option lets the proto compiler generate Java code inside the package // name (see below) instead of inside an outer class. It creates a simpler // developer experience by reducing one-level of name nesting and be diff --git a/vendor/github.com/operator-framework/api/pkg/lib/version/doc.go b/vendor/github.com/operator-framework/api/pkg/lib/version/doc.go new file mode 100644 index 0000000000..7318ee6d19 --- /dev/null +++ b/vendor/github.com/operator-framework/api/pkg/lib/version/doc.go @@ -0,0 +1,5 @@ +// +k8s:openapi-gen=true +// +k8s:openapi-model-package=com.github.operator-framework.api.pkg.operators.lib.version + +// Package version contains the OperatorVersion type. +package version diff --git a/vendor/github.com/operator-framework/api/pkg/lib/version/zz_generated.model_name.go b/vendor/github.com/operator-framework/api/pkg/lib/version/zz_generated.model_name.go new file mode 100644 index 0000000000..1fd31307ba --- /dev/null +++ b/vendor/github.com/operator-framework/api/pkg/lib/version/zz_generated.model_name.go @@ -0,0 +1,26 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by openapi-gen-v0.0. DO NOT EDIT. + +package version + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in OperatorVersion) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.lib.version.OperatorVersion" +} diff --git a/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/doc.go b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/doc.go index 74bc9b819a..fd9d1d8490 100644 --- a/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/doc.go +++ b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/doc.go @@ -1,6 +1,8 @@ // +groupName=operators.coreos.com // +k8s:deepcopy-gen=package // +k8s:conversion-gen=github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators +// +k8s:openapi-gen=true +// +k8s:openapi-model-package=com.github.operator-framework.api.pkg.operators.v1alpha1 // Package v1alpha1 contains resources types for version v1alpha1 of the operators.coreos.com API group. package v1alpha1 diff --git a/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/subscription_types.go b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/subscription_types.go index 7aa854f596..292fedf9b9 100644 --- a/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/subscription_types.go +++ b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/subscription_types.go @@ -232,8 +232,6 @@ type SubscriptionStatus struct { // CatalogHealth contains the Subscription's view of its relevant CatalogSources' status. // It is used to determine SubscriptionStatusConditions related to CatalogSources. // +optional - // +patchMergeKey= - // +patchStrategy=merge CatalogHealth []SubscriptionCatalogHealth `json:"catalogHealth,omitempty"` // Conditions is a list of the latest available observations about a Subscription's current state. diff --git a/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/zz_generated.model_name.go b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/zz_generated.model_name.go new file mode 100644 index 0000000000..555d6d7f09 --- /dev/null +++ b/vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/zz_generated.model_name.go @@ -0,0 +1,306 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by openapi-gen-v0.0. DO NOT EDIT. + +package v1alpha1 + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in APIResourceReference) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.APIResourceReference" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in APIServiceDefinitions) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.APIServiceDefinitions" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in APIServiceDescription) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.APIServiceDescription" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ActionDescriptor) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ActionDescriptor" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in AppLink) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.AppLink" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in BundleLookup) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.BundleLookup" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in BundleLookupCondition) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.BundleLookupCondition" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CRDDescription) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CRDDescription" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CatalogSource) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CatalogSource" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CatalogSourceList) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CatalogSourceList" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CatalogSourceSpec) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CatalogSourceSpec" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CatalogSourceStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CatalogSourceStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CleanupSpec) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CleanupSpec" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CleanupStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CleanupStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ClusterServiceVersion) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ClusterServiceVersion" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ClusterServiceVersionCondition) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ClusterServiceVersionCondition" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ClusterServiceVersionList) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ClusterServiceVersionList" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ClusterServiceVersionSpec) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ClusterServiceVersionSpec" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ClusterServiceVersionStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ClusterServiceVersionStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ConfigMapResourceReference) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ConfigMapResourceReference" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in CustomResourceDefinitions) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.CustomResourceDefinitions" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in DependentStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.DependentStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ExtractContentConfig) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ExtractContentConfig" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in GRPCConnectionState) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.GRPCConnectionState" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in GrpcPodConfig) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.GrpcPodConfig" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in Icon) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.Icon" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallMode) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallMode" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallPlan) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallPlan" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallPlanCondition) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallPlanCondition" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallPlanList) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallPlanList" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallPlanReference) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallPlanReference" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallPlanSpec) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallPlanSpec" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in InstallPlanStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.InstallPlanStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in Maintainer) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.Maintainer" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in NamedInstallStrategy) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.NamedInstallStrategy" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in RegistryPoll) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.RegistryPoll" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in RegistryServiceStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.RegistryServiceStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in RelatedImage) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.RelatedImage" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in RequirementStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.RequirementStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ResourceInstance) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ResourceInstance" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in ResourceList) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.ResourceList" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SpecDescriptor) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SpecDescriptor" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in StatusDescriptor) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.StatusDescriptor" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in Step) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.Step" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in StepResource) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.StepResource" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in StrategyDeploymentPermissions) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.StrategyDeploymentPermissions" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in StrategyDeploymentSpec) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.StrategyDeploymentSpec" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in StrategyDetailsDeployment) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.StrategyDetailsDeployment" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in Subscription) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.Subscription" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SubscriptionCatalogHealth) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SubscriptionCatalogHealth" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SubscriptionCondition) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SubscriptionCondition" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SubscriptionConfig) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SubscriptionConfig" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SubscriptionList) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SubscriptionList" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SubscriptionSpec) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SubscriptionSpec" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in SubscriptionStatus) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.SubscriptionStatus" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in UpdateStrategy) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.UpdateStrategy" +} + +// OpenAPIModelName returns the OpenAPI model name for this type. +func (in WebhookDescription) OpenAPIModelName() string { + return "com.github.operator-framework.api.pkg.operators.v1alpha1.WebhookDescription" +} diff --git a/vendor/github.com/prometheus/common/expfmt/expfmt.go b/vendor/github.com/prometheus/common/expfmt/expfmt.go index c34c7de432..4e4c13e724 100644 --- a/vendor/github.com/prometheus/common/expfmt/expfmt.go +++ b/vendor/github.com/prometheus/common/expfmt/expfmt.go @@ -45,6 +45,7 @@ const ( // The Content-Type values for the different wire protocols. Do not do direct // comparisons to these constants, instead use the comparison functions. + // // Deprecated: Use expfmt.NewFormat(expfmt.TypeUnknown) instead. FmtUnknown Format = `` // Deprecated: Use expfmt.NewFormat(expfmt.TypeTextPlain) instead. diff --git a/vendor/github.com/prometheus/common/expfmt/fuzz.go b/vendor/github.com/prometheus/common/expfmt/fuzz.go index 0290f6abc4..872c0c15b4 100644 --- a/vendor/github.com/prometheus/common/expfmt/fuzz.go +++ b/vendor/github.com/prometheus/common/expfmt/fuzz.go @@ -13,7 +13,6 @@ // Build only when actually fuzzing //go:build gofuzz -// +build gofuzz package expfmt diff --git a/vendor/github.com/stoewer/go-strcase/.gitignore b/vendor/github.com/stoewer/go-strcase/.gitignore deleted file mode 100644 index db5247b944..0000000000 --- a/vendor/github.com/stoewer/go-strcase/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -vendor -doc - -# Temporary files -*~ -*.swp - -# Editor and IDE config -.idea -*.iml -.vscode diff --git a/vendor/github.com/stoewer/go-strcase/.golangci.yml b/vendor/github.com/stoewer/go-strcase/.golangci.yml deleted file mode 100644 index 0e75d86ae0..0000000000 --- a/vendor/github.com/stoewer/go-strcase/.golangci.yml +++ /dev/null @@ -1,19 +0,0 @@ -version: "2" - -linters: - enable: - - dupl - - goconst - - gocyclo - - godox - - gosec - - lll - - misspell - - prealloc - - staticcheck - - unconvert - - unparam - -formatters: - enable: - - gofmt diff --git a/vendor/github.com/stoewer/go-strcase/LICENSE b/vendor/github.com/stoewer/go-strcase/LICENSE deleted file mode 100644 index a105a3819a..0000000000 --- a/vendor/github.com/stoewer/go-strcase/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017, Adrian Stoewer - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/stoewer/go-strcase/README.md b/vendor/github.com/stoewer/go-strcase/README.md deleted file mode 100644 index 84a640e714..0000000000 --- a/vendor/github.com/stoewer/go-strcase/README.md +++ /dev/null @@ -1,50 +0,0 @@ -[![GH Actions](https://github.com/stoewer/go-strcase/actions/workflows/lint-test.yml/badge.svg?branch=master)](https://github.com/stoewer/go-strcase/actions) -[![codecov](https://codecov.io/github/stoewer/go-strcase/branch/master/graph/badge.svg?token=c0UokYnop5)](https://codecov.io/github/stoewer/go-strcase) -[![GoDoc](https://godoc.org/github.com/stoewer/go-strcase?status.svg)](https://pkg.go.dev/github.com/stoewer/go-strcase) ---- - -Go strcase -========== - -The package `strcase` converts between different kinds of naming formats such as camel case -(`CamelCase`), snake case (`snake_case`) or kebab case (`kebab-case`). -The package is designed to work only with strings consisting of standard ASCII letters. -Unicode is currently not supported. - -Versioning and stability ------------------------- - -Although the master branch is supposed to remain always backward compatible, the repository -contains version tags in order to support vendoring tools. -The tag names follow semantic versioning conventions and have the following format `v1.0.0`. -This package supports Go modules introduced with version 1.11. - -Example -------- - -```go -import "github.com/stoewer/go-strcase" - -var snake = strcase.SnakeCase("CamelCase") -``` - -Dependencies ------------- - -### Build dependencies - -* none - -### Test dependencies - -* `github.com/stretchr/testify` - -Run linters and unit tests --------------------------- - -To run the static code analysis, linters and tests use the following commands: - -``` -golangci-lint run --config .golangci.yml ./... -go test ./... -``` diff --git a/vendor/github.com/stoewer/go-strcase/camel.go b/vendor/github.com/stoewer/go-strcase/camel.go deleted file mode 100644 index 7a9bec7c10..0000000000 --- a/vendor/github.com/stoewer/go-strcase/camel.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2017, A. Stoewer -// All rights reserved. - -package strcase - -import ( - "strings" -) - -// UpperCamelCase converts a string into camel case starting with a upper case letter. -func UpperCamelCase(s string) string { - return camelCase(s, true) -} - -// LowerCamelCase converts a string into camel case starting with a lower case letter. -func LowerCamelCase(s string) string { - return camelCase(s, false) -} - -func camelCase(s string, upper bool) string { - s = strings.TrimSpace(s) - buffer := make([]rune, 0, len(s)) - - stringIter(s, func(prev, curr, next rune) { - if !isDelimiter(curr) { - if isDelimiter(prev) || (upper && prev == 0) { - buffer = append(buffer, toUpper(curr)) - } else if isLower(prev) { - buffer = append(buffer, curr) - } else if isUpper(prev) && isUpper(curr) && isLower(next) { - // Assume a case like "R" for "XRequestId" - buffer = append(buffer, curr) - } else if isUpper(curr) && isDigit(prev) { - // Preserve uppercase letters after numbers - buffer = append(buffer, curr) - } else { - buffer = append(buffer, toLower(curr)) - } - } - }) - - return string(buffer) -} diff --git a/vendor/github.com/stoewer/go-strcase/doc.go b/vendor/github.com/stoewer/go-strcase/doc.go deleted file mode 100644 index 3e441ca3ef..0000000000 --- a/vendor/github.com/stoewer/go-strcase/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2017, A. Stoewer -// All rights reserved. - -// Package strcase converts between different kinds of naming formats such as camel case -// (CamelCase), snake case (snake_case) or kebab case (kebab-case). The package is designed -// to work only with strings consisting of standard ASCII letters. Unicode is currently not -// supported. -package strcase diff --git a/vendor/github.com/stoewer/go-strcase/helper.go b/vendor/github.com/stoewer/go-strcase/helper.go deleted file mode 100644 index 96e79d6e13..0000000000 --- a/vendor/github.com/stoewer/go-strcase/helper.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2017, A. Stoewer -// All rights reserved. - -package strcase - -// isLower checks if a character is lower case. More precisely it evaluates if it is -// in the range of ASCII character 'a' to 'z'. -func isLower(ch rune) bool { - return ch >= 'a' && ch <= 'z' -} - -// toLower converts a character in the range of ASCII characters 'A' to 'Z' to its lower -// case counterpart. Other characters remain the same. -func toLower(ch rune) rune { - if ch >= 'A' && ch <= 'Z' { - return ch + 32 - } - return ch -} - -// isLower checks if a character is upper case. More precisely it evaluates if it is -// in the range of ASCII characters 'A' to 'Z'. -func isUpper(ch rune) bool { - return ch >= 'A' && ch <= 'Z' -} - -// toLower converts a character in the range of ASCII characters 'a' to 'z' to its lower -// case counterpart. Other characters remain the same. -func toUpper(ch rune) rune { - if ch >= 'a' && ch <= 'z' { - return ch - 32 - } - return ch -} - -// isSpace checks if a character is some kind of whitespace. -func isSpace(ch rune) bool { - return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' -} - -// isDigit checks if a character is a digit. More precisely it evaluates if it is -// in the range of ASCII characters '0' to '9'. -func isDigit(ch rune) bool { - return ch >= '0' && ch <= '9' -} - -// isDelimiter checks if a character is some kind of whitespace or '_' or '-'. -func isDelimiter(ch rune) bool { - return ch == '-' || ch == '_' || isSpace(ch) -} - -// iterFunc is a callback that is called fro a specific position in a string. Its arguments are the -// rune at the respective string position as well as the previous and the next rune. If curr is at the -// first position of the string prev is zero. If curr is at the end of the string next is zero. -type iterFunc func(prev, curr, next rune) - -// stringIter iterates over a string, invoking the callback for every single rune in the string. -func stringIter(s string, callback iterFunc) { - var prev rune - var curr rune - for _, next := range s { - if curr == 0 { - prev = curr - curr = next - continue - } - - callback(prev, curr, next) - - prev = curr - curr = next - } - - if len(s) > 0 { - callback(prev, curr, 0) - } -} diff --git a/vendor/github.com/stoewer/go-strcase/kebab.go b/vendor/github.com/stoewer/go-strcase/kebab.go deleted file mode 100644 index e9a6487579..0000000000 --- a/vendor/github.com/stoewer/go-strcase/kebab.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2017, A. Stoewer -// All rights reserved. - -package strcase - -// KebabCase converts a string into kebab case. -func KebabCase(s string) string { - return delimiterCase(s, '-', false) -} - -// UpperKebabCase converts a string into kebab case with capital letters. -func UpperKebabCase(s string) string { - return delimiterCase(s, '-', true) -} diff --git a/vendor/github.com/stoewer/go-strcase/snake.go b/vendor/github.com/stoewer/go-strcase/snake.go deleted file mode 100644 index 1b216e20cf..0000000000 --- a/vendor/github.com/stoewer/go-strcase/snake.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2017, A. Stoewer -// All rights reserved. - -package strcase - -import ( - "strings" -) - -// SnakeCase converts a string into snake case. -func SnakeCase(s string) string { - return delimiterCase(s, '_', false) -} - -// UpperSnakeCase converts a string into snake case with capital letters. -func UpperSnakeCase(s string) string { - return delimiterCase(s, '_', true) -} - -// delimiterCase converts a string into snake_case or kebab-case depending on the delimiter passed -// as second argument. When upperCase is true the result will be UPPER_SNAKE_CASE or UPPER-KEBAB-CASE. -func delimiterCase(s string, delimiter rune, upperCase bool) string { - s = strings.TrimSpace(s) - buffer := make([]rune, 0, len(s)+3) - - adjustCase := toLower - if upperCase { - adjustCase = toUpper - } - - var prev rune - var curr rune - for _, next := range s { - if isDelimiter(curr) { - if !isDelimiter(prev) { - buffer = append(buffer, delimiter) - } - } else if isUpper(curr) { - if isLower(prev) || (isUpper(prev) && isLower(next)) { - buffer = append(buffer, delimiter) - } - buffer = append(buffer, adjustCase(curr)) - } else if curr != 0 { - buffer = append(buffer, adjustCase(curr)) - } - prev = curr - curr = next - } - - if len(s) > 0 { - if isUpper(curr) && isLower(prev) && prev != 0 { - buffer = append(buffer, delimiter) - } - buffer = append(buffer, adjustCase(curr)) - } - - return string(buffer) -} diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/client.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/client.go deleted file mode 100644 index 521daa25db..0000000000 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/client.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" - -import ( - "context" - "io" - "net/http" - "net/url" - "strings" -) - -// DefaultClient is the default Client and is used by Get, Head, Post and PostForm. -// Please be careful of initialization order - for example, if you change -// the global propagator, the DefaultClient might still be using the old one. -var DefaultClient = &http.Client{Transport: NewTransport(http.DefaultTransport)} - -// Get is a convenient replacement for http.Get that adds a span around the request. -func Get(ctx context.Context, targetURL string) (resp *http.Response, err error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, targetURL, http.NoBody) - if err != nil { - return nil, err - } - return DefaultClient.Do(req) -} - -// Head is a convenient replacement for http.Head that adds a span around the request. -func Head(ctx context.Context, targetURL string) (resp *http.Response, err error) { - req, err := http.NewRequestWithContext(ctx, http.MethodHead, targetURL, http.NoBody) - if err != nil { - return nil, err - } - return DefaultClient.Do(req) -} - -// Post is a convenient replacement for http.Post that adds a span around the request. -func Post(ctx context.Context, targetURL, contentType string, body io.Reader) (resp *http.Response, err error) { - req, err := http.NewRequestWithContext(ctx, http.MethodPost, targetURL, body) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", contentType) - return DefaultClient.Do(req) -} - -// PostForm is a convenient replacement for http.PostForm that adds a span around the request. -func PostForm(ctx context.Context, targetURL string, data url.Values) (resp *http.Response, err error) { - return Post(ctx, targetURL, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) -} diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go index a83a026274..3ae0824344 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go @@ -23,5 +23,5 @@ const ( type Filter func(*http.Request) bool func newTracer(tp trace.TracerProvider) trace.Tracer { - return tp.Tracer(ScopeName, trace.WithInstrumentationVersion(Version())) + return tp.Tracer(ScopeName, trace.WithInstrumentationVersion(Version)) } diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go index 38fb79c032..f7338b7e52 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go @@ -26,7 +26,6 @@ type config struct { Meter metric.Meter Propagators propagation.TextMapPropagator SpanStartOptions []trace.SpanStartOption - PublicEndpoint bool PublicEndpointFn func(*http.Request) bool ReadEvent bool WriteEvent bool @@ -67,7 +66,7 @@ func newConfig(opts ...Option) *config { c.Meter = c.MeterProvider.Meter( ScopeName, - metric.WithInstrumentationVersion(Version()), + metric.WithInstrumentationVersion(Version), ) return c @@ -93,20 +92,10 @@ func WithMeterProvider(provider metric.MeterProvider) Option { }) } -// WithPublicEndpoint configures the Handler to link the span with an incoming -// span context. If this option is not provided, then the association is a child -// association instead of a link. -func WithPublicEndpoint() Option { - return optionFunc(func(c *config) { - c.PublicEndpoint = true - }) -} - // WithPublicEndpointFn runs with every request, and allows conditionally // configuring the Handler to link the span with an incoming span context. If // this option is not provided or returns false, then the association is a // child association instead of a link. -// Note: WithPublicEndpoint takes precedence over WithPublicEndpointFn. func WithPublicEndpointFn(fn func(*http.Request) bool) Option { return optionFunc(func(c *config) { c.PublicEndpointFn = fn @@ -143,11 +132,13 @@ func WithFilter(f Filter) Option { }) } -type event int +// Event represents message event types for [WithMessageEvents]. +type Event int // Different types of events that can be recorded, see WithMessageEvents. const ( - ReadEvents event = iota + unspecifiedEvents Event = iota + ReadEvents WriteEvents ) @@ -160,7 +151,7 @@ const ( // using the ReadBytesKey // - WriteEvents: Record the number of bytes written after every http.ResponeWriter.Write // using the WriteBytesKey -func WithMessageEvents(events ...event) Option { +func WithMessageEvents(events ...Event) Option { return optionFunc(func(c *config) { for _, e := range events { switch e { diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/doc.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/doc.go index 56b24b982a..1c9aa3ff42 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/doc.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/doc.go @@ -2,6 +2,5 @@ // SPDX-License-Identifier: Apache-2.0 // Package otelhttp provides an http.Handler and functions that are intended -// to be used to add tracing by wrapping existing handlers (with Handler) and -// routes WithRouteTag. +// to be used to add tracing by wrapping existing handlers. package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go index fef83b42fe..1ecd4be2dd 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go @@ -29,7 +29,6 @@ type middleware struct { writeEvent bool filters []Filter spanNameFormatter func(string, *http.Request) string - publicEndpoint bool publicEndpointFn func(*http.Request) bool metricAttributesFn func(*http.Request) []attribute.KeyValue @@ -77,7 +76,6 @@ func (h *middleware) configure(c *config) { h.writeEvent = c.WriteEvent h.filters = c.Filters h.spanNameFormatter = c.SpanNameFormatter - h.publicEndpoint = c.PublicEndpoint h.publicEndpointFn = c.PublicEndpointFn h.server = c.ServerName h.semconv = semconv.NewHTTPServer(c.Meter) @@ -102,7 +100,7 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http } opts = append(opts, h.spanStartOptions...) - if h.publicEndpoint || (h.publicEndpointFn != nil && h.publicEndpointFn(r.WithContext(ctx))) { + if h.publicEndpointFn != nil && h.publicEndpointFn(r.WithContext(ctx)) { opts = append(opts, trace.WithNewRoot()) // Linking incoming span context if any for public endpoint. if s := trace.SpanContextFromContext(ctx); s.IsValid() && s.IsRemote() { @@ -221,18 +219,3 @@ func (h *middleware) metricAttributesFromRequest(r *http.Request) []attribute.Ke } return attributeForRequest } - -// WithRouteTag annotates spans and metrics with the provided route name -// with HTTP route attribute. -func WithRouteTag(route string, h http.Handler) http.Handler { - attr := semconv.NewHTTPServer(nil).Route(route) - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - span := trace.SpanFromContext(r.Context()) - span.SetAttributes(attr) - - labeler, _ := LabelerFromContext(r.Context()) - labeler.Add(attr) - - h.ServeHTTP(w, r) - }) -} diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/client.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/client.go new file mode 100644 index 0000000000..29d6f508c1 --- /dev/null +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/client.go @@ -0,0 +1,287 @@ +// Code generated by gotmpl. DO NOT MODIFY. +// source: internal/shared/semconv/client.go.tmpl + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package semconv provides OpenTelemetry semantic convention types and +// functionality. +package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv" + +import ( + "context" + "fmt" + "net/http" + "slices" + "strconv" + "strings" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/semconv/v1.39.0" + "go.opentelemetry.io/otel/semconv/v1.39.0/httpconv" +) + +type HTTPClient struct{ + requestBodySize httpconv.ClientRequestBodySize + requestDuration httpconv.ClientRequestDuration +} + +func NewHTTPClient(meter metric.Meter) HTTPClient { + client := HTTPClient{} + + var err error + client.requestBodySize, err = httpconv.NewClientRequestBodySize(meter) + handleErr(err) + + client.requestDuration, err = httpconv.NewClientRequestDuration( + meter, + metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10), + ) + handleErr(err) + + return client +} + +func (n HTTPClient) Status(code int) (codes.Code, string) { + if code < 100 || code >= 600 { + return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) + } + if code >= 400 { + return codes.Error, "" + } + return codes.Unset, "" +} + +// RequestTraceAttrs returns trace attributes for an HTTP request made by a client. +func (n HTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue { + /* + below attributes are returned: + - http.request.method + - http.request.method.original + - url.full + - server.address + - server.port + - network.protocol.name + - network.protocol.version + */ + numOfAttributes := 3 // URL, server address, proto, and method. + + var urlHost string + if req.URL != nil { + urlHost = req.URL.Host + } + var requestHost string + var requestPort int + for _, hostport := range []string{urlHost, req.Header.Get("Host")} { + requestHost, requestPort = SplitHostPort(hostport) + if requestHost != "" || requestPort > 0 { + break + } + } + + eligiblePort := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort) + if eligiblePort > 0 { + numOfAttributes++ + } + useragent := req.UserAgent() + if useragent != "" { + numOfAttributes++ + } + + protoName, protoVersion := netProtocol(req.Proto) + if protoName != "" && protoName != "http" { + numOfAttributes++ + } + if protoVersion != "" { + numOfAttributes++ + } + + method, originalMethod := n.method(req.Method) + if originalMethod != (attribute.KeyValue{}) { + numOfAttributes++ + } + + attrs := make([]attribute.KeyValue, 0, numOfAttributes) + + attrs = append(attrs, method) + if originalMethod != (attribute.KeyValue{}) { + attrs = append(attrs, originalMethod) + } + + var u string + if req.URL != nil { + // Remove any username/password info that may be in the URL. + userinfo := req.URL.User + req.URL.User = nil + u = req.URL.String() + // Restore any username/password info that was removed. + req.URL.User = userinfo + } + attrs = append(attrs, semconv.URLFull(u)) + + attrs = append(attrs, semconv.ServerAddress(requestHost)) + if eligiblePort > 0 { + attrs = append(attrs, semconv.ServerPort(eligiblePort)) + } + + if protoName != "" && protoName != "http" { + attrs = append(attrs, semconv.NetworkProtocolName(protoName)) + } + if protoVersion != "" { + attrs = append(attrs, semconv.NetworkProtocolVersion(protoVersion)) + } + + return attrs +} + +// ResponseTraceAttrs returns trace attributes for an HTTP response made by a client. +func (n HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue { + /* + below attributes are returned: + - http.response.status_code + - error.type + */ + var count int + if resp.StatusCode > 0 { + count++ + } + + if isErrorStatusCode(resp.StatusCode) { + count++ + } + + attrs := make([]attribute.KeyValue, 0, count) + if resp.StatusCode > 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(resp.StatusCode)) + } + + if isErrorStatusCode(resp.StatusCode) { + errorType := strconv.Itoa(resp.StatusCode) + attrs = append(attrs, semconv.ErrorTypeKey.String(errorType)) + } + return attrs +} + +func (n HTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) { + if method == "" { + return semconv.HTTPRequestMethodGet, attribute.KeyValue{} + } + if attr, ok := methodLookup[method]; ok { + return attr, attribute.KeyValue{} + } + + orig := semconv.HTTPRequestMethodOriginal(method) + if attr, ok := methodLookup[strings.ToUpper(method)]; ok { + return attr, orig + } + return semconv.HTTPRequestMethodGet, orig +} + +func (n HTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue { + num := len(additionalAttributes) + 2 + var h string + if req.URL != nil { + h = req.URL.Host + } + var requestHost string + var requestPort int + for _, hostport := range []string{h, req.Header.Get("Host")} { + requestHost, requestPort = SplitHostPort(hostport) + if requestHost != "" || requestPort > 0 { + break + } + } + + port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort) + if port > 0 { + num++ + } + + protoName, protoVersion := netProtocol(req.Proto) + if protoName != "" { + num++ + } + if protoVersion != "" { + num++ + } + + if statusCode > 0 { + num++ + } + + attributes := slices.Grow(additionalAttributes, num) + attributes = append(attributes, + semconv.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)), + semconv.ServerAddress(requestHost), + n.scheme(req), + ) + + if port > 0 { + attributes = append(attributes, semconv.ServerPort(port)) + } + if protoName != "" { + attributes = append(attributes, semconv.NetworkProtocolName(protoName)) + } + if protoVersion != "" { + attributes = append(attributes, semconv.NetworkProtocolVersion(protoVersion)) + } + + if statusCode > 0 { + attributes = append(attributes, semconv.HTTPResponseStatusCode(statusCode)) + } + return attributes +} + +type MetricOpts struct { + measurement metric.MeasurementOption + addOptions metric.AddOption +} + +func (o MetricOpts) MeasurementOption() metric.MeasurementOption { + return o.measurement +} + +func (o MetricOpts) AddOptions() metric.AddOption { + return o.addOptions +} + +func (n HTTPClient) MetricOptions(ma MetricAttributes) map[string]MetricOpts { + opts := map[string]MetricOpts{} + + attributes := n.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes) + set := metric.WithAttributeSet(attribute.NewSet(attributes...)) + opts["new"] = MetricOpts{ + measurement: set, + addOptions: set, + } + + return opts +} + +func (n HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts map[string]MetricOpts) { + n.requestBodySize.Inst().Record(ctx, md.RequestSize, opts["new"].MeasurementOption()) + n.requestDuration.Inst().Record(ctx, md.ElapsedTime/1000, opts["new"].MeasurementOption()) +} + +// TraceAttributes returns attributes for httptrace. +func (n HTTPClient) TraceAttributes(host string) []attribute.KeyValue { + return []attribute.KeyValue{ + semconv.ServerAddress(host), + } +} + +func (n HTTPClient) scheme(req *http.Request) attribute.KeyValue { + if req.URL != nil && req.URL.Scheme != "" { + return semconv.URLScheme(req.URL.Scheme) + } + if req.TLS != nil { + return semconv.URLScheme("https") + } + return semconv.URLScheme("http") +} + +func isErrorStatusCode(code int) bool { + return code >= 400 || code < 100 +} diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/env.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/env.go deleted file mode 100644 index 821b80ec41..0000000000 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/env.go +++ /dev/null @@ -1,248 +0,0 @@ -// Code generated by gotmpl. DO NOT MODIFY. -// source: internal/shared/semconv/env.go.tmpl - -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv" - -import ( - "context" - "fmt" - "net/http" - "strings" - "sync" - - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/semconv/v1.37.0/httpconv" -) - -// OTelSemConvStabilityOptIn is an environment variable. -// That can be set to "http/dup" to keep getting the old HTTP semantic conventions. -const OTelSemConvStabilityOptIn = "OTEL_SEMCONV_STABILITY_OPT_IN" - -type ResponseTelemetry struct { - StatusCode int - ReadBytes int64 - ReadError error - WriteBytes int64 - WriteError error -} - -type HTTPServer struct { - requestBodySizeHistogram httpconv.ServerRequestBodySize - responseBodySizeHistogram httpconv.ServerResponseBodySize - requestDurationHistogram httpconv.ServerRequestDuration -} - -// RequestTraceAttrs returns trace attributes for an HTTP request received by a -// server. -// -// The server must be the primary server name if it is known. For example this -// would be the ServerName directive -// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache -// server, and the server_name directive -// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an -// nginx server. More generically, the primary server name would be the host -// header value that matches the default virtual host of an HTTP server. It -// should include the host identifier and if a port is used to route to the -// server that port identifier should be included as an appropriate port -// suffix. -// -// If the primary server name is not known, server should be an empty string. -// The req Host will be used to determine the server instead. -func (s HTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue { - return CurrentHTTPServer{}.RequestTraceAttrs(server, req, opts) -} - -func (s HTTPServer) NetworkTransportAttr(network string) []attribute.KeyValue { - return []attribute.KeyValue{ - CurrentHTTPServer{}.NetworkTransportAttr(network), - } -} - -// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP response. -// -// If any of the fields in the ResponseTelemetry are not set the attribute will be omitted. -func (s HTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue { - return CurrentHTTPServer{}.ResponseTraceAttrs(resp) -} - -// Route returns the attribute for the route. -func (s HTTPServer) Route(route string) attribute.KeyValue { - return CurrentHTTPServer{}.Route(route) -} - -// Status returns a span status code and message for an HTTP status code -// value returned by a server. Status codes in the 400-499 range are not -// returned as errors. -func (s HTTPServer) Status(code int) (codes.Code, string) { - if code < 100 || code >= 600 { - return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) - } - if code >= 500 { - return codes.Error, "" - } - return codes.Unset, "" -} - -type ServerMetricData struct { - ServerName string - ResponseSize int64 - - MetricData - MetricAttributes -} - -type MetricAttributes struct { - Req *http.Request - StatusCode int - AdditionalAttributes []attribute.KeyValue -} - -type MetricData struct { - RequestSize int64 - - // The request duration, in milliseconds - ElapsedTime float64 -} - -var ( - metricAddOptionPool = &sync.Pool{ - New: func() any { - return &[]metric.AddOption{} - }, - } - - metricRecordOptionPool = &sync.Pool{ - New: func() any { - return &[]metric.RecordOption{} - }, - } -) - -func (s HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) { - attributes := CurrentHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes) - o := metric.WithAttributeSet(attribute.NewSet(attributes...)) - recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption) - *recordOpts = append(*recordOpts, o) - s.requestBodySizeHistogram.Inst().Record(ctx, md.RequestSize, *recordOpts...) - s.responseBodySizeHistogram.Inst().Record(ctx, md.ResponseSize, *recordOpts...) - s.requestDurationHistogram.Inst().Record(ctx, md.ElapsedTime/1000.0, o) - *recordOpts = (*recordOpts)[:0] - metricRecordOptionPool.Put(recordOpts) -} - -// hasOptIn returns true if the comma-separated version string contains the -// exact optIn value. -func hasOptIn(version, optIn string) bool { - for _, v := range strings.Split(version, ",") { - if strings.TrimSpace(v) == optIn { - return true - } - } - return false -} - -func NewHTTPServer(meter metric.Meter) HTTPServer { - server := HTTPServer{} - - var err error - server.requestBodySizeHistogram, err = httpconv.NewServerRequestBodySize(meter) - handleErr(err) - - server.responseBodySizeHistogram, err = httpconv.NewServerResponseBodySize(meter) - handleErr(err) - - server.requestDurationHistogram, err = httpconv.NewServerRequestDuration( - meter, - metric.WithExplicitBucketBoundaries( - 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, - 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, - ), - ) - handleErr(err) - return server -} - -type HTTPClient struct { - requestBodySize httpconv.ClientRequestBodySize - requestDuration httpconv.ClientRequestDuration -} - -func NewHTTPClient(meter metric.Meter) HTTPClient { - client := HTTPClient{} - - var err error - client.requestBodySize, err = httpconv.NewClientRequestBodySize(meter) - handleErr(err) - - client.requestDuration, err = httpconv.NewClientRequestDuration( - meter, - metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10), - ) - handleErr(err) - - return client -} - -// RequestTraceAttrs returns attributes for an HTTP request made by a client. -func (c HTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue { - return CurrentHTTPClient{}.RequestTraceAttrs(req) -} - -// ResponseTraceAttrs returns metric attributes for an HTTP request made by a client. -func (c HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue { - return CurrentHTTPClient{}.ResponseTraceAttrs(resp) -} - -func (c HTTPClient) Status(code int) (codes.Code, string) { - if code < 100 || code >= 600 { - return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) - } - if code >= 400 { - return codes.Error, "" - } - return codes.Unset, "" -} - -func (c HTTPClient) ErrorType(err error) attribute.KeyValue { - return CurrentHTTPClient{}.ErrorType(err) -} - -type MetricOpts struct { - measurement metric.MeasurementOption - addOptions metric.AddOption -} - -func (o MetricOpts) MeasurementOption() metric.MeasurementOption { - return o.measurement -} - -func (o MetricOpts) AddOptions() metric.AddOption { - return o.addOptions -} - -func (c HTTPClient) MetricOptions(ma MetricAttributes) map[string]MetricOpts { - opts := map[string]MetricOpts{} - - attributes := CurrentHTTPClient{}.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes) - set := metric.WithAttributeSet(attribute.NewSet(attributes...)) - opts["new"] = MetricOpts{ - measurement: set, - addOptions: set, - } - - return opts -} - -func (s HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts map[string]MetricOpts) { - s.requestBodySize.Inst().Record(ctx, md.RequestSize, opts["new"].MeasurementOption()) - s.requestDuration.Inst().Record(ctx, md.ElapsedTime/1000, opts["new"].MeasurementOption()) -} - -func (s HTTPClient) TraceAttributes(host string) []attribute.KeyValue { - return CurrentHTTPClient{}.TraceAttributes(host) -} diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/gen.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/gen.go index 1bb207b809..a8a0d58df3 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/gen.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/gen.go @@ -6,10 +6,10 @@ package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/ // Generate semconv package: //go:generate gotmpl --body=../../../../../../internal/shared/semconv/bench_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=bench_test.go //go:generate gotmpl --body=../../../../../../internal/shared/semconv/common_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=common_test.go -//go:generate gotmpl --body=../../../../../../internal/shared/semconv/env.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=env.go -//go:generate gotmpl --body=../../../../../../internal/shared/semconv/env_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=env_test.go -//go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconv.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=httpconv.go -//go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconv_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=httpconv_test.go +//go:generate gotmpl --body=../../../../../../internal/shared/semconv/server.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=server.go +//go:generate gotmpl --body=../../../../../../internal/shared/semconv/server_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=server_test.go +//go:generate gotmpl --body=../../../../../../internal/shared/semconv/client.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=client.go +//go:generate gotmpl --body=../../../../../../internal/shared/semconv/client_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=client_test.go //go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconvtest_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=httpconvtest_test.go //go:generate gotmpl --body=../../../../../../internal/shared/semconv/util.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=util.go //go:generate gotmpl --body=../../../../../../internal/shared/semconv/util_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=util_test.go diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/httpconv.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/httpconv.go deleted file mode 100644 index 28c51a3b38..0000000000 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/httpconv.go +++ /dev/null @@ -1,517 +0,0 @@ -// Code generated by gotmpl. DO NOT MODIFY. -// source: internal/shared/semconv/httpconv.go.tmpl - -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -// Package semconv provides OpenTelemetry semantic convention types and -// functionality. -package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv" - -import ( - "fmt" - "net/http" - "reflect" - "slices" - "strconv" - "strings" - - "go.opentelemetry.io/otel/attribute" - semconvNew "go.opentelemetry.io/otel/semconv/v1.37.0" -) - -type RequestTraceAttrsOpts struct { - // If set, this is used as value for the "http.client_ip" attribute. - HTTPClientIP string -} - -type CurrentHTTPServer struct{} - -// RequestTraceAttrs returns trace attributes for an HTTP request received by a -// server. -// -// The server must be the primary server name if it is known. For example this -// would be the ServerName directive -// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache -// server, and the server_name directive -// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an -// nginx server. More generically, the primary server name would be the host -// header value that matches the default virtual host of an HTTP server. It -// should include the host identifier and if a port is used to route to the -// server that port identifier should be included as an appropriate port -// suffix. -// -// If the primary server name is not known, server should be an empty string. -// The req Host will be used to determine the server instead. -func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue { - count := 3 // ServerAddress, Method, Scheme - - var host string - var p int - if server == "" { - host, p = SplitHostPort(req.Host) - } else { - // Prioritize the primary server name. - host, p = SplitHostPort(server) - if p < 0 { - _, p = SplitHostPort(req.Host) - } - } - - hostPort := requiredHTTPPort(req.TLS != nil, p) - if hostPort > 0 { - count++ - } - - method, methodOriginal := n.method(req.Method) - if methodOriginal != (attribute.KeyValue{}) { - count++ - } - - scheme := n.scheme(req.TLS != nil) - - peer, peerPort := SplitHostPort(req.RemoteAddr) - if peer != "" { - // The Go HTTP server sets RemoteAddr to "IP:port", this will not be a - // file-path that would be interpreted with a sock family. - count++ - if peerPort > 0 { - count++ - } - } - - useragent := req.UserAgent() - if useragent != "" { - count++ - } - - // For client IP, use, in order: - // 1. The value passed in the options - // 2. The value in the X-Forwarded-For header - // 3. The peer address - clientIP := opts.HTTPClientIP - if clientIP == "" { - clientIP = serverClientIP(req.Header.Get("X-Forwarded-For")) - if clientIP == "" { - clientIP = peer - } - } - if clientIP != "" { - count++ - } - - if req.URL != nil && req.URL.Path != "" { - count++ - } - - protoName, protoVersion := netProtocol(req.Proto) - if protoName != "" && protoName != "http" { - count++ - } - if protoVersion != "" { - count++ - } - - route := httpRoute(req.Pattern) - if route != "" { - count++ - } - - attrs := make([]attribute.KeyValue, 0, count) - attrs = append(attrs, - semconvNew.ServerAddress(host), - method, - scheme, - ) - - if hostPort > 0 { - attrs = append(attrs, semconvNew.ServerPort(hostPort)) - } - if methodOriginal != (attribute.KeyValue{}) { - attrs = append(attrs, methodOriginal) - } - - if peer, peerPort := SplitHostPort(req.RemoteAddr); peer != "" { - // The Go HTTP server sets RemoteAddr to "IP:port", this will not be a - // file-path that would be interpreted with a sock family. - attrs = append(attrs, semconvNew.NetworkPeerAddress(peer)) - if peerPort > 0 { - attrs = append(attrs, semconvNew.NetworkPeerPort(peerPort)) - } - } - - if useragent != "" { - attrs = append(attrs, semconvNew.UserAgentOriginal(useragent)) - } - - if clientIP != "" { - attrs = append(attrs, semconvNew.ClientAddress(clientIP)) - } - - if req.URL != nil && req.URL.Path != "" { - attrs = append(attrs, semconvNew.URLPath(req.URL.Path)) - } - - if protoName != "" && protoName != "http" { - attrs = append(attrs, semconvNew.NetworkProtocolName(protoName)) - } - if protoVersion != "" { - attrs = append(attrs, semconvNew.NetworkProtocolVersion(protoVersion)) - } - - if route != "" { - attrs = append(attrs, n.Route(route)) - } - - return attrs -} - -func (n CurrentHTTPServer) NetworkTransportAttr(network string) attribute.KeyValue { - switch network { - case "tcp", "tcp4", "tcp6": - return semconvNew.NetworkTransportTCP - case "udp", "udp4", "udp6": - return semconvNew.NetworkTransportUDP - case "unix", "unixgram", "unixpacket": - return semconvNew.NetworkTransportUnix - default: - return semconvNew.NetworkTransportPipe - } -} - -func (n CurrentHTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) { - if method == "" { - return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{} - } - if attr, ok := methodLookup[method]; ok { - return attr, attribute.KeyValue{} - } - - orig := semconvNew.HTTPRequestMethodOriginal(method) - if attr, ok := methodLookup[strings.ToUpper(method)]; ok { - return attr, orig - } - return semconvNew.HTTPRequestMethodGet, orig -} - -func (n CurrentHTTPServer) scheme(https bool) attribute.KeyValue { //nolint:revive // ignore linter - if https { - return semconvNew.URLScheme("https") - } - return semconvNew.URLScheme("http") -} - -// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP -// response. -// -// If any of the fields in the ResponseTelemetry are not set the attribute will -// be omitted. -func (n CurrentHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue { - var count int - - if resp.ReadBytes > 0 { - count++ - } - if resp.WriteBytes > 0 { - count++ - } - if resp.StatusCode > 0 { - count++ - } - - attributes := make([]attribute.KeyValue, 0, count) - - if resp.ReadBytes > 0 { - attributes = append(attributes, - semconvNew.HTTPRequestBodySize(int(resp.ReadBytes)), - ) - } - if resp.WriteBytes > 0 { - attributes = append(attributes, - semconvNew.HTTPResponseBodySize(int(resp.WriteBytes)), - ) - } - if resp.StatusCode > 0 { - attributes = append(attributes, - semconvNew.HTTPResponseStatusCode(resp.StatusCode), - ) - } - - return attributes -} - -// Route returns the attribute for the route. -func (n CurrentHTTPServer) Route(route string) attribute.KeyValue { - return semconvNew.HTTPRoute(route) -} - -func (n CurrentHTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue { - num := len(additionalAttributes) + 3 - var host string - var p int - if server == "" { - host, p = SplitHostPort(req.Host) - } else { - // Prioritize the primary server name. - host, p = SplitHostPort(server) - if p < 0 { - _, p = SplitHostPort(req.Host) - } - } - hostPort := requiredHTTPPort(req.TLS != nil, p) - if hostPort > 0 { - num++ - } - protoName, protoVersion := netProtocol(req.Proto) - if protoName != "" { - num++ - } - if protoVersion != "" { - num++ - } - - if statusCode > 0 { - num++ - } - - attributes := slices.Grow(additionalAttributes, num) - attributes = append(attributes, - semconvNew.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)), - n.scheme(req.TLS != nil), - semconvNew.ServerAddress(host)) - - if hostPort > 0 { - attributes = append(attributes, semconvNew.ServerPort(hostPort)) - } - if protoName != "" { - attributes = append(attributes, semconvNew.NetworkProtocolName(protoName)) - } - if protoVersion != "" { - attributes = append(attributes, semconvNew.NetworkProtocolVersion(protoVersion)) - } - - if statusCode > 0 { - attributes = append(attributes, semconvNew.HTTPResponseStatusCode(statusCode)) - } - return attributes -} - -type CurrentHTTPClient struct{} - -// RequestTraceAttrs returns trace attributes for an HTTP request made by a client. -func (n CurrentHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue { - /* - below attributes are returned: - - http.request.method - - http.request.method.original - - url.full - - server.address - - server.port - - network.protocol.name - - network.protocol.version - */ - numOfAttributes := 3 // URL, server address, proto, and method. - - var urlHost string - if req.URL != nil { - urlHost = req.URL.Host - } - var requestHost string - var requestPort int - for _, hostport := range []string{urlHost, req.Header.Get("Host")} { - requestHost, requestPort = SplitHostPort(hostport) - if requestHost != "" || requestPort > 0 { - break - } - } - - eligiblePort := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort) - if eligiblePort > 0 { - numOfAttributes++ - } - useragent := req.UserAgent() - if useragent != "" { - numOfAttributes++ - } - - protoName, protoVersion := netProtocol(req.Proto) - if protoName != "" && protoName != "http" { - numOfAttributes++ - } - if protoVersion != "" { - numOfAttributes++ - } - - method, originalMethod := n.method(req.Method) - if originalMethod != (attribute.KeyValue{}) { - numOfAttributes++ - } - - attrs := make([]attribute.KeyValue, 0, numOfAttributes) - - attrs = append(attrs, method) - if originalMethod != (attribute.KeyValue{}) { - attrs = append(attrs, originalMethod) - } - - var u string - if req.URL != nil { - // Remove any username/password info that may be in the URL. - userinfo := req.URL.User - req.URL.User = nil - u = req.URL.String() - // Restore any username/password info that was removed. - req.URL.User = userinfo - } - attrs = append(attrs, semconvNew.URLFull(u)) - - attrs = append(attrs, semconvNew.ServerAddress(requestHost)) - if eligiblePort > 0 { - attrs = append(attrs, semconvNew.ServerPort(eligiblePort)) - } - - if protoName != "" && protoName != "http" { - attrs = append(attrs, semconvNew.NetworkProtocolName(protoName)) - } - if protoVersion != "" { - attrs = append(attrs, semconvNew.NetworkProtocolVersion(protoVersion)) - } - - return attrs -} - -// ResponseTraceAttrs returns trace attributes for an HTTP response made by a client. -func (n CurrentHTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue { - /* - below attributes are returned: - - http.response.status_code - - error.type - */ - var count int - if resp.StatusCode > 0 { - count++ - } - - if isErrorStatusCode(resp.StatusCode) { - count++ - } - - attrs := make([]attribute.KeyValue, 0, count) - if resp.StatusCode > 0 { - attrs = append(attrs, semconvNew.HTTPResponseStatusCode(resp.StatusCode)) - } - - if isErrorStatusCode(resp.StatusCode) { - errorType := strconv.Itoa(resp.StatusCode) - attrs = append(attrs, semconvNew.ErrorTypeKey.String(errorType)) - } - return attrs -} - -func (n CurrentHTTPClient) ErrorType(err error) attribute.KeyValue { - t := reflect.TypeOf(err) - var value string - if t.PkgPath() == "" && t.Name() == "" { - // Likely a builtin type. - value = t.String() - } else { - value = fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()) - } - - if value == "" { - return semconvNew.ErrorTypeOther - } - - return semconvNew.ErrorTypeKey.String(value) -} - -func (n CurrentHTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) { - if method == "" { - return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{} - } - if attr, ok := methodLookup[method]; ok { - return attr, attribute.KeyValue{} - } - - orig := semconvNew.HTTPRequestMethodOriginal(method) - if attr, ok := methodLookup[strings.ToUpper(method)]; ok { - return attr, orig - } - return semconvNew.HTTPRequestMethodGet, orig -} - -func (n CurrentHTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue { - num := len(additionalAttributes) + 2 - var h string - if req.URL != nil { - h = req.URL.Host - } - var requestHost string - var requestPort int - for _, hostport := range []string{h, req.Header.Get("Host")} { - requestHost, requestPort = SplitHostPort(hostport) - if requestHost != "" || requestPort > 0 { - break - } - } - - port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort) - if port > 0 { - num++ - } - - protoName, protoVersion := netProtocol(req.Proto) - if protoName != "" { - num++ - } - if protoVersion != "" { - num++ - } - - if statusCode > 0 { - num++ - } - - attributes := slices.Grow(additionalAttributes, num) - attributes = append(attributes, - semconvNew.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)), - semconvNew.ServerAddress(requestHost), - n.scheme(req), - ) - - if port > 0 { - attributes = append(attributes, semconvNew.ServerPort(port)) - } - if protoName != "" { - attributes = append(attributes, semconvNew.NetworkProtocolName(protoName)) - } - if protoVersion != "" { - attributes = append(attributes, semconvNew.NetworkProtocolVersion(protoVersion)) - } - - if statusCode > 0 { - attributes = append(attributes, semconvNew.HTTPResponseStatusCode(statusCode)) - } - return attributes -} - -// TraceAttributes returns attributes for httptrace. -func (n CurrentHTTPClient) TraceAttributes(host string) []attribute.KeyValue { - return []attribute.KeyValue{ - semconvNew.ServerAddress(host), - } -} - -func (n CurrentHTTPClient) scheme(req *http.Request) attribute.KeyValue { - if req.URL != nil && req.URL.Scheme != "" { - return semconvNew.URLScheme(req.URL.Scheme) - } - if req.TLS != nil { - return semconvNew.URLScheme("https") - } - return semconvNew.URLScheme("http") -} - -func isErrorStatusCode(code int) bool { - return code >= 400 || code < 100 -} diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/server.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/server.go new file mode 100644 index 0000000000..e0e9ebc05d --- /dev/null +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/server.go @@ -0,0 +1,403 @@ +// Code generated by gotmpl. DO NOT MODIFY. +// source: internal/shared/semconv/server.go.tmpl + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package semconv provides OpenTelemetry semantic convention types and +// functionality. +package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv" + +import ( + "context" + "fmt" + "net/http" + "slices" + "strings" + "sync" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/semconv/v1.39.0" + "go.opentelemetry.io/otel/semconv/v1.39.0/httpconv" +) + +type RequestTraceAttrsOpts struct { + // If set, this is used as value for the "http.client_ip" attribute. + HTTPClientIP string +} + +type ResponseTelemetry struct { + StatusCode int + ReadBytes int64 + ReadError error + WriteBytes int64 + WriteError error +} + +type HTTPServer struct{ + requestBodySizeHistogram httpconv.ServerRequestBodySize + responseBodySizeHistogram httpconv.ServerResponseBodySize + requestDurationHistogram httpconv.ServerRequestDuration +} + +func NewHTTPServer(meter metric.Meter) HTTPServer { + server := HTTPServer{} + + var err error + server.requestBodySizeHistogram, err = httpconv.NewServerRequestBodySize(meter) + handleErr(err) + + server.responseBodySizeHistogram, err = httpconv.NewServerResponseBodySize(meter) + handleErr(err) + + server.requestDurationHistogram, err = httpconv.NewServerRequestDuration( + meter, + metric.WithExplicitBucketBoundaries( + 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, + 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, + ), + ) + handleErr(err) + return server +} + +// Status returns a span status code and message for an HTTP status code +// value returned by a server. Status codes in the 400-499 range are not +// returned as errors. +func (n HTTPServer) Status(code int) (codes.Code, string) { + if code < 100 || code >= 600 { + return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) + } + if code >= 500 { + return codes.Error, "" + } + return codes.Unset, "" +} + +// RequestTraceAttrs returns trace attributes for an HTTP request received by a +// server. +// +// The server must be the primary server name if it is known. For example this +// would be the ServerName directive +// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache +// server, and the server_name directive +// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an +// nginx server. More generically, the primary server name would be the host +// header value that matches the default virtual host of an HTTP server. It +// should include the host identifier and if a port is used to route to the +// server that port identifier should be included as an appropriate port +// suffix. +// +// If the primary server name is not known, server should be an empty string. +// The req Host will be used to determine the server instead. +func (n HTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue { + count := 3 // ServerAddress, Method, Scheme + + var host string + var p int + if server == "" { + host, p = SplitHostPort(req.Host) + } else { + // Prioritize the primary server name. + host, p = SplitHostPort(server) + if p < 0 { + _, p = SplitHostPort(req.Host) + } + } + + hostPort := requiredHTTPPort(req.TLS != nil, p) + if hostPort > 0 { + count++ + } + + method, methodOriginal := n.method(req.Method) + if methodOriginal != (attribute.KeyValue{}) { + count++ + } + + scheme := n.scheme(req.TLS != nil) + + peer, peerPort := SplitHostPort(req.RemoteAddr) + if peer != "" { + // The Go HTTP server sets RemoteAddr to "IP:port", this will not be a + // file-path that would be interpreted with a sock family. + count++ + if peerPort > 0 { + count++ + } + } + + useragent := req.UserAgent() + if useragent != "" { + count++ + } + + // For client IP, use, in order: + // 1. The value passed in the options + // 2. The value in the X-Forwarded-For header + // 3. The peer address + clientIP := opts.HTTPClientIP + if clientIP == "" { + clientIP = serverClientIP(req.Header.Get("X-Forwarded-For")) + if clientIP == "" { + clientIP = peer + } + } + if clientIP != "" { + count++ + } + + if req.URL != nil && req.URL.Path != "" { + count++ + } + + protoName, protoVersion := netProtocol(req.Proto) + if protoName != "" && protoName != "http" { + count++ + } + if protoVersion != "" { + count++ + } + + route := httpRoute(req.Pattern) + if route != "" { + count++ + } + + attrs := make([]attribute.KeyValue, 0, count) + attrs = append(attrs, + semconv.ServerAddress(host), + method, + scheme, + ) + + if hostPort > 0 { + attrs = append(attrs, semconv.ServerPort(hostPort)) + } + if methodOriginal != (attribute.KeyValue{}) { + attrs = append(attrs, methodOriginal) + } + + if peer, peerPort := SplitHostPort(req.RemoteAddr); peer != "" { + // The Go HTTP server sets RemoteAddr to "IP:port", this will not be a + // file-path that would be interpreted with a sock family. + attrs = append(attrs, semconv.NetworkPeerAddress(peer)) + if peerPort > 0 { + attrs = append(attrs, semconv.NetworkPeerPort(peerPort)) + } + } + + if useragent != "" { + attrs = append(attrs, semconv.UserAgentOriginal(useragent)) + } + + if clientIP != "" { + attrs = append(attrs, semconv.ClientAddress(clientIP)) + } + + if req.URL != nil && req.URL.Path != "" { + attrs = append(attrs, semconv.URLPath(req.URL.Path)) + } + + if protoName != "" && protoName != "http" { + attrs = append(attrs, semconv.NetworkProtocolName(protoName)) + } + if protoVersion != "" { + attrs = append(attrs, semconv.NetworkProtocolVersion(protoVersion)) + } + + if route != "" { + attrs = append(attrs, n.Route(route)) + } + + return attrs +} + +func (s HTTPServer) NetworkTransportAttr(network string) []attribute.KeyValue { + attr := semconv.NetworkTransportPipe + switch network { + case "tcp", "tcp4", "tcp6": + attr = semconv.NetworkTransportTCP + case "udp", "udp4", "udp6": + attr = semconv.NetworkTransportUDP + case "unix", "unixgram", "unixpacket": + attr = semconv.NetworkTransportUnix + } + + return []attribute.KeyValue{attr} +} + +type ServerMetricData struct { + ServerName string + ResponseSize int64 + + MetricData + MetricAttributes +} + +type MetricAttributes struct { + Req *http.Request + StatusCode int + Route string + AdditionalAttributes []attribute.KeyValue +} + +type MetricData struct { + RequestSize int64 + + // The request duration, in milliseconds + ElapsedTime float64 +} + +var ( + metricAddOptionPool = &sync.Pool{ + New: func() any { + return &[]metric.AddOption{} + }, + } + + metricRecordOptionPool = &sync.Pool{ + New: func() any { + return &[]metric.RecordOption{} + }, + } +) + +func (n HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) { + attributes := n.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.Route, md.AdditionalAttributes) + o := metric.WithAttributeSet(attribute.NewSet(attributes...)) + recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption) + *recordOpts = append(*recordOpts, o) + n.requestBodySizeHistogram.Inst().Record(ctx, md.RequestSize, *recordOpts...) + n.responseBodySizeHistogram.Inst().Record(ctx, md.ResponseSize, *recordOpts...) + n.requestDurationHistogram.Inst().Record(ctx, md.ElapsedTime/1000.0, o) + *recordOpts = (*recordOpts)[:0] + metricRecordOptionPool.Put(recordOpts) +} + +func (n HTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) { + if method == "" { + return semconv.HTTPRequestMethodGet, attribute.KeyValue{} + } + if attr, ok := methodLookup[method]; ok { + return attr, attribute.KeyValue{} + } + + orig := semconv.HTTPRequestMethodOriginal(method) + if attr, ok := methodLookup[strings.ToUpper(method)]; ok { + return attr, orig + } + return semconv.HTTPRequestMethodGet, orig +} + +func (n HTTPServer) scheme(https bool) attribute.KeyValue { //nolint:revive // ignore linter + if https { + return semconv.URLScheme("https") + } + return semconv.URLScheme("http") +} + +// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP +// response. +// +// If any of the fields in the ResponseTelemetry are not set the attribute will +// be omitted. +func (n HTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue { + var count int + + if resp.ReadBytes > 0 { + count++ + } + if resp.WriteBytes > 0 { + count++ + } + if resp.StatusCode > 0 { + count++ + } + + attributes := make([]attribute.KeyValue, 0, count) + + if resp.ReadBytes > 0 { + attributes = append(attributes, + semconv.HTTPRequestBodySize(int(resp.ReadBytes)), + ) + } + if resp.WriteBytes > 0 { + attributes = append(attributes, + semconv.HTTPResponseBodySize(int(resp.WriteBytes)), + ) + } + if resp.StatusCode > 0 { + attributes = append(attributes, + semconv.HTTPResponseStatusCode(resp.StatusCode), + ) + } + + return attributes +} + +// Route returns the attribute for the route. +func (n HTTPServer) Route(route string) attribute.KeyValue { + return semconv.HTTPRoute(route) +} + +func (n HTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, route string, additionalAttributes []attribute.KeyValue) []attribute.KeyValue { + num := len(additionalAttributes) + 3 + var host string + var p int + if server == "" { + host, p = SplitHostPort(req.Host) + } else { + // Prioritize the primary server name. + host, p = SplitHostPort(server) + if p < 0 { + _, p = SplitHostPort(req.Host) + } + } + hostPort := requiredHTTPPort(req.TLS != nil, p) + if hostPort > 0 { + num++ + } + protoName, protoVersion := netProtocol(req.Proto) + if protoName != "" { + num++ + } + if protoVersion != "" { + num++ + } + + if statusCode > 0 { + num++ + } + + if route != "" { + num++ + } + + attributes := slices.Grow(additionalAttributes, num) + attributes = append(attributes, + semconv.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)), + n.scheme(req.TLS != nil), + semconv.ServerAddress(host)) + + if hostPort > 0 { + attributes = append(attributes, semconv.ServerPort(hostPort)) + } + if protoName != "" { + attributes = append(attributes, semconv.NetworkProtocolName(protoName)) + } + if protoVersion != "" { + attributes = append(attributes, semconv.NetworkProtocolVersion(protoVersion)) + } + + if statusCode > 0 { + attributes = append(attributes, semconv.HTTPResponseStatusCode(statusCode)) + } + + if route != "" { + attributes = append(attributes, semconv.HTTPRoute(route)) + } + return attributes +} diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/util.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/util.go index 96422ad1ed..131fda489b 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/util.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/util.go @@ -14,7 +14,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - semconvNew "go.opentelemetry.io/otel/semconv/v1.37.0" + semconvNew "go.opentelemetry.io/otel/semconv/v1.39.0" ) // SplitHostPort splits a network address hostport of the form "host", diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go index 514ae6753b..59b6c5498d 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go @@ -5,6 +5,7 @@ package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http import ( "context" + "fmt" "io" "net/http" "net/http/httptrace" @@ -15,6 +16,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/propagation" + otelsemconv "go.opentelemetry.io/otel/semconv/v1.39.0" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request" @@ -83,6 +85,8 @@ func defaultTransportFormatter(_ string, r *http.Request) string { // RoundTrip creates a Span and propagates its context via the provided request's headers // before handing the request to the configured base RoundTripper. The created span will // end when the response body is closed or when a read from the body returns io.EOF. +// If GetBody returns an error, the error is reported via otel.Handle and the request +// continues with the original Body. func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { requestStartTime := time.Now() for _, f := range t.filters { @@ -117,11 +121,22 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { r = r.Clone(ctx) // According to RoundTripper spec, we shouldn't modify the origin request. - // if request body is nil or NoBody, we don't want to mutate the body as it + // GetBody is preferred over direct access to Body if the function is set. + // If the resulting body is nil or is NoBody, we don't want to mutate the body as it // will affect the identity of it in an unforeseeable way because we assert // ReadCloser fulfills a certain interface and it is indeed nil or NoBody. - bw := request.NewBodyWrapper(r.Body, func(int64) {}) - if r.Body != nil && r.Body != http.NoBody { + body := r.Body + if r.GetBody != nil { + b, err := r.GetBody() + if err != nil { + otel.Handle(fmt.Errorf("http.Request GetBody returned an error: %w", err)) + } else { + body = b + } + } + + bw := request.NewBodyWrapper(body, func(int64) {}) + if body != nil && body != http.NoBody { r.Body = bw } @@ -161,15 +176,7 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { }() if err != nil { - // set error type attribute if the error is part of the predefined - // error types. - // otherwise, record it as an exception - if errType := t.semconv.ErrorType(err); errType.Valid() { - span.SetAttributes(errType) - } else { - span.RecordError(err) - } - + span.SetAttributes(otelsemconv.ErrorType(err)) span.SetStatus(codes.Error, err.Error()) span.End() @@ -229,7 +236,7 @@ func (wb *wrappedBody) Write(p []byte) (int, error) { // This will not panic given the guard in newWrappedBody. n, err := wb.body.(io.Writer).Write(p) if err != nil { - wb.span.RecordError(err) + wb.span.SetAttributes(otelsemconv.ErrorType(err)) wb.span.SetStatus(codes.Error, err.Error()) } return n, err @@ -247,7 +254,7 @@ func (wb *wrappedBody) Read(b []byte) (int, error) { wb.recordBytesRead() wb.span.End() default: - wb.span.RecordError(err) + wb.span.SetAttributes(otelsemconv.ErrorType(err)) wb.span.SetStatus(codes.Error, err.Error()) } return n, err diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go index dfb53cf1f3..d0107952e1 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go @@ -4,7 +4,4 @@ package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" // Version is the current release version of the otelhttp instrumentation. -func Version() string { - return "0.63.0" - // This string is updated by the pre_release.sh script during release -} +const Version = "0.65.0" diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go index 379bc8170d..d431fc4517 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/span.go @@ -113,7 +113,7 @@ func span(sd tracesdk.ReadOnlySpan) *tracepb.Span { if psid := sd.Parent().SpanID(); psid.IsValid() { s.ParentSpanId = psid[:] } - s.Flags = buildSpanFlags(sd.Parent()) + s.Flags = buildSpanFlagsWith(sd.SpanContext().TraceFlags(), sd.Parent()) return s } @@ -159,7 +159,7 @@ func links(links []tracesdk.Link) []*tracepb.Span_Link { tid := otLink.SpanContext.TraceID() sid := otLink.SpanContext.SpanID() - flags := buildSpanFlags(otLink.SpanContext) + flags := buildSpanFlagsWith(otLink.SpanContext.TraceFlags(), otLink.SpanContext) sl = append(sl, &tracepb.Span_Link{ TraceId: tid[:], @@ -172,13 +172,15 @@ func links(links []tracesdk.Link) []*tracepb.Span_Link { return sl } -func buildSpanFlags(sc trace.SpanContext) uint32 { - flags := tracepb.SpanFlags_SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK - if sc.IsRemote() { - flags |= tracepb.SpanFlags_SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK +func buildSpanFlagsWith(tf trace.TraceFlags, parent trace.SpanContext) uint32 { + // Lower 8 bits are the W3C TraceFlags; always indicate that we know whether the parent is remote + flags := uint32(tf) | uint32(tracepb.SpanFlags_SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) + // Set the parent-is-remote bit when applicable + if parent.IsRemote() { + flags |= uint32(tracepb.SpanFlags_SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) } - return uint32(flags) // nolint:gosec // Flags is a bitmask and can't be negative + return flags // nolint:gosec // Flags is a bitmask and can't be negative } // spanEvents transforms span Events to an OTLP span events. diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/client.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/client.go index 4b4cc76f4a..76b7cd461b 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/client.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/client.go @@ -17,9 +17,10 @@ import ( "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" - "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry" ) @@ -45,6 +46,9 @@ type client struct { conn *grpc.ClientConn tscMu sync.RWMutex tsc coltracepb.TraceServiceClient + + instID int64 + inst *observ.Instrumentation } // Compile time check *client implements otlptrace.Client. @@ -68,6 +72,7 @@ func newClient(opts ...Option) *client { stopCtx: ctx, stopFunc: cancel, conn: cfg.GRPCConn, + instID: counter.NextExporterID(), } if len(cfg.Traces.Headers) > 0 { @@ -92,13 +97,24 @@ func (c *client) Start(context.Context) error { c.conn = conn } + // Initialize the instrumentation if not already done. + // + // Initialize here instead of NewClient to allow any errors to be passed + // back to the caller and so that any setup of the environment variables to + // enable instrumentation can be set via code. + var err error + if c.inst == nil { + target := c.conn.CanonicalTarget() + c.inst, err = observ.NewInstrumentation(c.instID, target) + } + // The otlptrace.Client interface states this method is called just once, // so no need to check if already started. c.tscMu.Lock() c.tsc = coltracepb.NewTraceServiceClient(c.conn) c.tscMu.Unlock() - return nil + return err } var errAlreadyStopped = errors.New("the client is already stopped") @@ -174,7 +190,7 @@ var errShutdown = errors.New("the client is shutdown") // // Retryable errors from the server will be handled according to any // RetryConfig the client was created with. -func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) error { +func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) (uploadErr error) { // Hold a read lock to ensure a shut down initiated after this starts does // not abandon the export. This read lock acquire has less priority than a // write lock acquire (i.e. Stop), meaning if the client is shutting down @@ -189,6 +205,12 @@ func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc ctx, cancel := c.exportContext(ctx) defer cancel() + var code codes.Code + if c.inst != nil { + op := c.inst.ExportSpans(ctx, len(protoSpans)) + defer func() { op.End(uploadErr, code) }() + } + return c.requestFunc(ctx, func(iCtx context.Context) error { resp, err := c.tsc.Export(iCtx, &coltracepb.ExportTraceServiceRequest{ ResourceSpans: protoSpans, @@ -197,16 +219,17 @@ func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc msg := resp.PartialSuccess.GetErrorMessage() n := resp.PartialSuccess.GetRejectedSpans() if n != 0 || msg != "" { - err := internal.TracePartialSuccessError(n, msg) - otel.Handle(err) + e := internal.TracePartialSuccessError(n, msg) + uploadErr = errors.Join(uploadErr, e) } } // nil is converted to OK. - if status.Code(err) == codes.OK { + code = status.Code(err) + if code == codes.OK { // Success. - return nil + return uploadErr } - return err + return errors.Join(uploadErr, err) }) } diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter/counter.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter/counter.go new file mode 100644 index 0000000000..323b2a2c9a --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter/counter.go @@ -0,0 +1,31 @@ +// Code generated by gotmpl. DO NOT MODIFY. +// source: internal/shared/counter/counter.go.tmpl + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package counter provides a simple counter for generating unique IDs. +// +// This package is used to generate unique IDs while allowing testing packages +// to reset the counter. +package counter // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter" + +import "sync/atomic" + +// exporterN is a global 0-based count of the number of exporters created. +var exporterN atomic.Int64 + +// NextExporterID returns the next unique ID for an exporter. +func NextExporterID() int64 { + const inc = 1 + return exporterN.Add(inc) - inc +} + +// SetExporterID sets the exporter ID counter to v and returns the previous +// value. +// +// This function is useful for testing purposes, allowing you to reset the +// counter. It should not be used in production code. +func SetExporterID(v int64) int64 { + return exporterN.Swap(v) +} diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/gen.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/gen.go index b6e6b10fbf..7fe9c9f3a3 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/gen.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/gen.go @@ -23,3 +23,12 @@ package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/ot //go:generate gotmpl --body=../../../../../internal/shared/otlp/otlptrace/otlptracetest/collector.go.tmpl "--data={}" --out=otlptracetest/collector.go //go:generate gotmpl --body=../../../../../internal/shared/otlp/otlptrace/otlptracetest/data.go.tmpl "--data={}" --out=otlptracetest/data.go //go:generate gotmpl --body=../../../../../internal/shared/otlp/otlptrace/otlptracetest/otlptest.go.tmpl "--data={}" --out=otlptracetest/otlptest.go + +//go:generate gotmpl --body=../../../../../internal/shared/x/x.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\" }" --out=x/x.go +//go:generate gotmpl --body=../../../../../internal/shared/x/x_test.go.tmpl "--data={}" --out=x/x_test.go + +//go:generate gotmpl --body=../../../../../internal/shared/otlp/observ/target.go.tmpl "--data={ \"pkg\": \"observ\", \"pkg_path\": \"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ\" }" --out=observ/target.go +//go:generate gotmpl --body=../../../../../internal/shared/otlp/observ/target_test.go.tmpl "--data={ \"pkg\": \"observ\" }" --out=observ/target_test.go + +//go:generate gotmpl --body=../../../../../internal/shared/counter/counter.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter\" }" --out=counter/counter.go +//go:generate gotmpl --body=../../../../../internal/shared/counter/counter_test.go.tmpl "--data={}" --out=counter/counter_test.go diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/doc.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/doc.go new file mode 100644 index 0000000000..0dd54e4b96 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/doc.go @@ -0,0 +1,6 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package observ provides experimental observability instrumentation for the +// otlptracegrpc exporter. +package observ // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ" diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/instrumentation.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/instrumentation.go new file mode 100644 index 0000000000..d4a69f4d7c --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/instrumentation.go @@ -0,0 +1,342 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package observ // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ" + +import ( + "context" + "errors" + "fmt" + "sync" + "time" + + "google.golang.org/grpc/codes" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x" + "go.opentelemetry.io/otel/internal/global" + "go.opentelemetry.io/otel/metric" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" + "go.opentelemetry.io/otel/semconv/v1.39.0/otelconv" +) + +const ( + // ScopeName is the unique name of the meter used for instrumentation. + ScopeName = "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ" + + // SchemaURL is the schema URL of the metrics produced by this + // instrumentation. + SchemaURL = semconv.SchemaURL + + // Version is the current version of this instrumentation. + // + // This matches the version of the exporter. + Version = internal.Version +) + +var ( + measureAttrsPool = &sync.Pool{ + New: func() any { + const n = 1 + // component.name + 1 + // component.type + 1 + // server.addr + 1 + // server.port + 1 + // error.type + 1 // rpc.grpc.status_code + s := make([]attribute.KeyValue, 0, n) + // Return a pointer to a slice instead of a slice itself + // to avoid allocations on every call. + return &s + }, + } + + addOptPool = &sync.Pool{ + New: func() any { + const n = 1 // WithAttributeSet + o := make([]metric.AddOption, 0, n) + return &o + }, + } + + recordOptPool = &sync.Pool{ + New: func() any { + const n = 1 // WithAttributeSet + o := make([]metric.RecordOption, 0, n) + return &o + }, + } +) + +func get[T any](p *sync.Pool) *[]T { return p.Get().(*[]T) } + +func put[T any](p *sync.Pool, s *[]T) { + *s = (*s)[:0] // Reset. + p.Put(s) +} + +// ComponentName returns the component name for the exporter with the +// provided ID. +func ComponentName(id int64) string { + t := semconv.OTelComponentTypeOtlpGRPCSpanExporter.Value.AsString() + return fmt.Sprintf("%s/%d", t, id) +} + +// Instrumentation is experimental instrumentation for the exporter. +type Instrumentation struct { + inflightSpans metric.Int64UpDownCounter + exportedSpans metric.Int64Counter + opDuration metric.Float64Histogram + + attrs []attribute.KeyValue + addOpt metric.AddOption + recOpt metric.RecordOption +} + +// NewInstrumentation returns instrumentation for an OTLP over gPRC trace +// exporter with the provided ID using the global MeterProvider. +// +// The id should be the unique exporter instance ID. It is used +// to set the "component.name" attribute. +// +// The target is the endpoint the exporter is exporting to. +// +// If the experimental observability is disabled, nil is returned. +func NewInstrumentation(id int64, target string) (*Instrumentation, error) { + if !x.Observability.Enabled() { + return nil, nil + } + + attrs := BaseAttrs(id, target) + i := &Instrumentation{ + attrs: attrs, + addOpt: metric.WithAttributeSet(attribute.NewSet(attrs...)), + + // Do not modify attrs (NewSet sorts in-place), make a new slice. + recOpt: metric.WithAttributeSet(attribute.NewSet(append( + // Default to OK status code. + []attribute.KeyValue{ + semconv.RPCResponseStatusCode(codes.OK.String()), + }, + attrs..., + )...)), + } + + mp := otel.GetMeterProvider() + m := mp.Meter( + ScopeName, + metric.WithInstrumentationVersion(Version), + metric.WithSchemaURL(SchemaURL), + ) + + var err error + + inflightSpans, e := otelconv.NewSDKExporterSpanInflight(m) + if e != nil { + e = fmt.Errorf("failed to create span inflight metric: %w", e) + err = errors.Join(err, e) + } + i.inflightSpans = inflightSpans.Inst() + + exportedSpans, e := otelconv.NewSDKExporterSpanExported(m) + if e != nil { + e = fmt.Errorf("failed to create span exported metric: %w", e) + err = errors.Join(err, e) + } + i.exportedSpans = exportedSpans.Inst() + + opDuration, e := otelconv.NewSDKExporterOperationDuration(m) + if e != nil { + e = fmt.Errorf("failed to create operation duration metric: %w", e) + err = errors.Join(err, e) + } + i.opDuration = opDuration.Inst() + + return i, err +} + +// BaseAttrs returns the base attributes for the exporter with the provided ID +// and target. +// +// The id should be the unique exporter instance ID. It is used +// to set the "component.name" attribute. +// +// The target is the gRPC target the exporter is exporting to. It is expected +// to be the output of the Client's CanonicalTarget method. +func BaseAttrs(id int64, target string) []attribute.KeyValue { + host, port, err := ParseCanonicalTarget(target) + if err != nil || (host == "" && port < 0) { + if err != nil { + global.Debug("failed to parse target", "target", target, "error", err) + } + return []attribute.KeyValue{ + semconv.OTelComponentName(ComponentName(id)), + semconv.OTelComponentTypeOtlpGRPCSpanExporter, + } + } + + // Do not use append so the slice is exactly allocated. + + if port < 0 { + return []attribute.KeyValue{ + semconv.OTelComponentName(ComponentName(id)), + semconv.OTelComponentTypeOtlpGRPCSpanExporter, + semconv.ServerAddress(host), + } + } + + if host == "" { + return []attribute.KeyValue{ + semconv.OTelComponentName(ComponentName(id)), + semconv.OTelComponentTypeOtlpGRPCSpanExporter, + semconv.ServerPort(port), + } + } + + return []attribute.KeyValue{ + semconv.OTelComponentName(ComponentName(id)), + semconv.OTelComponentTypeOtlpGRPCSpanExporter, + semconv.ServerAddress(host), + semconv.ServerPort(port), + } +} + +// ExportSpans instruments the ExportSpans method of the exporter. It returns +// an [ExportOp] that must have its [ExportOp.End] method called when the +// ExportSpans method returns. +func (i *Instrumentation) ExportSpans(ctx context.Context, nSpans int) ExportOp { + start := time.Now() + + addOpt := get[metric.AddOption](addOptPool) + defer put(addOptPool, addOpt) + *addOpt = append(*addOpt, i.addOpt) + i.inflightSpans.Add(ctx, int64(nSpans), *addOpt...) + + return ExportOp{ + ctx: ctx, + start: start, + nSpans: int64(nSpans), + inst: i, + } +} + +// ExportOp tracks the operation being observed by [Instrumentation.ExportSpans]. +type ExportOp struct { + ctx context.Context + start time.Time + nSpans int64 + + inst *Instrumentation +} + +// End completes the observation of the operation being observed by a call to +// [Instrumentation.ExportSpans]. +// +// Any error that is encountered is provided as err. +// +// If err is not nil, all spans will be recorded as failures unless error is of +// type [internal.PartialSuccess]. In the case of a PartialSuccess, the number +// of successfully exported spans will be determined by inspecting the +// RejectedItems field of the PartialSuccess. +func (e ExportOp) End(err error, code codes.Code) { + addOpt := get[metric.AddOption](addOptPool) + defer put(addOptPool, addOpt) + *addOpt = append(*addOpt, e.inst.addOpt) + + e.inst.inflightSpans.Add(e.ctx, -e.nSpans, *addOpt...) + + success := successful(e.nSpans, err) + // Record successfully exported spans, even if the value is 0 which are + // meaningful to distribution aggregations. + e.inst.exportedSpans.Add(e.ctx, success, *addOpt...) + + if err != nil { + attrs := get[attribute.KeyValue](measureAttrsPool) + defer put(measureAttrsPool, attrs) + *attrs = append(*attrs, e.inst.attrs...) + *attrs = append(*attrs, semconv.ErrorType(err)) + + // Do not inefficiently make a copy of attrs by using + // WithAttributes instead of WithAttributeSet. + o := metric.WithAttributeSet(attribute.NewSet(*attrs...)) + // Reset addOpt with new attribute set. + *addOpt = append((*addOpt)[:0], o) + + e.inst.exportedSpans.Add(e.ctx, e.nSpans-success, *addOpt...) + } + + recOpt := get[metric.RecordOption](recordOptPool) + defer put(recordOptPool, recOpt) + *recOpt = append(*recOpt, e.inst.recordOption(err, code)) + + d := time.Since(e.start).Seconds() + e.inst.opDuration.Record(e.ctx, d, *recOpt...) +} + +// recordOption returns a RecordOption with attributes representing the +// outcome of the operation being recorded. +// +// If err is nil and code is codes.OK, the default recOpt of the +// Instrumentation is returned. +// +// If err is not nil or code is not codes.OK, a new RecordOption is returned +// with the base attributes of the Instrumentation plus the rpc.grpc.status_code +// attribute set to the provided code, and if err is not nil, the error.type +// attribute set to the type of the error. +func (i *Instrumentation) recordOption(err error, code codes.Code) metric.RecordOption { + if err == nil && code == codes.OK { + return i.recOpt + } + + attrs := get[attribute.KeyValue](measureAttrsPool) + defer put(measureAttrsPool, attrs) + *attrs = append(*attrs, i.attrs...) + + *attrs = append(*attrs, semconv.RPCResponseStatusCode(code.String())) + if err != nil { + *attrs = append(*attrs, semconv.ErrorType(err)) + } + + // Do not inefficiently make a copy of attrs by using WithAttributes + // instead of WithAttributeSet. + return metric.WithAttributeSet(attribute.NewSet(*attrs...)) +} + +// successful returns the number of successfully exported spans out of the n +// that were exported based on the provided error. +// +// If err is nil, n is returned. All spans were successfully exported. +// +// If err is not nil and not an [internal.PartialSuccess] error, 0 is returned. +// It is assumed all spans failed to be exported. +// +// If err is an [internal.PartialSuccess] error, the number of successfully +// exported spans is computed by subtracting the RejectedItems field from n. If +// RejectedItems is negative, n is returned. If RejectedItems is greater than +// n, 0 is returned. +func successful(n int64, err error) int64 { + if err == nil { + return n // All spans successfully exported. + } + // Split rejection calculation so successful is inlinable. + return n - rejected(n, err) +} + +var errPartialPool = &sync.Pool{ + New: func() any { return new(internal.PartialSuccess) }, +} + +// rejected returns how many out of the n spans exporter were rejected based on +// the provided non-nil err. +func rejected(n int64, err error) int64 { + ps := errPartialPool.Get().(*internal.PartialSuccess) + defer errPartialPool.Put(ps) + // Check for partial success. + if errors.As(err, ps) { + // Bound RejectedItems to [0, n]. This should not be needed, + // but be defensive as this is from an external source. + return min(max(ps.RejectedItems, 0), n) + } + return n // All spans rejected. +} diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/target.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/target.go new file mode 100644 index 0000000000..34eee27db0 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/target.go @@ -0,0 +1,143 @@ +// Code generated by gotmpl. DO NOT MODIFY. +// source: internal/shared/otlp/observ/target.go.tmpl + +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package observ // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ" + +import ( + "errors" + "fmt" + "net" + "net/netip" + "strconv" + "strings" +) + +const ( + schemeUnix = "unix" + schemeUnixAbstract = "unix-abstract" +) + +// ParseCanonicalTarget parses a target string and returns the extracted host +// (domain address or IP), the target port, or an error. +// +// If no port is specified, -1 is returned. +// +// If no host is specified, an empty string is returned. +// +// The target string is expected to always have the form +// "://[authority]/". For example: +// - "dns:///example.com:42" +// - "dns://8.8.8.8/example.com:42" +// - "unix:///path/to/socket" +// - "unix-abstract:///socket-name" +// - "passthrough:///192.34.2.1:42" +// +// The target is expected to come from the CanonicalTarget method of a gRPC +// Client. +func ParseCanonicalTarget(target string) (string, int, error) { + const sep = "://" + + // Find scheme. Do not allocate the string by using url.Parse. + idx := strings.Index(target, sep) + if idx == -1 { + return "", -1, fmt.Errorf("invalid target %q: missing scheme", target) + } + scheme, endpoint := target[:idx], target[idx+len(sep):] + + // Check for unix schemes. + if scheme == schemeUnix || scheme == schemeUnixAbstract { + return parseUnix(endpoint) + } + + // Strip leading slash and any authority. + if i := strings.Index(endpoint, "/"); i != -1 { + endpoint = endpoint[i+1:] + } + + // DNS, passthrough, and custom resolvers. + return parseEndpoint(endpoint) +} + +// parseUnix parses unix socket targets. +func parseUnix(endpoint string) (string, int, error) { + // Format: unix[-abstract]://path + // + // We should have "/path" (empty authority) if valid. + if len(endpoint) >= 1 && endpoint[0] == '/' { + // Return the full path including leading slash. + return endpoint, -1, nil + } + + // If there's no leading slash, it means there might be an authority + // Check for authority case (should error): "authority/path" + if slashIdx := strings.Index(endpoint, "/"); slashIdx > 0 { + return "", -1, fmt.Errorf("invalid (non-empty) authority: %s", endpoint[:slashIdx]) + } + + return "", -1, errors.New("invalid unix target format") +} + +// parseEndpoint parses an endpoint from a gRPC target. +// +// It supports the following formats: +// - "host" +// - "host%zone" +// - "host:port" +// - "host%zone:port" +// - "ipv4" +// - "ipv4%zone" +// - "ipv4:port" +// - "ipv4%zone:port" +// - "ipv6" +// - "ipv6%zone" +// - "[ipv6]" +// - "[ipv6%zone]" +// - "[ipv6]:port" +// - "[ipv6%zone]:port" +// +// It returns the host or host%zone (domain address or IP), the port (or -1 if +// not specified), or an error if the input is not a valid. +func parseEndpoint(endpoint string) (string, int, error) { + // First check if the endpoint is just an IP address. + if ip := parseIP(endpoint); ip != "" { + return ip, -1, nil + } + + // If there's no colon, there is no port (IPv6 with no port checked above). + if !strings.Contains(endpoint, ":") { + return endpoint, -1, nil + } + + host, portStr, err := net.SplitHostPort(endpoint) + if err != nil { + return "", -1, fmt.Errorf("invalid host:port %q: %w", endpoint, err) + } + + const base, bitSize = 10, 16 + port16, err := strconv.ParseUint(portStr, base, bitSize) + if err != nil { + return "", -1, fmt.Errorf("invalid port %q: %w", portStr, err) + } + port := int(port16) // port is guaranteed to be in the range [0, 65535]. + + return host, port, nil +} + +// parseIP attempts to parse the entire endpoint as an IP address. +// It returns the normalized string form of the IP if successful, +// or an empty string if parsing fails. +func parseIP(ip string) string { + // Strip leading and trailing brackets for IPv6 addresses. + if len(ip) >= 2 && ip[0] == '[' && ip[len(ip)-1] == ']' { + ip = ip[1 : len(ip)-1] + } + addr, err := netip.ParseAddr(ip) + if err != nil { + return "" + } + // Return the normalized string form of the IP. + return addr.String() +} diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/partialsuccess.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/partialsuccess.go index 1c46594233..a811a07b35 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/partialsuccess.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/partialsuccess.go @@ -29,6 +29,17 @@ func (ps PartialSuccess) Error() string { return fmt.Sprintf("OTLP partial success: %s (%d %s rejected)", msg, ps.RejectedItems, ps.RejectedKind) } +// As returns true if ps can be assigned to target and makes the assignment. +// Otherwise, it returns false. This supports the errors.As() interface. +func (ps PartialSuccess) As(target any) bool { + t, ok := target.(*PartialSuccess) + if !ok { + return false + } + *t = ps + return true +} + // Is supports the errors.Is() interface. func (ps PartialSuccess) Is(err error) bool { _, ok := err.(PartialSuccess) diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry/retry.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry/retry.go index 259a898ae7..a7b8e81a78 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry/retry.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry/retry.go @@ -94,6 +94,11 @@ func (c Config) RequestFunc(evaluate EvaluateFunc) RequestFunc { return err } + // Check if context is canceled before attempting to wait and retry. + if ctx.Err() != nil { + return fmt.Errorf("%w: %w", ctx.Err(), err) + } + if maxElapsedTime != 0 && time.Since(startTime) > maxElapsedTime { return fmt.Errorf("max retry time elapsed: %w", err) } diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/version.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/version.go new file mode 100644 index 0000000000..19fac1b725 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/version.go @@ -0,0 +1,8 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal" + +// Version is the current release version of the OpenTelemetry OTLP gRPC trace +// exporter in use. +const Version = "1.40.0" diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/x/README.md b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/README.md similarity index 54% rename from vendor/go.opentelemetry.io/otel/sdk/trace/internal/x/README.md rename to vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/README.md index feec16fa64..15a3011bff 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/x/README.md +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/README.md @@ -1,33 +1,34 @@ # Experimental Features -The Trace SDK contains features that have not yet stabilized in the OpenTelemetry specification. -These features are added to the OpenTelemetry Go Trace SDK prior to stabilization in the specification so that users can start experimenting with them and provide feedback. +The `otlptracegrpc` exporter contains features that have not yet stabilized in the OpenTelemetry specification. +These features are added to the `otlptracegrpc` exporter prior to stabilization in the specification so that users can start experimenting with them and provide feedback. -These features may change in backwards incompatible ways as feedback is applied. +These feature may change in backwards incompatible ways as feedback is applied. See the [Compatibility and Stability](#compatibility-and-stability) section for more information. ## Features -- [Self-Observability](#self-observability) +- [Observability](#observability) -### Self-Observability +### Observability -The SDK provides a self-observability feature that allows you to monitor the SDK itself. +The `otlptracegrpc` exporter provides a observability feature that allows you to monitor the SDK itself. -To opt-in, set the environment variable `OTEL_GO_X_SELF_OBSERVABILITY` to `true`. +To opt-in, set the environment variable `OTEL_GO_X_OBSERVABILITY` to `true`. When enabled, the SDK will create the following metrics using the global `MeterProvider`: -- `otel.sdk.span.live` -- `otel.sdk.span.started` +- `otel.sdk.exporter.span.inflight` +- `otel.sdk.exporter.span.exported` +- `otel.sdk.exporter.operation.duration` Please see the [Semantic conventions for OpenTelemetry SDK metrics] documentation for more details on these metrics. -[Semantic conventions for OpenTelemetry SDK metrics]: https://github.com/open-telemetry/semantic-conventions/blob/v1.36.0/docs/otel/sdk-metrics.md +[Semantic conventions for OpenTelemetry SDK metrics]: https://github.com/open-telemetry/semantic-conventions/blob/v1.37.0/docs/otel/sdk-metrics.md ## Compatibility and Stability -Experimental features do not fall within the scope of the OpenTelemetry Go versioning and stability [policy](../../../../VERSIONING.md). +Experimental features do not fall within the scope of the OpenTelemetry Go versioning and stability [policy](../../../../../../VERSIONING.md). These features may be removed or modified in successive version releases, including patch versions. When an experimental feature is promoted to a stable feature, a migration path will be included in the changelog entry of the release. diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/observ.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/observ.go new file mode 100644 index 0000000000..4e89c6524f --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/observ.go @@ -0,0 +1,22 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package x // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x" + +import "strings" + +// Observability is an experimental feature flag that determines if exporter +// observability metrics are enabled. +// +// To enable this feature set the OTEL_GO_X_OBSERVABILITY environment variable +// to the case-insensitive string value of "true" (i.e. "True" and "TRUE" +// will also enable this). +var Observability = newFeature( + []string{"OBSERVABILITY"}, + func(v string) (string, bool) { + if strings.EqualFold(v, "true") { + return v, true + } + return "", false + }, +) diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/x/x.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/x.go similarity index 55% rename from vendor/go.opentelemetry.io/otel/sdk/trace/internal/x/x.go rename to vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/x.go index 2fcbbcc66e..741ba62c97 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/x/x.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x/x.go @@ -1,45 +1,38 @@ +// Code generated by gotmpl. DO NOT MODIFY. +// source: internal/shared/x/x.go.tmpl + // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -// Package x documents experimental features for [go.opentelemetry.io/otel/sdk/trace]. -package x // import "go.opentelemetry.io/otel/sdk/trace/internal/x" +// Package x documents experimental features for [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc]. +package x // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x" import ( "os" - "strings" ) -// SelfObservability is an experimental feature flag that determines if SDK -// self-observability metrics are enabled. -// -// To enable this feature set the OTEL_GO_X_SELF_OBSERVABILITY environment variable -// to the case-insensitive string value of "true" (i.e. "True" and "TRUE" -// will also enable this). -var SelfObservability = newFeature("SELF_OBSERVABILITY", func(v string) (string, bool) { - if strings.EqualFold(v, "true") { - return v, true - } - return "", false -}) - // Feature is an experimental feature control flag. It provides a uniform way // to interact with these feature flags and parse their values. type Feature[T any] struct { - key string + keys []string parse func(v string) (T, bool) } -func newFeature[T any](suffix string, parse func(string) (T, bool)) Feature[T] { +func newFeature[T any](suffix []string, parse func(string) (T, bool)) Feature[T] { const envKeyRoot = "OTEL_GO_X_" + keys := make([]string, 0, len(suffix)) + for _, s := range suffix { + keys = append(keys, envKeyRoot+s) + } return Feature[T]{ - key: envKeyRoot + suffix, + keys: keys, parse: parse, } } -// Key returns the environment variable key that needs to be set to enable the +// Keys returns the environment variable keys that can be set to enable the // feature. -func (f Feature[T]) Key() string { return f.key } +func (f Feature[T]) Keys() []string { return f.keys } // Lookup returns the user configured value for the feature and true if the // user has enabled the feature. Otherwise, if the feature is not enabled, a @@ -49,11 +42,13 @@ func (f Feature[T]) Lookup() (v T, ok bool) { // // > The SDK MUST interpret an empty value of an environment variable the // > same way as when the variable is unset. - vRaw := os.Getenv(f.key) - if vRaw == "" { - return v, ok + for _, key := range f.keys { + vRaw := os.Getenv(key) + if vRaw != "" { + return f.parse(vRaw) + } } - return f.parse(vRaw) + return v, ok } // Enabled reports whether the feature is enabled. diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go index 3b79c1a0b5..370cd2a6f8 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go @@ -5,5 +5,5 @@ package otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace" // Version is the current release version of the OpenTelemetry OTLP trace exporter in use. func Version() string { - return "1.38.0" + return "1.40.0" } diff --git a/vendor/go.opentelemetry.io/otel/sdk/internal/x/features.go b/vendor/go.opentelemetry.io/otel/sdk/internal/x/features.go new file mode 100644 index 0000000000..bfeb73e811 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/internal/x/features.go @@ -0,0 +1,39 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package x documents experimental features for [go.opentelemetry.io/otel/sdk]. +package x // import "go.opentelemetry.io/otel/sdk/internal/x" + +import "strings" + +// Resource is an experimental feature flag that defines if resource detectors +// should be included experimental semantic conventions. +// +// To enable this feature set the OTEL_GO_X_RESOURCE environment variable +// to the case-insensitive string value of "true" (i.e. "True" and "TRUE" +// will also enable this). +var Resource = newFeature( + []string{"RESOURCE"}, + func(v string) (string, bool) { + if strings.EqualFold(v, "true") { + return v, true + } + return "", false + }, +) + +// Observability is an experimental feature flag that determines if SDK +// observability metrics are enabled. +// +// To enable this feature set the OTEL_GO_X_OBSERVABILITY environment variable +// to the case-insensitive string value of "true" (i.e. "True" and "TRUE" +// will also enable this). +var Observability = newFeature( + []string{"OBSERVABILITY", "SELF_OBSERVABILITY"}, + func(v string) (string, bool) { + if strings.EqualFold(v, "true") { + return v, true + } + return "", false + }, +) diff --git a/vendor/go.opentelemetry.io/otel/sdk/internal/x/x.go b/vendor/go.opentelemetry.io/otel/sdk/internal/x/x.go index 1be472e917..13347e5605 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/internal/x/x.go +++ b/vendor/go.opentelemetry.io/otel/sdk/internal/x/x.go @@ -1,48 +1,38 @@ +// Code generated by gotmpl. DO NOT MODIFY. +// source: internal/shared/x/x.go.tmpl + // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -// Package x contains support for OTel SDK experimental features. -// -// This package should only be used for features defined in the specification. -// It should not be used for experiments or new project ideas. +// Package x documents experimental features for [go.opentelemetry.io/otel/sdk]. package x // import "go.opentelemetry.io/otel/sdk/internal/x" import ( "os" - "strings" ) -// Resource is an experimental feature flag that defines if resource detectors -// should be included experimental semantic conventions. -// -// To enable this feature set the OTEL_GO_X_RESOURCE environment variable -// to the case-insensitive string value of "true" (i.e. "True" and "TRUE" -// will also enable this). -var Resource = newFeature("RESOURCE", func(v string) (string, bool) { - if strings.EqualFold(v, "true") { - return v, true - } - return "", false -}) - // Feature is an experimental feature control flag. It provides a uniform way // to interact with these feature flags and parse their values. type Feature[T any] struct { - key string + keys []string parse func(v string) (T, bool) } -func newFeature[T any](suffix string, parse func(string) (T, bool)) Feature[T] { +func newFeature[T any](suffix []string, parse func(string) (T, bool)) Feature[T] { const envKeyRoot = "OTEL_GO_X_" + keys := make([]string, 0, len(suffix)) + for _, s := range suffix { + keys = append(keys, envKeyRoot+s) + } return Feature[T]{ - key: envKeyRoot + suffix, + keys: keys, parse: parse, } } -// Key returns the environment variable key that needs to be set to enable the +// Keys returns the environment variable keys that can be set to enable the // feature. -func (f Feature[T]) Key() string { return f.key } +func (f Feature[T]) Keys() []string { return f.keys } // Lookup returns the user configured value for the feature and true if the // user has enabled the feature. Otherwise, if the feature is not enabled, a @@ -52,11 +42,13 @@ func (f Feature[T]) Lookup() (v T, ok bool) { // // > The SDK MUST interpret an empty value of an environment variable the // > same way as when the variable is unset. - vRaw := os.Getenv(f.key) - if vRaw == "" { - return v, ok + for _, key := range f.keys { + vRaw := os.Getenv(key) + if vRaw != "" { + return f.parse(vRaw) + } } - return f.parse(vRaw) + return v, ok } // Enabled reports whether the feature is enabled. diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go b/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go index 3f20eb7a56..8a7bb330bf 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go @@ -13,7 +13,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" ) type ( diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/container.go b/vendor/go.opentelemetry.io/otel/sdk/resource/container.go index bbe142d203..a19b39def8 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/container.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/container.go @@ -11,7 +11,7 @@ import ( "os" "regexp" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" ) type containerIDProvider func() (string, error) diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/env.go b/vendor/go.opentelemetry.io/otel/sdk/resource/env.go index 4a1b017eea..c49157224e 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/env.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/env.go @@ -12,7 +12,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" ) const ( diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go index 5fed33d4fb..023621ba76 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go @@ -8,7 +8,7 @@ import ( "errors" "strings" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" ) type hostIDProvider func() (string, error) @@ -51,17 +51,16 @@ type hostIDReaderDarwin struct { execCommand commandExecutor } -// read executes `ioreg -rd1 -c "IOPlatformExpertDevice"` and parses host id +// read executes `/usr/sbin/ioreg -rd1 -c "IOPlatformExpertDevice"` and parses host id // from the IOPlatformUUID line. If the command fails or the uuid cannot be // parsed an error will be returned. func (r *hostIDReaderDarwin) read() (string, error) { - result, err := r.execCommand("ioreg", "-rd1", "-c", "IOPlatformExpertDevice") + result, err := r.execCommand("/usr/sbin/ioreg", "-rd1", "-c", "IOPlatformExpertDevice") if err != nil { return "", err } - lines := strings.Split(result, "\n") - for _, line := range lines { + for line := range strings.SplitSeq(result, "\n") { if strings.Contains(line, "IOPlatformUUID") { parts := strings.Split(line, " = ") if len(parts) == 2 { diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_bsd.go b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_bsd.go index cc8b8938ed..4c1c30f256 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_bsd.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_bsd.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build dragonfly || freebsd || netbsd || openbsd || solaris -// +build dragonfly freebsd netbsd openbsd solaris package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_linux.go b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_linux.go index f84f173240..4a26096c8d 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_linux.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_linux.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build linux -// +build linux package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_unsupported.go b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_unsupported.go index df12c44c56..63ad2fa4e0 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_unsupported.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_unsupported.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows -// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_windows.go b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_windows.go index 3677c83d7d..2b8ca20b38 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_windows.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_windows.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build windows -// +build windows package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/os.go b/vendor/go.opentelemetry.io/otel/sdk/resource/os.go index 51da76e807..534809e217 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/os.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/os.go @@ -8,7 +8,7 @@ import ( "strings" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" ) type osDescriptionProvider func() (string, error) diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_unix.go b/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_unix.go index 7252af79fc..a1763267c2 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_unix.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/os_release_unix.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build aix || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix dragonfly freebsd linux netbsd openbsd solaris zos package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go b/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go index a6ff26a4d2..6c50ab6867 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/os_unsupported.go b/vendor/go.opentelemetry.io/otel/sdk/resource/os_unsupported.go index a77742b077..25f629532a 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/os_unsupported.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/os_unsupported.go @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package resource // import "go.opentelemetry.io/otel/sdk/resource" diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/process.go b/vendor/go.opentelemetry.io/otel/sdk/resource/process.go index 138e57721b..a1189553c7 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/process.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/process.go @@ -11,7 +11,7 @@ import ( "path/filepath" "runtime" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" ) type ( diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go b/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go index 9bc3e525d1..7d15cbb9c0 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go @@ -6,20 +6,14 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" "errors" - "fmt" "sync" "sync/atomic" "time" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/internal/global" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/sdk" - "go.opentelemetry.io/otel/sdk/internal/env" - "go.opentelemetry.io/otel/sdk/trace/internal/x" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" - "go.opentelemetry.io/otel/semconv/v1.37.0/otelconv" + "go.opentelemetry.io/otel/sdk/trace/internal/env" + "go.opentelemetry.io/otel/sdk/trace/internal/observ" "go.opentelemetry.io/otel/trace" ) @@ -33,8 +27,6 @@ const ( DefaultMaxExportBatchSize = 512 ) -var queueFull = otelconv.ErrorTypeAttr("queue_full") - // BatchSpanProcessorOption configures a BatchSpanProcessor. type BatchSpanProcessorOption func(o *BatchSpanProcessorOptions) @@ -78,10 +70,7 @@ type batchSpanProcessor struct { queue chan ReadOnlySpan dropped uint32 - selfObservabilityEnabled bool - callbackRegistration metric.Registration - spansProcessedCounter otelconv.SDKProcessorSpanProcessed - componentNameAttr attribute.KeyValue + inst *observ.BSP batch []ReadOnlySpan batchMutex sync.Mutex @@ -124,19 +113,14 @@ func NewBatchSpanProcessor(exporter SpanExporter, options ...BatchSpanProcessorO stopCh: make(chan struct{}), } - if x.SelfObservability.Enabled() { - bsp.selfObservabilityEnabled = true - bsp.componentNameAttr = componentName() - - var err error - bsp.spansProcessedCounter, bsp.callbackRegistration, err = newBSPObs( - bsp.componentNameAttr, - func() int64 { return int64(len(bsp.queue)) }, - int64(bsp.o.MaxQueueSize), - ) - if err != nil { - otel.Handle(err) - } + var err error + bsp.inst, err = observ.NewBSP( + nextProcessorID(), + func() int64 { return int64(len(bsp.queue)) }, + int64(bsp.o.MaxQueueSize), + ) + if err != nil { + otel.Handle(err) } bsp.stopWait.Add(1) @@ -157,51 +141,6 @@ func nextProcessorID() int64 { return processorIDCounter.Add(1) - 1 } -func componentName() attribute.KeyValue { - id := nextProcessorID() - name := fmt.Sprintf("%s/%d", otelconv.ComponentTypeBatchingSpanProcessor, id) - return semconv.OTelComponentName(name) -} - -// newBSPObs creates and returns a new set of metrics instruments and a -// registration for a BatchSpanProcessor. It is the caller's responsibility -// to unregister the registration when it is no longer needed. -func newBSPObs( - cmpnt attribute.KeyValue, - qLen func() int64, - qMax int64, -) (otelconv.SDKProcessorSpanProcessed, metric.Registration, error) { - meter := otel.GetMeterProvider().Meter( - selfObsScopeName, - metric.WithInstrumentationVersion(sdk.Version()), - metric.WithSchemaURL(semconv.SchemaURL), - ) - - qCap, err := otelconv.NewSDKProcessorSpanQueueCapacity(meter) - - qSize, e := otelconv.NewSDKProcessorSpanQueueSize(meter) - err = errors.Join(err, e) - - spansProcessed, e := otelconv.NewSDKProcessorSpanProcessed(meter) - err = errors.Join(err, e) - - cmpntT := semconv.OTelComponentTypeBatchingSpanProcessor - attrs := metric.WithAttributes(cmpnt, cmpntT) - - reg, e := meter.RegisterCallback( - func(_ context.Context, o metric.Observer) error { - o.ObserveInt64(qSize.Inst(), qLen(), attrs) - o.ObserveInt64(qCap.Inst(), qMax, attrs) - return nil - }, - qSize.Inst(), - qCap.Inst(), - ) - err = errors.Join(err, e) - - return spansProcessed, reg, err -} - // OnStart method does nothing. func (*batchSpanProcessor) OnStart(context.Context, ReadWriteSpan) {} @@ -242,8 +181,8 @@ func (bsp *batchSpanProcessor) Shutdown(ctx context.Context) error { case <-ctx.Done(): err = ctx.Err() } - if bsp.selfObservabilityEnabled { - err = errors.Join(err, bsp.callbackRegistration.Unregister()) + if bsp.inst != nil { + err = errors.Join(err, bsp.inst.Shutdown()) } }) return err @@ -357,10 +296,8 @@ func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error { if l := len(bsp.batch); l > 0 { global.Debug("exporting spans", "count", len(bsp.batch), "total_dropped", atomic.LoadUint32(&bsp.dropped)) - if bsp.selfObservabilityEnabled { - bsp.spansProcessedCounter.Add(ctx, int64(l), - bsp.componentNameAttr, - bsp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeBatchingSpanProcessor)) + if bsp.inst != nil { + bsp.inst.Processed(ctx, int64(l)) } err := bsp.e.ExportSpans(ctx, bsp.batch) @@ -470,11 +407,8 @@ func (bsp *batchSpanProcessor) enqueueBlockOnQueueFull(ctx context.Context, sd R case bsp.queue <- sd: return true case <-ctx.Done(): - if bsp.selfObservabilityEnabled { - bsp.spansProcessedCounter.Add(ctx, 1, - bsp.componentNameAttr, - bsp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeBatchingSpanProcessor), - bsp.spansProcessedCounter.AttrErrorType(queueFull)) + if bsp.inst != nil { + bsp.inst.ProcessedQueueFull(ctx, 1) } return false } @@ -490,11 +424,8 @@ func (bsp *batchSpanProcessor) enqueueDrop(ctx context.Context, sd ReadOnlySpan) return true default: atomic.AddUint32(&bsp.dropped, 1) - if bsp.selfObservabilityEnabled { - bsp.spansProcessedCounter.Add(ctx, 1, - bsp.componentNameAttr, - bsp.spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeBatchingSpanProcessor), - bsp.spansProcessedCounter.AttrErrorType(queueFull)) + if bsp.inst != nil { + bsp.inst.ProcessedQueueFull(ctx, 1) } } return false diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/doc.go b/vendor/go.opentelemetry.io/otel/sdk/trace/doc.go index e58e7f6ed7..b502c7d479 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/doc.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/doc.go @@ -7,7 +7,7 @@ Package trace contains support for OpenTelemetry distributed tracing. The following assumes a basic familiarity with OpenTelemetry concepts. See https://opentelemetry.io. -See [go.opentelemetry.io/otel/sdk/trace/internal/x] for information about +See [go.opentelemetry.io/otel/sdk/internal/x] for information about the experimental features. */ package trace // import "go.opentelemetry.io/otel/sdk/trace" diff --git a/vendor/go.opentelemetry.io/otel/sdk/internal/env/env.go b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/env/env.go similarity index 98% rename from vendor/go.opentelemetry.io/otel/sdk/internal/env/env.go rename to vendor/go.opentelemetry.io/otel/sdk/trace/internal/env/env.go index e3309231d4..58f68df441 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/internal/env/env.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/env/env.go @@ -3,7 +3,7 @@ // Package env provides types and functionality for environment variable support // in the OpenTelemetry SDK. -package env // import "go.opentelemetry.io/otel/sdk/internal/env" +package env // import "go.opentelemetry.io/otel/sdk/trace/internal/env" import ( "os" diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/batch_span_processor.go b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/batch_span_processor.go new file mode 100644 index 0000000000..d9cfba0b45 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/batch_span_processor.go @@ -0,0 +1,119 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ" + +import ( + "context" + "errors" + "fmt" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/sdk" + "go.opentelemetry.io/otel/sdk/internal/x" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" + "go.opentelemetry.io/otel/semconv/v1.39.0/otelconv" +) + +const ( + // ScopeName is the name of the instrumentation scope. + ScopeName = "go.opentelemetry.io/otel/sdk/trace/internal/observ" + + // SchemaURL is the schema URL of the instrumentation. + SchemaURL = semconv.SchemaURL +) + +// ErrQueueFull is the attribute value for the "queue_full" error type. +var ErrQueueFull = otelconv.SDKProcessorSpanProcessed{}.AttrErrorType( + otelconv.ErrorTypeAttr("queue_full"), +) + +// BSPComponentName returns the component name attribute for a +// BatchSpanProcessor with the given ID. +func BSPComponentName(id int64) attribute.KeyValue { + t := otelconv.ComponentTypeBatchingSpanProcessor + name := fmt.Sprintf("%s/%d", t, id) + return semconv.OTelComponentName(name) +} + +// BSP is the instrumentation for an OTel SDK BatchSpanProcessor. +type BSP struct { + reg metric.Registration + + processed metric.Int64Counter + processedOpts []metric.AddOption + processedQueueFullOpts []metric.AddOption +} + +func NewBSP(id int64, qLen func() int64, qMax int64) (*BSP, error) { + if !x.Observability.Enabled() { + return nil, nil + } + + meter := otel.GetMeterProvider().Meter( + ScopeName, + metric.WithInstrumentationVersion(sdk.Version()), + metric.WithSchemaURL(SchemaURL), + ) + + qCap, err := otelconv.NewSDKProcessorSpanQueueCapacity(meter) + if err != nil { + err = fmt.Errorf("failed to create BSP queue capacity metric: %w", err) + } + qCapInst := qCap.Inst() + + qSize, e := otelconv.NewSDKProcessorSpanQueueSize(meter) + if e != nil { + e := fmt.Errorf("failed to create BSP queue size metric: %w", e) + err = errors.Join(err, e) + } + qSizeInst := qSize.Inst() + + cmpntT := semconv.OTelComponentTypeBatchingSpanProcessor + cmpnt := BSPComponentName(id) + set := attribute.NewSet(cmpnt, cmpntT) + + obsOpts := []metric.ObserveOption{metric.WithAttributeSet(set)} + reg, e := meter.RegisterCallback( + func(_ context.Context, o metric.Observer) error { + o.ObserveInt64(qSizeInst, qLen(), obsOpts...) + o.ObserveInt64(qCapInst, qMax, obsOpts...) + return nil + }, + qSizeInst, + qCapInst, + ) + if e != nil { + e := fmt.Errorf("failed to register BSP queue size/capacity callback: %w", e) + err = errors.Join(err, e) + } + + processed, e := otelconv.NewSDKProcessorSpanProcessed(meter) + if e != nil { + e := fmt.Errorf("failed to create BSP processed spans metric: %w", e) + err = errors.Join(err, e) + } + processedOpts := []metric.AddOption{metric.WithAttributeSet(set)} + + set = attribute.NewSet(cmpnt, cmpntT, ErrQueueFull) + processedQueueFullOpts := []metric.AddOption{metric.WithAttributeSet(set)} + + return &BSP{ + reg: reg, + processed: processed.Inst(), + processedOpts: processedOpts, + processedQueueFullOpts: processedQueueFullOpts, + }, err +} + +func (b *BSP) Shutdown() error { return b.reg.Unregister() } + +func (b *BSP) Processed(ctx context.Context, n int64) { + b.processed.Add(ctx, n, b.processedOpts...) +} + +func (b *BSP) ProcessedQueueFull(ctx context.Context, n int64) { + b.processed.Add(ctx, n, b.processedQueueFullOpts...) +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/doc.go b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/doc.go new file mode 100644 index 0000000000..b542121e6a --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/doc.go @@ -0,0 +1,6 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package observ provides observability instrumentation for the OTel trace SDK +// package. +package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ" diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/simple_span_processor.go b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/simple_span_processor.go new file mode 100644 index 0000000000..8afd052679 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/simple_span_processor.go @@ -0,0 +1,97 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ" + +import ( + "context" + "fmt" + "sync" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/sdk" + "go.opentelemetry.io/otel/sdk/internal/x" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" + "go.opentelemetry.io/otel/semconv/v1.39.0/otelconv" +) + +var measureAttrsPool = sync.Pool{ + New: func() any { + // "component.name" + "component.type" + "error.type" + const n = 1 + 1 + 1 + s := make([]attribute.KeyValue, 0, n) + // Return a pointer to a slice instead of a slice itself + // to avoid allocations on every call. + return &s + }, +} + +// SSP is the instrumentation for an OTel SDK SimpleSpanProcessor. +type SSP struct { + spansProcessedCounter metric.Int64Counter + addOpts []metric.AddOption + attrs []attribute.KeyValue +} + +// SSPComponentName returns the component name attribute for a +// SimpleSpanProcessor with the given ID. +func SSPComponentName(id int64) attribute.KeyValue { + t := otelconv.ComponentTypeSimpleSpanProcessor + name := fmt.Sprintf("%s/%d", t, id) + return semconv.OTelComponentName(name) +} + +// NewSSP returns instrumentation for an OTel SDK SimpleSpanProcessor with the +// provided ID. +// +// If the experimental observability is disabled, nil is returned. +func NewSSP(id int64) (*SSP, error) { + if !x.Observability.Enabled() { + return nil, nil + } + + meter := otel.GetMeterProvider().Meter( + ScopeName, + metric.WithInstrumentationVersion(sdk.Version()), + metric.WithSchemaURL(SchemaURL), + ) + spansProcessedCounter, err := otelconv.NewSDKProcessorSpanProcessed(meter) + if err != nil { + err = fmt.Errorf("failed to create SSP processed spans metric: %w", err) + } + + componentName := SSPComponentName(id) + componentType := spansProcessedCounter.AttrComponentType(otelconv.ComponentTypeSimpleSpanProcessor) + attrs := []attribute.KeyValue{componentName, componentType} + addOpts := []metric.AddOption{metric.WithAttributeSet(attribute.NewSet(attrs...))} + + return &SSP{ + spansProcessedCounter: spansProcessedCounter.Inst(), + addOpts: addOpts, + attrs: attrs, + }, err +} + +// SpanProcessed records that a span has been processed by the SimpleSpanProcessor. +// If err is non-nil, it records the processing error as an attribute. +func (ssp *SSP) SpanProcessed(ctx context.Context, err error) { + ssp.spansProcessedCounter.Add(ctx, 1, ssp.addOption(err)...) +} + +func (ssp *SSP) addOption(err error) []metric.AddOption { + if err == nil { + return ssp.addOpts + } + attrs := measureAttrsPool.Get().(*[]attribute.KeyValue) + defer func() { + *attrs = (*attrs)[:0] // reset the slice for reuse + measureAttrsPool.Put(attrs) + }() + *attrs = append(*attrs, ssp.attrs...) + *attrs = append(*attrs, semconv.ErrorType(err)) + // Do not inefficiently make a copy of attrs by using + // WithAttributes instead of WithAttributeSet. + return []metric.AddOption{metric.WithAttributeSet(attribute.NewSet(*attrs...))} +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go new file mode 100644 index 0000000000..13a2db2969 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go @@ -0,0 +1,223 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package observ // import "go.opentelemetry.io/otel/sdk/trace/internal/observ" + +import ( + "context" + "errors" + "fmt" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/sdk" + "go.opentelemetry.io/otel/sdk/internal/x" + "go.opentelemetry.io/otel/semconv/v1.39.0/otelconv" + "go.opentelemetry.io/otel/trace" +) + +var meterOpts = []metric.MeterOption{ + metric.WithInstrumentationVersion(sdk.Version()), + metric.WithSchemaURL(SchemaURL), +} + +// Tracer is instrumentation for an OTel SDK Tracer. +type Tracer struct { + enabled bool + + live metric.Int64UpDownCounter + started metric.Int64Counter +} + +func NewTracer() (Tracer, error) { + if !x.Observability.Enabled() { + return Tracer{}, nil + } + meter := otel.GetMeterProvider().Meter(ScopeName, meterOpts...) + + var err error + l, e := otelconv.NewSDKSpanLive(meter) + if e != nil { + e = fmt.Errorf("failed to create span live metric: %w", e) + err = errors.Join(err, e) + } + + s, e := otelconv.NewSDKSpanStarted(meter) + if e != nil { + e = fmt.Errorf("failed to create span started metric: %w", e) + err = errors.Join(err, e) + } + + return Tracer{enabled: true, live: l.Inst(), started: s.Inst()}, err +} + +func (t Tracer) Enabled() bool { return t.enabled } + +func (t Tracer) SpanStarted(ctx context.Context, psc trace.SpanContext, span trace.Span) { + key := spanStartedKey{ + parent: parentStateNoParent, + sampling: samplingStateDrop, + } + + if psc.IsValid() { + if psc.IsRemote() { + key.parent = parentStateRemoteParent + } else { + key.parent = parentStateLocalParent + } + } + + if span.IsRecording() { + if span.SpanContext().IsSampled() { + key.sampling = samplingStateRecordAndSample + } else { + key.sampling = samplingStateRecordOnly + } + } + + opts := spanStartedOpts[key] + t.started.Add(ctx, 1, opts...) +} + +func (t Tracer) SpanLive(ctx context.Context, span trace.Span) { + t.spanLive(ctx, 1, span) +} + +func (t Tracer) SpanEnded(ctx context.Context, span trace.Span) { + t.spanLive(ctx, -1, span) +} + +func (t Tracer) spanLive(ctx context.Context, value int64, span trace.Span) { + key := spanLiveKey{sampled: span.SpanContext().IsSampled()} + opts := spanLiveOpts[key] + t.live.Add(ctx, value, opts...) +} + +type parentState int + +const ( + parentStateNoParent parentState = iota + parentStateLocalParent + parentStateRemoteParent +) + +type samplingState int + +const ( + samplingStateDrop samplingState = iota + samplingStateRecordOnly + samplingStateRecordAndSample +) + +type spanStartedKey struct { + parent parentState + sampling samplingState +} + +var spanStartedOpts = map[spanStartedKey][]metric.AddOption{ + { + parentStateNoParent, + samplingStateDrop, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop), + )), + }, + { + parentStateLocalParent, + samplingStateDrop, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop), + )), + }, + { + parentStateRemoteParent, + samplingStateDrop, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop), + )), + }, + + { + parentStateNoParent, + samplingStateRecordOnly, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly), + )), + }, + { + parentStateLocalParent, + samplingStateRecordOnly, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly), + )), + }, + { + parentStateRemoteParent, + samplingStateRecordOnly, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly), + )), + }, + + { + parentStateNoParent, + samplingStateRecordAndSample, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample), + )), + }, + { + parentStateLocalParent, + samplingStateRecordAndSample, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample), + )), + }, + { + parentStateRemoteParent, + samplingStateRecordAndSample, + }: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote), + otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample), + )), + }, +} + +type spanLiveKey struct { + sampled bool +} + +var spanLiveOpts = map[spanLiveKey][]metric.AddOption{ + {true}: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanLive{}.AttrSpanSamplingResult( + otelconv.SpanSamplingResultRecordAndSample, + ), + )), + }, + {false}: { + metric.WithAttributeSet(attribute.NewSet( + otelconv.SDKSpanLive{}.AttrSpanSamplingResult( + otelconv.SpanSamplingResultRecordOnly, + ), + )), + }, +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go b/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go index 37ce2ac876..d2cf4ebd3e 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go @@ -5,29 +5,21 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" - "errors" "fmt" "sync" "sync/atomic" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/internal/global" - "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/sdk" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" - "go.opentelemetry.io/otel/sdk/trace/internal/x" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" - "go.opentelemetry.io/otel/semconv/v1.37.0/otelconv" + "go.opentelemetry.io/otel/sdk/trace/internal/observ" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace/embedded" "go.opentelemetry.io/otel/trace/noop" ) -const ( - defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer" - selfObsScopeName = "go.opentelemetry.io/otel/sdk/trace" -) +const defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer" // tracerProviderConfig. type tracerProviderConfig struct { @@ -163,19 +155,16 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T t, ok := p.namedTracer[is] if !ok { t = &tracer{ - provider: p, - instrumentationScope: is, - selfObservabilityEnabled: x.SelfObservability.Enabled(), + provider: p, + instrumentationScope: is, } - if t.selfObservabilityEnabled { - var err error - t.spanLiveMetric, t.spanStartedMetric, err = newInst() - if err != nil { - msg := "failed to create self-observability metrics for tracer: %w" - err := fmt.Errorf(msg, err) - otel.Handle(err) - } + + var err error + t.inst, err = observ.NewTracer() + if err != nil { + otel.Handle(err) } + p.namedTracer[is] = t } return t, ok @@ -201,23 +190,6 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T return t } -func newInst() (otelconv.SDKSpanLive, otelconv.SDKSpanStarted, error) { - m := otel.GetMeterProvider().Meter( - selfObsScopeName, - metric.WithInstrumentationVersion(sdk.Version()), - metric.WithSchemaURL(semconv.SchemaURL), - ) - - var err error - spanLiveMetric, e := otelconv.NewSDKSpanLive(m) - err = errors.Join(err, e) - - spanStartedMetric, e := otelconv.NewSDKSpanStarted(m) - err = errors.Join(err, e) - - return spanLiveMetric, spanStartedMetric, err -} - // RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors. func (p *TracerProvider) RegisterSpanProcessor(sp SpanProcessor) { // This check prevents calls during a shutdown. diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go b/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go index 689663d48b..81c5060ad6 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go @@ -280,3 +280,31 @@ func (pb parentBased) Description() string { pb.config.localParentNotSampled.Description(), ) } + +// AlwaysRecord returns a sampler decorator which ensures that every span +// is passed to the SpanProcessor, even those that would be normally dropped. +// It converts `Drop` decisions from the root sampler into `RecordOnly` decisions, +// allowing processors to see all spans without sending them to exporters. This is +// typically used to enable accurate span-to-metrics processing. +func AlwaysRecord(root Sampler) Sampler { + return alwaysRecord{root} +} + +type alwaysRecord struct { + root Sampler +} + +func (ar alwaysRecord) ShouldSample(p SamplingParameters) SamplingResult { + rootSamplerSamplingResult := ar.root.ShouldSample(p) + if rootSamplerSamplingResult.Decision == Drop { + return SamplingResult{ + Decision: RecordOnly, + Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(), + } + } + return rootSamplerSamplingResult +} + +func (ar alwaysRecord) Description() string { + return "AlwaysRecord{root:" + ar.root.Description() + "}" +} diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go b/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go index 411d9ccdd7..771e427a4c 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/simple_span_processor.go @@ -6,9 +6,12 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace" import ( "context" "sync" + "sync/atomic" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/internal/global" + "go.opentelemetry.io/otel/sdk/trace/internal/observ" + "go.opentelemetry.io/otel/trace" ) // simpleSpanProcessor is a SpanProcessor that synchronously sends all @@ -17,6 +20,8 @@ type simpleSpanProcessor struct { exporterMu sync.Mutex exporter SpanExporter stopOnce sync.Once + + inst *observ.SSP } var _ SpanProcessor = (*simpleSpanProcessor)(nil) @@ -33,11 +38,26 @@ func NewSimpleSpanProcessor(exporter SpanExporter) SpanProcessor { ssp := &simpleSpanProcessor{ exporter: exporter, } + + var err error + ssp.inst, err = observ.NewSSP(nextSimpleProcessorID()) + if err != nil { + otel.Handle(err) + } + global.Warn("SimpleSpanProcessor is not recommended for production use, consider using BatchSpanProcessor instead.") return ssp } +var simpleProcessorIDCounter atomic.Int64 + +// nextSimpleProcessorID returns an identifier for this simple span processor, +// starting with 0 and incrementing by 1 each time it is called. +func nextSimpleProcessorID() int64 { + return simpleProcessorIDCounter.Add(1) - 1 +} + // OnStart does nothing. func (*simpleSpanProcessor) OnStart(context.Context, ReadWriteSpan) {} @@ -46,11 +66,20 @@ func (ssp *simpleSpanProcessor) OnEnd(s ReadOnlySpan) { ssp.exporterMu.Lock() defer ssp.exporterMu.Unlock() + var err error if ssp.exporter != nil && s.SpanContext().TraceFlags().IsSampled() { - if err := ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}); err != nil { + err = ssp.exporter.ExportSpans(context.Background(), []ReadOnlySpan{s}) + if err != nil { otel.Handle(err) } } + + if ssp.inst != nil { + // Add the span to the context to ensure the metric is recorded + // with the correct span context. + ctx := trace.ContextWithSpanContext(context.Background(), s.SpanContext()) + ssp.inst.SpanProcessed(ctx, err) + } } // Shutdown shuts down the exporter this SimpleSpanProcessor exports to. diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/span.go b/vendor/go.opentelemetry.io/otel/sdk/trace/span.go index b376051fbb..d466610591 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/span.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/span.go @@ -20,7 +20,7 @@ import ( "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.39.0" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace/embedded" ) @@ -151,6 +151,12 @@ type recordingSpan struct { // tracer is the SDK tracer that created this span. tracer *tracer + + // origCtx is the context used when starting this span that has the + // recordingSpan instance set as the active span. If not nil, it is used + // when ending the span to ensure any metrics are recorded with a context + // containing this span without requiring an additional allocation. + origCtx context.Context } var ( @@ -158,6 +164,10 @@ var ( _ runtimeTracer = (*recordingSpan)(nil) ) +func (s *recordingSpan) setOrigCtx(ctx context.Context) { + s.origCtx = ctx +} + // SpanContext returns the SpanContext of this span. func (s *recordingSpan) SpanContext() trace.SpanContext { if s == nil { @@ -496,14 +506,15 @@ func (s *recordingSpan) End(options ...trace.SpanEndOption) { } s.mu.Unlock() - if s.tracer.selfObservabilityEnabled { - defer func() { - // Add the span to the context to ensure the metric is recorded - // with the correct span context. - ctx := trace.ContextWithSpan(context.Background(), s) - set := spanLiveSet(s.spanContext.IsSampled()) - s.tracer.spanLiveMetric.AddSet(ctx, -1, set) - }() + if s.tracer.inst.Enabled() { + ctx := s.origCtx + if ctx == nil { + // This should not happen as the origCtx should be set, but + // ensure trace information is propagated in the case of an + // error. + ctx = trace.ContextWithSpan(context.Background(), s) + } + defer s.tracer.inst.SpanEnded(ctx, s) } sps := s.tracer.provider.getSpanProcessors() diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go b/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go index bec5e20978..321d974305 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go @@ -3,7 +3,7 @@ package trace // import "go.opentelemetry.io/otel/sdk/trace" -import "go.opentelemetry.io/otel/sdk/internal/env" +import "go.opentelemetry.io/otel/sdk/trace/internal/env" const ( // DefaultAttributeValueLengthLimit is the default maximum allowed diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go b/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go index e965c4cce8..e1d08fd4d8 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/tracer.go @@ -7,9 +7,8 @@ import ( "context" "time" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/instrumentation" - "go.opentelemetry.io/otel/semconv/v1.37.0/otelconv" + "go.opentelemetry.io/otel/sdk/trace/internal/observ" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace/embedded" ) @@ -20,9 +19,7 @@ type tracer struct { provider *TracerProvider instrumentationScope instrumentation.Scope - selfObservabilityEnabled bool - spanLiveMetric otelconv.SDKSpanLive - spanStartedMetric otelconv.SDKSpanStarted + inst observ.Tracer } var _ trace.Tracer = &tracer{} @@ -53,10 +50,17 @@ func (tr *tracer) Start( s := tr.newSpan(ctx, name, &config) newCtx := trace.ContextWithSpan(ctx, s) - if tr.selfObservabilityEnabled { + if tr.inst.Enabled() { + if o, ok := s.(interface{ setOrigCtx(context.Context) }); ok { + // If this is a recording span, store the original context. + // This allows later retrieval of baggage and other information + // that may have been stored in the context at span start time and + // to avoid the allocation of repeatedly calling + // trace.ContextWithSpan. + o.setOrigCtx(newCtx) + } psc := trace.SpanContextFromContext(ctx) - set := spanStartedSet(psc, s) - tr.spanStartedMetric.AddSet(newCtx, 1, set) + tr.inst.SpanStarted(newCtx, psc, s) } if rw, ok := s.(ReadWriteSpan); ok && s.IsRecording() { @@ -168,12 +172,11 @@ func (tr *tracer) newRecordingSpan( s.SetAttributes(sr.Attributes...) s.SetAttributes(config.Attributes()...) - if tr.selfObservabilityEnabled { + if tr.inst.Enabled() { // Propagate any existing values from the context with the new span to // the measurement context. ctx = trace.ContextWithSpan(ctx, s) - set := spanLiveSet(s.spanContext.IsSampled()) - tr.spanLiveMetric.AddSet(ctx, 1, set) + tr.inst.SpanLive(ctx, s) } return s @@ -183,112 +186,3 @@ func (tr *tracer) newRecordingSpan( func (tr *tracer) newNonRecordingSpan(sc trace.SpanContext) nonRecordingSpan { return nonRecordingSpan{tracer: tr, sc: sc} } - -type parentState int - -const ( - parentStateNoParent parentState = iota - parentStateLocalParent - parentStateRemoteParent -) - -type samplingState int - -const ( - samplingStateDrop samplingState = iota - samplingStateRecordOnly - samplingStateRecordAndSample -) - -type spanStartedSetKey struct { - parent parentState - sampling samplingState -} - -var spanStartedSetCache = map[spanStartedSetKey]attribute.Set{ - {parentStateNoParent, samplingStateDrop}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop), - ), - {parentStateLocalParent, samplingStateDrop}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop), - ), - {parentStateRemoteParent, samplingStateDrop}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop), - ), - - {parentStateNoParent, samplingStateRecordOnly}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly), - ), - {parentStateLocalParent, samplingStateRecordOnly}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly), - ), - {parentStateRemoteParent, samplingStateRecordOnly}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly), - ), - - {parentStateNoParent, samplingStateRecordAndSample}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample), - ), - {parentStateLocalParent, samplingStateRecordAndSample}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample), - ), - {parentStateRemoteParent, samplingStateRecordAndSample}: attribute.NewSet( - otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote), - otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample), - ), -} - -func spanStartedSet(psc trace.SpanContext, span trace.Span) attribute.Set { - key := spanStartedSetKey{ - parent: parentStateNoParent, - sampling: samplingStateDrop, - } - - if psc.IsValid() { - if psc.IsRemote() { - key.parent = parentStateRemoteParent - } else { - key.parent = parentStateLocalParent - } - } - - if span.IsRecording() { - if span.SpanContext().IsSampled() { - key.sampling = samplingStateRecordAndSample - } else { - key.sampling = samplingStateRecordOnly - } - } - - return spanStartedSetCache[key] -} - -type spanLiveSetKey struct { - sampled bool -} - -var spanLiveSetCache = map[spanLiveSetKey]attribute.Set{ - {true}: attribute.NewSet( - otelconv.SDKSpanLive{}.AttrSpanSamplingResult( - otelconv.SpanSamplingResultRecordAndSample, - ), - ), - {false}: attribute.NewSet( - otelconv.SDKSpanLive{}.AttrSpanSamplingResult( - otelconv.SpanSamplingResultRecordOnly, - ), - ), -} - -func spanLiveSet(sampled bool) attribute.Set { - key := spanLiveSetKey{sampled: sampled} - return spanLiveSetCache[key] -} diff --git a/vendor/go.opentelemetry.io/otel/sdk/version.go b/vendor/go.opentelemetry.io/otel/sdk/version.go index 7f97cc31e5..b5497c2816 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/version.go +++ b/vendor/go.opentelemetry.io/otel/sdk/version.go @@ -6,5 +6,5 @@ package sdk // import "go.opentelemetry.io/otel/sdk" // Version is the current release version of the OpenTelemetry SDK in use. func Version() string { - return "1.38.0" + return "1.40.0" } diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/httpconv/metric.go b/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/httpconv/metric.go similarity index 98% rename from vendor/go.opentelemetry.io/otel/semconv/v1.37.0/httpconv/metric.go rename to vendor/go.opentelemetry.io/otel/semconv/v1.39.0/httpconv/metric.go index a0ddf652d3..cb993812a8 100644 --- a/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/httpconv/metric.go +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/httpconv/metric.go @@ -67,6 +67,8 @@ var ( RequestMethodPut RequestMethodAttr = "PUT" // RequestMethodTrace is the TRACE method. RequestMethodTrace RequestMethodAttr = "TRACE" + // RequestMethodQuery is the QUERY method. + RequestMethodQuery RequestMethodAttr = "QUERY" // RequestMethodOther is the any HTTP method that the instrumentation has no // prior knowledge of. RequestMethodOther RequestMethodAttr = "_OTHER" @@ -1318,8 +1320,9 @@ func (ServerRequestBodySize) AttrResponseStatusCode(val int) attribute.KeyValue } // AttrRoute returns an optional attribute for the "http.route" semantic -// convention. It represents the matched route, that is, the path template in the -// format used by the respective server framework. +// convention. It represents the matched route template for the request. This +// MUST be low-cardinality and include all static path segments, with dynamic +// path segments represented with placeholders. func (ServerRequestBodySize) AttrRoute(val string) attribute.KeyValue { return attribute.String("http.route", val) } @@ -1494,8 +1497,9 @@ func (ServerRequestDuration) AttrResponseStatusCode(val int) attribute.KeyValue } // AttrRoute returns an optional attribute for the "http.route" semantic -// convention. It represents the matched route, that is, the path template in the -// format used by the respective server framework. +// convention. It represents the matched route template for the request. This +// MUST be low-cardinality and include all static path segments, with dynamic +// path segments represented with placeholders. func (ServerRequestDuration) AttrRoute(val string) attribute.KeyValue { return attribute.String("http.route", val) } @@ -1684,8 +1688,9 @@ func (ServerResponseBodySize) AttrResponseStatusCode(val int) attribute.KeyValue } // AttrRoute returns an optional attribute for the "http.route" semantic -// convention. It represents the matched route, that is, the path template in the -// format used by the respective server framework. +// convention. It represents the matched route template for the request. This +// MUST be low-cardinality and include all static path segments, with dynamic +// path segments represented with placeholders. func (ServerResponseBodySize) AttrRoute(val string) attribute.KeyValue { return attribute.String("http.route", val) } diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/otelconv/metric.go b/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/otelconv/metric.go similarity index 96% rename from vendor/go.opentelemetry.io/otel/semconv/v1.37.0/otelconv/metric.go rename to vendor/go.opentelemetry.io/otel/semconv/v1.39.0/otelconv/metric.go index fd064530c3..901da86985 100644 --- a/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/otelconv/metric.go +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.39.0/otelconv/metric.go @@ -122,48 +122,6 @@ var ( SpanSamplingResultRecordAndSample SpanSamplingResultAttr = "RECORD_AND_SAMPLE" ) -// RPCGRPCStatusCodeAttr is an attribute conforming to the rpc.grpc.status_code -// semantic conventions. It represents the gRPC status code of the last gRPC -// requests performed in scope of this export call. -type RPCGRPCStatusCodeAttr int64 - -var ( - // RPCGRPCStatusCodeOk is the OK. - RPCGRPCStatusCodeOk RPCGRPCStatusCodeAttr = 0 - // RPCGRPCStatusCodeCancelled is the CANCELLED. - RPCGRPCStatusCodeCancelled RPCGRPCStatusCodeAttr = 1 - // RPCGRPCStatusCodeUnknown is the UNKNOWN. - RPCGRPCStatusCodeUnknown RPCGRPCStatusCodeAttr = 2 - // RPCGRPCStatusCodeInvalidArgument is the INVALID_ARGUMENT. - RPCGRPCStatusCodeInvalidArgument RPCGRPCStatusCodeAttr = 3 - // RPCGRPCStatusCodeDeadlineExceeded is the DEADLINE_EXCEEDED. - RPCGRPCStatusCodeDeadlineExceeded RPCGRPCStatusCodeAttr = 4 - // RPCGRPCStatusCodeNotFound is the NOT_FOUND. - RPCGRPCStatusCodeNotFound RPCGRPCStatusCodeAttr = 5 - // RPCGRPCStatusCodeAlreadyExists is the ALREADY_EXISTS. - RPCGRPCStatusCodeAlreadyExists RPCGRPCStatusCodeAttr = 6 - // RPCGRPCStatusCodePermissionDenied is the PERMISSION_DENIED. - RPCGRPCStatusCodePermissionDenied RPCGRPCStatusCodeAttr = 7 - // RPCGRPCStatusCodeResourceExhausted is the RESOURCE_EXHAUSTED. - RPCGRPCStatusCodeResourceExhausted RPCGRPCStatusCodeAttr = 8 - // RPCGRPCStatusCodeFailedPrecondition is the FAILED_PRECONDITION. - RPCGRPCStatusCodeFailedPrecondition RPCGRPCStatusCodeAttr = 9 - // RPCGRPCStatusCodeAborted is the ABORTED. - RPCGRPCStatusCodeAborted RPCGRPCStatusCodeAttr = 10 - // RPCGRPCStatusCodeOutOfRange is the OUT_OF_RANGE. - RPCGRPCStatusCodeOutOfRange RPCGRPCStatusCodeAttr = 11 - // RPCGRPCStatusCodeUnimplemented is the UNIMPLEMENTED. - RPCGRPCStatusCodeUnimplemented RPCGRPCStatusCodeAttr = 12 - // RPCGRPCStatusCodeInternal is the INTERNAL. - RPCGRPCStatusCodeInternal RPCGRPCStatusCodeAttr = 13 - // RPCGRPCStatusCodeUnavailable is the UNAVAILABLE. - RPCGRPCStatusCodeUnavailable RPCGRPCStatusCodeAttr = 14 - // RPCGRPCStatusCodeDataLoss is the DATA_LOSS. - RPCGRPCStatusCodeDataLoss RPCGRPCStatusCodeAttr = 15 - // RPCGRPCStatusCodeUnauthenticated is the UNAUTHENTICATED. - RPCGRPCStatusCodeUnauthenticated RPCGRPCStatusCodeAttr = 16 -) - // SDKExporterLogExported is an instrument used to record metric values // conforming to the "otel.sdk.exporter.log.exported" semantic conventions. It // represents the number of log records for which the export has finished, either @@ -906,11 +864,11 @@ func (SDKExporterOperationDuration) AttrComponentType(val ComponentTypeAttr) att return attribute.String("otel.component.type", string(val)) } -// AttrRPCGRPCStatusCode returns an optional attribute for the -// "rpc.grpc.status_code" semantic convention. It represents the gRPC status code -// of the last gRPC requests performed in scope of this export call. -func (SDKExporterOperationDuration) AttrRPCGRPCStatusCode(val RPCGRPCStatusCodeAttr) attribute.KeyValue { - return attribute.Int64("rpc.grpc.status_code", int64(val)) +// AttrRPCResponseStatusCode returns an optional attribute for the +// "rpc.response.status_code" semantic convention. It represents the gRPC status +// code of the last gRPC request performed in scope of this export call. +func (SDKExporterOperationDuration) AttrRPCResponseStatusCode(val string) attribute.KeyValue { + return attribute.String("rpc.response.status_code", val) } // AttrServerAddress returns an optional attribute for the "server.address" diff --git a/vendor/google.golang.org/grpc/balancer/balancer.go b/vendor/google.golang.org/grpc/balancer/balancer.go index b1264017db..d08b7ad637 100644 --- a/vendor/google.golang.org/grpc/balancer/balancer.go +++ b/vendor/google.golang.org/grpc/balancer/balancer.go @@ -75,8 +75,6 @@ func unregisterForTesting(name string) { func init() { internal.BalancerUnregister = unregisterForTesting - internal.ConnectedAddress = connectedAddress - internal.SetConnectedAddress = setConnectedAddress } // Get returns the resolver builder registered with the given name. diff --git a/vendor/google.golang.org/grpc/balancer/pickfirst/internal/internal.go b/vendor/google.golang.org/grpc/balancer/pickfirst/internal/internal.go index 7d66cb491c..cc902a4de6 100644 --- a/vendor/google.golang.org/grpc/balancer/pickfirst/internal/internal.go +++ b/vendor/google.golang.org/grpc/balancer/pickfirst/internal/internal.go @@ -26,6 +26,8 @@ import ( var ( // RandShuffle pseudo-randomizes the order of addresses. RandShuffle = rand.Shuffle + // RandFloat64 returns, as a float64, a pseudo-random number in [0.0,1.0). + RandFloat64 = rand.Float64 // TimeAfterFunc allows mocking the timer for testing connection delay // related functionality. TimeAfterFunc = func(d time.Duration, f func()) func() { diff --git a/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go b/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go index b4bc3a2bf3..dccd9f0bf3 100644 --- a/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go +++ b/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go @@ -21,11 +21,14 @@ package pickfirst import ( + "cmp" "encoding/json" "errors" "fmt" + "math" "net" "net/netip" + "slices" "sync" "time" @@ -34,6 +37,8 @@ import ( "google.golang.org/grpc/connectivity" expstats "google.golang.org/grpc/experimental/stats" "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/internal/balancer/weight" + "google.golang.org/grpc/internal/envconfig" internalgrpclog "google.golang.org/grpc/internal/grpclog" "google.golang.org/grpc/internal/pretty" "google.golang.org/grpc/resolver" @@ -258,8 +263,42 @@ func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState // will change the order of endpoints but not touch the order of the // addresses within each endpoint. - A61 if cfg.ShuffleAddressList { - endpoints = append([]resolver.Endpoint{}, endpoints...) - internal.RandShuffle(len(endpoints), func(i, j int) { endpoints[i], endpoints[j] = endpoints[j], endpoints[i] }) + if envconfig.PickFirstWeightedShuffling { + type weightedEndpoint struct { + endpoint resolver.Endpoint + weight float64 + } + + // For each endpoint, compute a key as described in A113 and + // https://utopia.duth.gr/~pefraimi/research/data/2007EncOfAlg.pdf: + var weightedEndpoints []weightedEndpoint + for _, endpoint := range endpoints { + u := internal.RandFloat64() // Random number in [0.0, 1.0) + weight := weightAttribute(endpoint) + weightedEndpoints = append(weightedEndpoints, weightedEndpoint{ + endpoint: endpoint, + weight: math.Pow(u, 1.0/float64(weight)), + }) + } + // Sort endpoints by key in descending order and reconstruct the + // endpoints slice. + slices.SortFunc(weightedEndpoints, func(a, b weightedEndpoint) int { + return cmp.Compare(b.weight, a.weight) + }) + + // Here, and in the "else" block below, we clone the endpoints + // slice to avoid mutating the resolver state. Doing the latter + // would lead to data races if the caller is accessing the same + // slice concurrently. + sortedEndpoints := make([]resolver.Endpoint, len(endpoints)) + for i, we := range weightedEndpoints { + sortedEndpoints[i] = we.endpoint + } + endpoints = sortedEndpoints + } else { + endpoints = slices.Clone(endpoints) + internal.RandShuffle(len(endpoints), func(i, j int) { endpoints[i], endpoints[j] = endpoints[j], endpoints[i] }) + } } // "Flatten the list by concatenating the ordered list of addresses for @@ -906,3 +945,17 @@ func equalAddressIgnoringBalAttributes(a, b *resolver.Address) bool { return a.Addr == b.Addr && a.ServerName == b.ServerName && a.Attributes.Equal(b.Attributes) } + +// weightAttribute is a convenience function which returns the value of the +// weight endpoint Attribute. +// +// When used in the xDS context, the weight attribute is guaranteed to be +// non-zero. But, when used in a non-xDS context, the weight attribute could be +// unset. A Default of 1 is used in the latter case. +func weightAttribute(e resolver.Endpoint) uint32 { + w := weight.FromEndpoint(e).Weight + if w == 0 { + return 1 + } + return w +} diff --git a/vendor/google.golang.org/grpc/balancer/subconn.go b/vendor/google.golang.org/grpc/balancer/subconn.go index 9ee44d4af0..c1ca7c92e7 100644 --- a/vendor/google.golang.org/grpc/balancer/subconn.go +++ b/vendor/google.golang.org/grpc/balancer/subconn.go @@ -111,20 +111,6 @@ type SubConnState struct { // ConnectionError is set if the ConnectivityState is TransientFailure, // describing the reason the SubConn failed. Otherwise, it is nil. ConnectionError error - // connectedAddr contains the connected address when ConnectivityState is - // Ready. Otherwise, it is indeterminate. - connectedAddress resolver.Address -} - -// connectedAddress returns the connected address for a SubConnState. The -// address is only valid if the state is READY. -func connectedAddress(scs SubConnState) resolver.Address { - return scs.connectedAddress -} - -// setConnectedAddress sets the connected address for a SubConnState. -func setConnectedAddress(scs *SubConnState, addr resolver.Address) { - scs.connectedAddress = addr } // A Producer is a type shared among potentially many consumers. It is diff --git a/vendor/google.golang.org/grpc/balancer_wrapper.go b/vendor/google.golang.org/grpc/balancer_wrapper.go index 2c760e623f..a1e56a3893 100644 --- a/vendor/google.golang.org/grpc/balancer_wrapper.go +++ b/vendor/google.golang.org/grpc/balancer_wrapper.go @@ -36,7 +36,6 @@ import ( ) var ( - setConnectedAddress = internal.SetConnectedAddress.(func(*balancer.SubConnState, resolver.Address)) // noOpRegisterHealthListenerFn is used when client side health checking is // disabled. It sends a single READY update on the registered listener. noOpRegisterHealthListenerFn = func(_ context.Context, listener func(balancer.SubConnState)) func() { @@ -305,7 +304,7 @@ func newHealthData(s connectivity.State) *healthData { // updateState is invoked by grpc to push a subConn state update to the // underlying balancer. -func (acbw *acBalancerWrapper) updateState(s connectivity.State, curAddr resolver.Address, err error) { +func (acbw *acBalancerWrapper) updateState(s connectivity.State, err error) { acbw.ccb.serializer.TrySchedule(func(ctx context.Context) { if ctx.Err() != nil || acbw.ccb.balancer == nil { return @@ -317,9 +316,6 @@ func (acbw *acBalancerWrapper) updateState(s connectivity.State, curAddr resolve // opts.StateListener is set, so this cannot ever be nil. // TODO: delete this comment when UpdateSubConnState is removed. scs := balancer.SubConnState{ConnectivityState: s, ConnectionError: err} - if s == connectivity.Ready { - setConnectedAddress(&scs, curAddr) - } // Invalidate the health listener by updating the healthData. acbw.healthMu.Lock() // A race may occur if a health listener is registered soon after the diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go index c0c2c9a76a..5dec2dacc0 100644 --- a/vendor/google.golang.org/grpc/clientconn.go +++ b/vendor/google.golang.org/grpc/clientconn.go @@ -35,6 +35,8 @@ import ( "google.golang.org/grpc/balancer/pickfirst" "google.golang.org/grpc/codes" "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/credentials" + expstats "google.golang.org/grpc/experimental/stats" "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/grpcsync" @@ -98,6 +100,41 @@ var ( errTransportCredentialsMissing = errors.New("grpc: the credentials require transport level security (use grpc.WithTransportCredentials() to set)") ) +var ( + disconnectionsMetric = expstats.RegisterInt64Count(expstats.MetricDescriptor{ + Name: "grpc.subchannel.disconnections", + Description: "EXPERIMENTAL. Number of times the selected subchannel becomes disconnected.", + Unit: "{disconnection}", + Labels: []string{"grpc.target"}, + OptionalLabels: []string{"grpc.lb.backend_service", "grpc.lb.locality", "grpc.disconnect_error"}, + Default: false, + }) + connectionAttemptsSucceededMetric = expstats.RegisterInt64Count(expstats.MetricDescriptor{ + Name: "grpc.subchannel.connection_attempts_succeeded", + Description: "EXPERIMENTAL. Number of successful connection attempts.", + Unit: "{attempt}", + Labels: []string{"grpc.target"}, + OptionalLabels: []string{"grpc.lb.backend_service", "grpc.lb.locality"}, + Default: false, + }) + connectionAttemptsFailedMetric = expstats.RegisterInt64Count(expstats.MetricDescriptor{ + Name: "grpc.subchannel.connection_attempts_failed", + Description: "EXPERIMENTAL. Number of failed connection attempts.", + Unit: "{attempt}", + Labels: []string{"grpc.target"}, + OptionalLabels: []string{"grpc.lb.backend_service", "grpc.lb.locality"}, + Default: false, + }) + openConnectionsMetric = expstats.RegisterInt64UpDownCount(expstats.MetricDescriptor{ + Name: "grpc.subchannel.open_connections", + Description: "EXPERIMENTAL. Number of open connections.", + Unit: "{attempt}", + Labels: []string{"grpc.target"}, + OptionalLabels: []string{"grpc.lb.backend_service", "grpc.security_level", "grpc.lb.locality"}, + Default: false, + }) +) + const ( defaultClientMaxReceiveMessageSize = 1024 * 1024 * 4 defaultClientMaxSendMessageSize = math.MaxInt32 @@ -262,9 +299,10 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn * }() // This creates the name resolver, load balancer, etc. - if err := cc.idlenessMgr.ExitIdleMode(); err != nil { - return nil, err + if err := cc.exitIdleMode(); err != nil { + return nil, fmt.Errorf("failed to exit idle mode: %w", err) } + cc.idlenessMgr.UnsafeSetNotIdle() // Return now for non-blocking dials. if !cc.dopts.block { @@ -332,7 +370,7 @@ func (cc *ClientConn) addTraceEvent(msg string) { Severity: channelz.CtInfo, } } - channelz.AddTraceEvent(logger, cc.channelz, 0, ted) + channelz.AddTraceEvent(logger, cc.channelz, 1, ted) } type idler ClientConn @@ -341,14 +379,17 @@ func (i *idler) EnterIdleMode() { (*ClientConn)(i).enterIdleMode() } -func (i *idler) ExitIdleMode() error { - return (*ClientConn)(i).exitIdleMode() +func (i *idler) ExitIdleMode() { + // Ignore the error returned from this method, because from the perspective + // of the caller (idleness manager), the channel would have always moved out + // of IDLE by the time this method returns. + (*ClientConn)(i).exitIdleMode() } // exitIdleMode moves the channel out of idle mode by recreating the name // resolver and load balancer. This should never be called directly; use // cc.idlenessMgr.ExitIdleMode instead. -func (cc *ClientConn) exitIdleMode() (err error) { +func (cc *ClientConn) exitIdleMode() error { cc.mu.Lock() if cc.conns == nil { cc.mu.Unlock() @@ -356,11 +397,23 @@ func (cc *ClientConn) exitIdleMode() (err error) { } cc.mu.Unlock() + // Set state to CONNECTING before building the name resolver + // so the channel does not remain in IDLE. + cc.csMgr.updateState(connectivity.Connecting) + // This needs to be called without cc.mu because this builds a new resolver // which might update state or report error inline, which would then need to // acquire cc.mu. if err := cc.resolverWrapper.start(); err != nil { - return err + // If resolver creation fails, treat it like an error reported by the + // resolver before any valid updates. Set channel's state to + // TransientFailure, and set an erroring picker with the resolver build + // error, which will returned as part of any subsequent RPCs. + logger.Warningf("Failed to start resolver: %v", err) + cc.csMgr.updateState(connectivity.TransientFailure) + cc.mu.Lock() + cc.updateResolverStateAndUnlock(resolver.State{}, err) + return fmt.Errorf("failed to start resolver: %w", err) } cc.addTraceEvent("exiting idle mode") @@ -681,10 +734,8 @@ func (cc *ClientConn) GetState() connectivity.State { // Notice: This API is EXPERIMENTAL and may be changed or removed in a later // release. func (cc *ClientConn) Connect() { - if err := cc.idlenessMgr.ExitIdleMode(); err != nil { - cc.addTraceEvent(err.Error()) - return - } + cc.idlenessMgr.ExitIdleMode() + // If the ClientConn was not in idle mode, we need to call ExitIdle on the // LB policy so that connections can be created. cc.mu.Lock() @@ -735,8 +786,8 @@ func init() { internal.EnterIdleModeForTesting = func(cc *ClientConn) { cc.idlenessMgr.EnterIdleModeForTesting() } - internal.ExitIdleModeForTesting = func(cc *ClientConn) error { - return cc.idlenessMgr.ExitIdleMode() + internal.ExitIdleModeForTesting = func(cc *ClientConn) { + cc.idlenessMgr.ExitIdleMode() } } @@ -861,6 +912,7 @@ func (cc *ClientConn) newAddrConnLocked(addrs []resolver.Address, opts balancer. channelz: channelz.RegisterSubChannel(cc.channelz, ""), resetBackoff: make(chan struct{}), } + ac.updateTelemetryLabelsLocked() ac.ctx, ac.cancel = context.WithCancel(cc.ctx) // Start with our address set to the first address; this may be updated if // we connect to different addresses. @@ -925,25 +977,24 @@ func (cc *ClientConn) incrCallsFailed() { // connect starts creating a transport. // It does nothing if the ac is not IDLE. // TODO(bar) Move this to the addrConn section. -func (ac *addrConn) connect() error { +func (ac *addrConn) connect() { ac.mu.Lock() if ac.state == connectivity.Shutdown { if logger.V(2) { logger.Infof("connect called on shutdown addrConn; ignoring.") } ac.mu.Unlock() - return errConnClosing + return } if ac.state != connectivity.Idle { if logger.V(2) { logger.Infof("connect called on addrConn in non-idle state (%v); ignoring.", ac.state) } ac.mu.Unlock() - return nil + return } ac.resetTransportAndUnlock() - return nil } // equalAddressIgnoringBalAttributes returns true is a and b are considered equal. @@ -977,7 +1028,7 @@ func (ac *addrConn) updateAddrs(addrs []resolver.Address) { } ac.addrs = addrs - + ac.updateTelemetryLabelsLocked() if ac.state == connectivity.Shutdown || ac.state == connectivity.TransientFailure || ac.state == connectivity.Idle { @@ -1216,6 +1267,9 @@ type addrConn struct { resetBackoff chan struct{} channelz *channelz.SubChannel + + localityLabel string + backendServiceLabel string } // Note: this requires a lock on ac.mu. @@ -1223,6 +1277,18 @@ func (ac *addrConn) updateConnectivityState(s connectivity.State, lastErr error) if ac.state == s { return } + + // If we are transitioning out of Ready, it means there is a disconnection. + // A SubConn can also transition from CONNECTING directly to IDLE when + // a transport is successfully created, but the connection fails + // before the SubConn can send the notification for READY. We treat + // this as a successful connection and transition to IDLE. + // TODO: https://github.com/grpc/grpc-go/issues/7862 - Remove the second + // part of the if condition below once the issue is fixed. + if ac.state == connectivity.Ready || (ac.state == connectivity.Connecting && s == connectivity.Idle) { + disconnectionsMetric.Record(ac.cc.metricsRecorderList, 1, ac.cc.target, ac.backendServiceLabel, ac.localityLabel, "unknown") + openConnectionsMetric.Record(ac.cc.metricsRecorderList, -1, ac.cc.target, ac.backendServiceLabel, ac.securityLevelLocked(), ac.localityLabel) + } ac.state = s ac.channelz.ChannelMetrics.State.Store(&s) if lastErr == nil { @@ -1230,7 +1296,7 @@ func (ac *addrConn) updateConnectivityState(s connectivity.State, lastErr error) } else { channelz.Infof(logger, ac.channelz, "Subchannel Connectivity change to %v, last error: %s", s, lastErr) } - ac.acbw.updateState(s, ac.curAddr, lastErr) + ac.acbw.updateState(s, lastErr) } // adjustParams updates parameters used to create transports upon @@ -1280,6 +1346,15 @@ func (ac *addrConn) resetTransportAndUnlock() { ac.mu.Unlock() if err := ac.tryAllAddrs(acCtx, addrs, connectDeadline); err != nil { + if !errors.Is(err, context.Canceled) { + connectionAttemptsFailedMetric.Record(ac.cc.metricsRecorderList, 1, ac.cc.target, ac.backendServiceLabel, ac.localityLabel) + } else { + if logger.V(2) { + // This records cancelled connection attempts which can be later + // replaced by a metric. + logger.Infof("Context cancellation detected; not recording this as a failed connection attempt.") + } + } // TODO: #7534 - Move re-resolution requests into the pick_first LB policy // to ensure one resolution request per pass instead of per subconn failure. ac.cc.resolveNow(resolver.ResolveNowOptions{}) @@ -1319,10 +1394,50 @@ func (ac *addrConn) resetTransportAndUnlock() { } // Success; reset backoff. ac.mu.Lock() + connectionAttemptsSucceededMetric.Record(ac.cc.metricsRecorderList, 1, ac.cc.target, ac.backendServiceLabel, ac.localityLabel) + openConnectionsMetric.Record(ac.cc.metricsRecorderList, 1, ac.cc.target, ac.backendServiceLabel, ac.securityLevelLocked(), ac.localityLabel) ac.backoffIdx = 0 ac.mu.Unlock() } +// updateTelemetryLabelsLocked calculates and caches the telemetry labels based on the +// first address in addrConn. +func (ac *addrConn) updateTelemetryLabelsLocked() { + labelsFunc, ok := internal.AddressToTelemetryLabels.(func(resolver.Address) map[string]string) + if !ok || len(ac.addrs) == 0 { + // Reset defaults + ac.localityLabel = "" + ac.backendServiceLabel = "" + return + } + labels := labelsFunc(ac.addrs[0]) + ac.localityLabel = labels["grpc.lb.locality"] + ac.backendServiceLabel = labels["grpc.lb.backend_service"] +} + +type securityLevelKey struct{} + +func (ac *addrConn) securityLevelLocked() string { + var secLevel string + // During disconnection, ac.transport is nil. Fall back to the security level + // stored in the current address during connection. + if ac.transport == nil { + secLevel, _ = ac.curAddr.Attributes.Value(securityLevelKey{}).(string) + return secLevel + } + authInfo := ac.transport.Peer().AuthInfo + if ci, ok := authInfo.(interface { + GetCommonAuthInfo() credentials.CommonAuthInfo + }); ok { + secLevel = ci.GetCommonAuthInfo().SecurityLevel.String() + // Store the security level in the current address' attributes so + // that it remains available for disconnection metrics after the + // transport is closed. + ac.curAddr.Attributes = ac.curAddr.Attributes.WithValue(securityLevelKey{}, secLevel) + } + return secLevel +} + // tryAllAddrs tries to create a connection to the addresses, and stop when at // the first successful one. It returns an error if no address was successfully // connected, or updates ac appropriately with the new transport. @@ -1412,25 +1527,26 @@ func (ac *addrConn) createTransport(ctx context.Context, addr resolver.Address, } ac.mu.Lock() - defer ac.mu.Unlock() if ctx.Err() != nil { // This can happen if the subConn was removed while in `Connecting` // state. tearDown() would have set the state to `Shutdown`, but // would not have closed the transport since ac.transport would not // have been set at that point. - // - // We run this in a goroutine because newTr.Close() calls onClose() + + // We unlock ac.mu because newTr.Close() calls onClose() // inline, which requires locking ac.mu. - // + ac.mu.Unlock() + // The error we pass to Close() is immaterial since there are no open // streams at this point, so no trailers with error details will be sent // out. We just need to pass a non-nil error. // // This can also happen when updateAddrs is called during a connection // attempt. - go newTr.Close(transport.ErrConnClosing) + newTr.Close(transport.ErrConnClosing) return nil } + defer ac.mu.Unlock() if hctx.Err() != nil { // onClose was already called for this connection, but the connection // was successfully established first. Consider it a success and set diff --git a/vendor/google.golang.org/grpc/credentials/tls.go b/vendor/google.golang.org/grpc/credentials/tls.go index 8277be7d6f..0bcd16dbbf 100644 --- a/vendor/google.golang.org/grpc/credentials/tls.go +++ b/vendor/google.golang.org/grpc/credentials/tls.go @@ -56,9 +56,13 @@ func (t TLSInfo) AuthType() string { // non-nil error if the validation fails. func (t TLSInfo) ValidateAuthority(authority string) error { var errs []error + host, _, err := net.SplitHostPort(authority) + if err != nil { + host = authority + } for _, cert := range t.State.PeerCertificates { var err error - if err = cert.VerifyHostname(authority); err == nil { + if err = cert.VerifyHostname(host); err == nil { return nil } errs = append(errs, err) diff --git a/vendor/google.golang.org/grpc/encoding/encoding.go b/vendor/google.golang.org/grpc/encoding/encoding.go index dadd21e40f..296f38c3a8 100644 --- a/vendor/google.golang.org/grpc/encoding/encoding.go +++ b/vendor/google.golang.org/grpc/encoding/encoding.go @@ -58,10 +58,6 @@ func init() { // Compressor is used for compressing and decompressing when sending or // receiving messages. -// -// If a Compressor implements `DecompressedSize(compressedBytes []byte) int`, -// gRPC will invoke it to determine the size of the buffer allocated for the -// result of decompression. A return value of -1 indicates unknown size. type Compressor interface { // Compress writes the data written to wc to w after compressing it. If an // error occurs while initializing the compressor, that error is returned diff --git a/vendor/google.golang.org/grpc/encoding/gzip/gzip.go b/vendor/google.golang.org/grpc/encoding/gzip/gzip.go index 6306e8bb0f..153e4dbfbf 100644 --- a/vendor/google.golang.org/grpc/encoding/gzip/gzip.go +++ b/vendor/google.golang.org/grpc/encoding/gzip/gzip.go @@ -27,7 +27,6 @@ package gzip import ( "compress/gzip" - "encoding/binary" "fmt" "io" "sync" @@ -111,17 +110,6 @@ func (z *reader) Read(p []byte) (n int, err error) { return n, err } -// RFC1952 specifies that the last four bytes "contains the size of -// the original (uncompressed) input data modulo 2^32." -// gRPC has a max message size of 2GB so we don't need to worry about wraparound. -func (c *compressor) DecompressedSize(buf []byte) int { - last := len(buf) - if last < 4 { - return -1 - } - return int(binary.LittleEndian.Uint32(buf[last-4 : last])) -} - func (c *compressor) Name() string { return Name } diff --git a/vendor/google.golang.org/grpc/experimental/stats/metricregistry.go b/vendor/google.golang.org/grpc/experimental/stats/metricregistry.go index 2b57ba65a3..472813f58f 100644 --- a/vendor/google.golang.org/grpc/experimental/stats/metricregistry.go +++ b/vendor/google.golang.org/grpc/experimental/stats/metricregistry.go @@ -76,6 +76,7 @@ const ( MetricTypeFloatHisto MetricTypeIntGauge MetricTypeIntUpDownCount + MetricTypeIntAsyncGauge ) // Int64CountHandle is a typed handle for a int count metric. This handle @@ -172,6 +173,30 @@ func (h *Int64GaugeHandle) Record(recorder MetricsRecorder, incr int64, labels . recorder.RecordInt64Gauge(h, incr, labels...) } +// AsyncMetric is a marker interface for asynchronous metric types. +type AsyncMetric interface { + isAsync() + Descriptor() *MetricDescriptor +} + +// Int64AsyncGaugeHandle is a typed handle for an int gauge metric. This handle is +// passed at the recording point in order to know which metric to record on. +type Int64AsyncGaugeHandle MetricDescriptor + +// isAsync implements the AsyncMetric interface. +func (h *Int64AsyncGaugeHandle) isAsync() {} + +// Descriptor returns the int64 gauge handle typecast to a pointer to a +// MetricDescriptor. +func (h *Int64AsyncGaugeHandle) Descriptor() *MetricDescriptor { + return (*MetricDescriptor)(h) +} + +// Record records the int64 gauge value on the metrics recorder provided. +func (h *Int64AsyncGaugeHandle) Record(recorder AsyncMetricsRecorder, value int64, labels ...string) { + recorder.RecordInt64AsyncGauge(h, value, labels...) +} + // registeredMetrics are the registered metric descriptor names. var registeredMetrics = make(map[string]bool) @@ -282,6 +307,20 @@ func RegisterInt64UpDownCount(descriptor MetricDescriptor) *Int64UpDownCountHand return (*Int64UpDownCountHandle)(descPtr) } +// RegisterInt64AsyncGauge registers the metric description onto the global registry. +// It returns a typed handle to use for recording data. +// +// NOTE: this function must only be called during initialization time (i.e. in +// an init() function), and is not thread-safe. If multiple metrics are +// registered with the same name, this function will panic. +func RegisterInt64AsyncGauge(descriptor MetricDescriptor) *Int64AsyncGaugeHandle { + registerMetric(descriptor.Name, descriptor.Default) + descriptor.Type = MetricTypeIntAsyncGauge + descPtr := &descriptor + metricsRegistry[descriptor.Name] = descPtr + return (*Int64AsyncGaugeHandle)(descPtr) +} + // snapshotMetricsRegistryForTesting snapshots the global data of the metrics // registry. Returns a cleanup function that sets the metrics registry to its // original state. diff --git a/vendor/google.golang.org/grpc/experimental/stats/metrics.go b/vendor/google.golang.org/grpc/experimental/stats/metrics.go index cb57f1a748..88742724a4 100644 --- a/vendor/google.golang.org/grpc/experimental/stats/metrics.go +++ b/vendor/google.golang.org/grpc/experimental/stats/metrics.go @@ -19,9 +19,13 @@ // Package stats contains experimental metrics/stats API's. package stats -import "google.golang.org/grpc/stats" +import ( + "google.golang.org/grpc/internal" + "google.golang.org/grpc/stats" +) // MetricsRecorder records on metrics derived from metric registry. +// Implementors must embed UnimplementedMetricsRecorder. type MetricsRecorder interface { // RecordInt64Count records the measurement alongside labels on the int // count associated with the provided handle. @@ -41,6 +45,46 @@ type MetricsRecorder interface { // RecordInt64UpDownCounter records the measurement alongside labels on the int // count associated with the provided handle. RecordInt64UpDownCount(handle *Int64UpDownCountHandle, incr int64, labels ...string) + // RegisterAsyncReporter registers a reporter to produce metric values for + // only the listed descriptors. The returned function must be called when + // the metrics are no longer needed, which will remove the reporter. The + // returned method needs to be idempotent and concurrent safe. + RegisterAsyncReporter(reporter AsyncMetricReporter, descriptors ...AsyncMetric) func() + + // EnforceMetricsRecorderEmbedding is included to force implementers to embed + // another implementation of this interface, allowing gRPC to add methods + // without breaking users. + internal.EnforceMetricsRecorderEmbedding +} + +// AsyncMetricReporter is an interface for types that record metrics asynchronously +// for the set of descriptors they are registered with. The AsyncMetricsRecorder +// parameter is used to record values for these metrics. +// +// Implementations must make unique recordings across all registered +// AsyncMetricReporters. Meaning, they should not report values for a metric with +// the same attributes as another AsyncMetricReporter will report. +// +// Implementations must be concurrent-safe. +type AsyncMetricReporter interface { + // Report records metric values using the provided recorder. + Report(AsyncMetricsRecorder) error +} + +// AsyncMetricReporterFunc is an adapter to allow the use of ordinary functions as +// AsyncMetricReporters. +type AsyncMetricReporterFunc func(AsyncMetricsRecorder) error + +// Report calls f(r). +func (f AsyncMetricReporterFunc) Report(r AsyncMetricsRecorder) error { + return f(r) +} + +// AsyncMetricsRecorder records on asynchronous metrics derived from metric registry. +type AsyncMetricsRecorder interface { + // RecordInt64AsyncGauge records the measurement alongside labels on the int + // count associated with the provided handle asynchronously + RecordInt64AsyncGauge(handle *Int64AsyncGaugeHandle, incr int64, labels ...string) } // Metrics is an experimental legacy alias of the now-stable stats.MetricSet. @@ -55,3 +99,33 @@ type Metric = string func NewMetrics(metrics ...Metric) *Metrics { return stats.NewMetricSet(metrics...) } + +// UnimplementedMetricsRecorder must be embedded to have forward compatible implementations. +type UnimplementedMetricsRecorder struct { + internal.EnforceMetricsRecorderEmbedding +} + +// RecordInt64Count provides a no-op implementation. +func (UnimplementedMetricsRecorder) RecordInt64Count(*Int64CountHandle, int64, ...string) {} + +// RecordFloat64Count provides a no-op implementation. +func (UnimplementedMetricsRecorder) RecordFloat64Count(*Float64CountHandle, float64, ...string) {} + +// RecordInt64Histo provides a no-op implementation. +func (UnimplementedMetricsRecorder) RecordInt64Histo(*Int64HistoHandle, int64, ...string) {} + +// RecordFloat64Histo provides a no-op implementation. +func (UnimplementedMetricsRecorder) RecordFloat64Histo(*Float64HistoHandle, float64, ...string) {} + +// RecordInt64Gauge provides a no-op implementation. +func (UnimplementedMetricsRecorder) RecordInt64Gauge(*Int64GaugeHandle, int64, ...string) {} + +// RecordInt64UpDownCount provides a no-op implementation. +func (UnimplementedMetricsRecorder) RecordInt64UpDownCount(*Int64UpDownCountHandle, int64, ...string) { +} + +// RegisterAsyncReporter provides a no-op implementation. +func (UnimplementedMetricsRecorder) RegisterAsyncReporter(AsyncMetricReporter, ...AsyncMetric) func() { + // No-op: Return an empty function to ensure caller doesn't panic on nil function call + return func() {} +} diff --git a/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go b/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go index f2c01f296a..e99cd5c838 100644 --- a/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go +++ b/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.5.1 +// - protoc-gen-go-grpc v1.6.0 // - protoc v5.27.1 // source: grpc/health/v1/health.proto diff --git a/vendor/google.golang.org/grpc/interceptor.go b/vendor/google.golang.org/grpc/interceptor.go index 877d78fc3d..099e3d0933 100644 --- a/vendor/google.golang.org/grpc/interceptor.go +++ b/vendor/google.golang.org/grpc/interceptor.go @@ -97,8 +97,12 @@ type StreamServerInfo struct { IsServerStream bool } -// StreamServerInterceptor provides a hook to intercept the execution of a streaming RPC on the server. -// info contains all the information of this RPC the interceptor can operate on. And handler is the -// service method implementation. It is the responsibility of the interceptor to invoke handler to -// complete the RPC. +// StreamServerInterceptor provides a hook to intercept the execution of a +// streaming RPC on the server. +// +// srv is the service implementation on which the RPC was invoked, and needs to +// be passed to handler, and not used otherwise. ss is the server side of the +// stream. info contains all the information of this RPC the interceptor can +// operate on. And handler is the service method implementation. It is the +// responsibility of the interceptor to invoke handler to complete the RPC. type StreamServerInterceptor func(srv any, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error diff --git a/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go b/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go index ba25b89887..f38de74a49 100644 --- a/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go +++ b/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go @@ -67,6 +67,10 @@ type Balancer struct { // balancerCurrent before the UpdateSubConnState is called on the // balancerCurrent. currentMu sync.Mutex + + // activeGoroutines tracks all the goroutines that this balancer has started + // and that should be waited on when the balancer closes. + activeGoroutines sync.WaitGroup } // swap swaps out the current lb with the pending lb and updates the ClientConn. @@ -76,7 +80,9 @@ func (gsb *Balancer) swap() { cur := gsb.balancerCurrent gsb.balancerCurrent = gsb.balancerPending gsb.balancerPending = nil + gsb.activeGoroutines.Add(1) go func() { + defer gsb.activeGoroutines.Done() gsb.currentMu.Lock() defer gsb.currentMu.Unlock() cur.Close() @@ -274,6 +280,7 @@ func (gsb *Balancer) Close() { currentBalancerToClose.Close() pendingBalancerToClose.Close() + gsb.activeGoroutines.Wait() } // balancerWrapper wraps a balancer.Balancer, and overrides some Balancer @@ -324,7 +331,12 @@ func (bw *balancerWrapper) UpdateState(state balancer.State) { defer bw.gsb.mu.Unlock() bw.lastState = state + // If Close() acquires the mutex before UpdateState(), the balancer + // will already have been removed from the current or pending state when + // reaching this point. if !bw.gsb.balancerCurrentOrPending(bw) { + // Returning here ensures that (*Balancer).swap() is not invoked after + // (*Balancer).Close() and therefore prevents "use after close". return } diff --git a/vendor/google.golang.org/grpc/internal/balancer/weight/weight.go b/vendor/google.golang.org/grpc/internal/balancer/weight/weight.go new file mode 100644 index 0000000000..11beb07d14 --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/balancer/weight/weight.go @@ -0,0 +1,66 @@ +/* + * + * Copyright 2025 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Package weight contains utilities to manage endpoint weights. Weights are +// used by LB policies such as ringhash to distribute load across multiple +// endpoints. +package weight + +import ( + "fmt" + + "google.golang.org/grpc/resolver" +) + +// attributeKey is the type used as the key to store EndpointInfo in the +// Attributes field of resolver.Endpoint. +type attributeKey struct{} + +// EndpointInfo will be stored in the Attributes field of Endpoints in order to +// use the ringhash balancer. +type EndpointInfo struct { + Weight uint32 +} + +// Equal allows the values to be compared by Attributes.Equal. +func (a EndpointInfo) Equal(o any) bool { + oa, ok := o.(EndpointInfo) + return ok && oa.Weight == a.Weight +} + +// Set returns a copy of endpoint in which the Attributes field is updated with +// EndpointInfo. +func Set(endpoint resolver.Endpoint, epInfo EndpointInfo) resolver.Endpoint { + endpoint.Attributes = endpoint.Attributes.WithValue(attributeKey{}, epInfo) + return endpoint +} + +// String returns a human-readable representation of EndpointInfo. +// This method is intended for logging, testing, and debugging purposes only. +// Do not rely on the output format, as it is not guaranteed to remain stable. +func (a EndpointInfo) String() string { + return fmt.Sprintf("Weight: %d", a.Weight) +} + +// FromEndpoint returns the EndpointInfo stored in the Attributes field of an +// endpoint. It returns an empty EndpointInfo if attribute is not found. +func FromEndpoint(endpoint resolver.Endpoint) EndpointInfo { + v := endpoint.Attributes.Value(attributeKey{}) + ei, _ := v.(EndpointInfo) + return ei +} diff --git a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go index 91f760936c..7ad6fb44ca 100644 --- a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go +++ b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go @@ -77,6 +77,33 @@ var ( // - Target resolution is disabled. // - The DNS resolver is being used. EnableDefaultPortForProxyTarget = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_DEFAULT_PORT_FOR_PROXY_TARGET", true) + + // XDSAuthorityRewrite indicates whether xDS authority rewriting is enabled. + // This feature is defined in gRFC A81 and is enabled by setting the + // environment variable GRPC_EXPERIMENTAL_XDS_AUTHORITY_REWRITE to "true". + XDSAuthorityRewrite = boolFromEnv("GRPC_EXPERIMENTAL_XDS_AUTHORITY_REWRITE", false) + + // PickFirstWeightedShuffling indicates whether weighted endpoint shuffling + // is enabled in the pick_first LB policy, as defined in gRFC A113. This + // feature can be disabled by setting the environment variable + // GRPC_EXPERIMENTAL_PF_WEIGHTED_SHUFFLING to "false". + PickFirstWeightedShuffling = boolFromEnv("GRPC_EXPERIMENTAL_PF_WEIGHTED_SHUFFLING", true) + + // DisableStrictPathChecking indicates whether strict path checking is + // disabled. This feature can be disabled by setting the environment + // variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to "true". + // + // When strict path checking is enabled, gRPC will reject requests with + // paths that do not conform to the gRPC over HTTP/2 specification found at + // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md. + // + // When disabled, gRPC will allow paths that do not contain a leading slash. + // Enabling strict path checking is recommended for security reasons, as it + // prevents potential path traversal vulnerabilities. + // + // A future release will remove this environment variable, enabling strict + // path checking behavior unconditionally. + DisableStrictPathChecking = boolFromEnv("GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING", false) ) func boolFromEnv(envVar string, def bool) bool { diff --git a/vendor/google.golang.org/grpc/internal/experimental.go b/vendor/google.golang.org/grpc/internal/experimental.go index 7617be2158..8a999917d9 100644 --- a/vendor/google.golang.org/grpc/internal/experimental.go +++ b/vendor/google.golang.org/grpc/internal/experimental.go @@ -25,4 +25,11 @@ var ( // BufferPool is implemented by the grpc package and returns a server // option to configure a shared buffer pool for a grpc.Server. BufferPool any // func (grpc.SharedBufferPool) grpc.ServerOption + + // SetDefaultBufferPool updates the default buffer pool. + SetDefaultBufferPool any // func(mem.BufferPool) + + // AcceptCompressors is implemented by the grpc package and returns + // a call option that restricts the grpc-accept-encoding header for a call. + AcceptCompressors any // func(...string) grpc.CallOption ) diff --git a/vendor/google.golang.org/grpc/internal/idle/idle.go b/vendor/google.golang.org/grpc/internal/idle/idle.go index 2c13ee9dac..d3cd24f80b 100644 --- a/vendor/google.golang.org/grpc/internal/idle/idle.go +++ b/vendor/google.golang.org/grpc/internal/idle/idle.go @@ -21,7 +21,6 @@ package idle import ( - "fmt" "math" "sync" "sync/atomic" @@ -33,15 +32,15 @@ var timeAfterFunc = func(d time.Duration, f func()) *time.Timer { return time.AfterFunc(d, f) } -// Enforcer is the functionality provided by grpc.ClientConn to enter -// and exit from idle mode. -type Enforcer interface { - ExitIdleMode() error +// ClientConn is the functionality provided by grpc.ClientConn to enter and exit +// from idle mode. +type ClientConn interface { + ExitIdleMode() EnterIdleMode() } -// Manager implements idleness detection and calls the configured Enforcer to -// enter/exit idle mode when appropriate. Must be created by NewManager. +// Manager implements idleness detection and calls the ClientConn to enter/exit +// idle mode when appropriate. Must be created by NewManager. type Manager struct { // State accessed atomically. lastCallEndTime int64 // Unix timestamp in nanos; time when the most recent RPC completed. @@ -51,8 +50,8 @@ type Manager struct { // Can be accessed without atomics or mutex since these are set at creation // time and read-only after that. - enforcer Enforcer // Functionality provided by grpc.ClientConn. - timeout time.Duration + cc ClientConn // Functionality provided by grpc.ClientConn. + timeout time.Duration // idleMu is used to guarantee mutual exclusion in two scenarios: // - Opposing intentions: @@ -72,9 +71,9 @@ type Manager struct { // NewManager creates a new idleness manager implementation for the // given idle timeout. It begins in idle mode. -func NewManager(enforcer Enforcer, timeout time.Duration) *Manager { +func NewManager(cc ClientConn, timeout time.Duration) *Manager { return &Manager{ - enforcer: enforcer, + cc: cc, timeout: timeout, actuallyIdle: true, activeCallsCount: -math.MaxInt32, @@ -127,7 +126,7 @@ func (m *Manager) handleIdleTimeout() { // Now that we've checked that there has been no activity, attempt to enter // idle mode, which is very likely to succeed. - if m.tryEnterIdleMode() { + if m.tryEnterIdleMode(true) { // Successfully entered idle mode. No timer needed until we exit idle. return } @@ -142,10 +141,13 @@ func (m *Manager) handleIdleTimeout() { // that, it performs a last minute check to ensure that no new RPC has come in, // making the channel active. // +// checkActivity controls if a check for RPC activity, since the last time the +// idle_timeout fired, is made. + // Return value indicates whether or not the channel moved to idle mode. // // Holds idleMu which ensures mutual exclusion with exitIdleMode. -func (m *Manager) tryEnterIdleMode() bool { +func (m *Manager) tryEnterIdleMode(checkActivity bool) bool { // Setting the activeCallsCount to -math.MaxInt32 indicates to OnCallBegin() // that the channel is either in idle mode or is trying to get there. if !atomic.CompareAndSwapInt32(&m.activeCallsCount, 0, -math.MaxInt32) { @@ -166,7 +168,7 @@ func (m *Manager) tryEnterIdleMode() bool { atomic.AddInt32(&m.activeCallsCount, math.MaxInt32) return false } - if atomic.LoadInt32(&m.activeSinceLastTimerCheck) == 1 { + if checkActivity && atomic.LoadInt32(&m.activeSinceLastTimerCheck) == 1 { // A very short RPC could have come in (and also finished) after we // checked for calls count and activity in handleIdleTimeout(), but // before the CAS operation. So, we need to check for activity again. @@ -177,44 +179,37 @@ func (m *Manager) tryEnterIdleMode() bool { // No new RPCs have come in since we set the active calls count value to // -math.MaxInt32. And since we have the lock, it is safe to enter idle mode // unconditionally now. - m.enforcer.EnterIdleMode() + m.cc.EnterIdleMode() m.actuallyIdle = true return true } // EnterIdleModeForTesting instructs the channel to enter idle mode. func (m *Manager) EnterIdleModeForTesting() { - m.tryEnterIdleMode() + m.tryEnterIdleMode(false) } // OnCallBegin is invoked at the start of every RPC. -func (m *Manager) OnCallBegin() error { +func (m *Manager) OnCallBegin() { if m.isClosed() { - return nil + return } if atomic.AddInt32(&m.activeCallsCount, 1) > 0 { // Channel is not idle now. Set the activity bit and allow the call. atomic.StoreInt32(&m.activeSinceLastTimerCheck, 1) - return nil + return } // Channel is either in idle mode or is in the process of moving to idle // mode. Attempt to exit idle mode to allow this RPC. - if err := m.ExitIdleMode(); err != nil { - // Undo the increment to calls count, and return an error causing the - // RPC to fail. - atomic.AddInt32(&m.activeCallsCount, -1) - return err - } - + m.ExitIdleMode() atomic.StoreInt32(&m.activeSinceLastTimerCheck, 1) - return nil } -// ExitIdleMode instructs m to call the enforcer's ExitIdleMode and update m's +// ExitIdleMode instructs m to call the ClientConn's ExitIdleMode and update its // internal state. -func (m *Manager) ExitIdleMode() error { +func (m *Manager) ExitIdleMode() { // Holds idleMu which ensures mutual exclusion with tryEnterIdleMode. m.idleMu.Lock() defer m.idleMu.Unlock() @@ -231,12 +226,10 @@ func (m *Manager) ExitIdleMode() error { // m.ExitIdleMode. // // In any case, there is nothing to do here. - return nil + return } - if err := m.enforcer.ExitIdleMode(); err != nil { - return fmt.Errorf("failed to exit idle mode: %w", err) - } + m.cc.ExitIdleMode() // Undo the idle entry process. This also respects any new RPC attempts. atomic.AddInt32(&m.activeCallsCount, math.MaxInt32) @@ -244,7 +237,23 @@ func (m *Manager) ExitIdleMode() error { // Start a new timer to fire after the configured idle timeout. m.resetIdleTimerLocked(m.timeout) - return nil +} + +// UnsafeSetNotIdle instructs the Manager to update its internal state to +// reflect the reality that the channel is no longer in IDLE mode. +// +// N.B. This method is intended only for internal use by the gRPC client +// when it exits IDLE mode **manually** from `Dial`. The callsite must ensure: +// - The channel was **actually in IDLE mode** immediately prior to the call. +// - There is **no concurrent activity** that could cause the channel to exit +// IDLE mode *naturally* at the same time. +func (m *Manager) UnsafeSetNotIdle() { + m.idleMu.Lock() + defer m.idleMu.Unlock() + + atomic.AddInt32(&m.activeCallsCount, math.MaxInt32) + m.actuallyIdle = false + m.resetIdleTimerLocked(m.timeout) } // OnCallEnd is invoked at the end of every RPC. diff --git a/vendor/google.golang.org/grpc/internal/internal.go b/vendor/google.golang.org/grpc/internal/internal.go index 2699223a27..4b3d563f8d 100644 --- a/vendor/google.golang.org/grpc/internal/internal.go +++ b/vendor/google.golang.org/grpc/internal/internal.go @@ -211,22 +211,11 @@ var ( // default resolver scheme. UserSetDefaultScheme = false - // ConnectedAddress returns the connected address for a SubConnState. The - // address is only valid if the state is READY. - ConnectedAddress any // func (scs SubConnState) resolver.Address - - // SetConnectedAddress sets the connected address for a SubConnState. - SetConnectedAddress any // func(scs *SubConnState, addr resolver.Address) - // SnapshotMetricRegistryForTesting snapshots the global data of the metric // registry. Returns a cleanup function that sets the metric registry to its // original state. Only called in testing functions. SnapshotMetricRegistryForTesting func() func() - // SetDefaultBufferPoolForTesting updates the default buffer pool, for - // testing purposes. - SetDefaultBufferPoolForTesting any // func(mem.BufferPool) - // SetBufferPoolingThresholdForTesting updates the buffer pooling threshold, for // testing purposes. SetBufferPoolingThresholdForTesting any // func(int) @@ -244,6 +233,18 @@ var ( // When set, the function will be called before the stream enters // the blocking state. NewStreamWaitingForResolver = func() {} + + // AddressToTelemetryLabels is an xDS-provided function to extract telemetry + // labels from a resolver.Address. Callers must assert its type before calling. + AddressToTelemetryLabels any // func(addr resolver.Address) map[string]string + + // AsyncReporterCleanupDelegate is initialized to a pass-through function by + // default (production behavior), allowing tests to swap it with an + // implementation which tracks registration of async reporter and its + // corresponding cleanup. + AsyncReporterCleanupDelegate = func(cleanup func()) func() { + return cleanup + } ) // HealthChecker defines the signature of the client-side LB channel health @@ -291,3 +292,9 @@ type EnforceClientConnEmbedding interface { type Timer interface { Stop() bool } + +// EnforceMetricsRecorderEmbedding is used to enforce proper MetricsRecorder +// implementation embedding. +type EnforceMetricsRecorderEmbedding interface { + enforceMetricsRecorderEmbedding() +} diff --git a/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go b/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go index ada5251cff..70b89e4d7f 100644 --- a/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go +++ b/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go @@ -125,7 +125,10 @@ func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts // IP address. if ipAddr, err := formatIP(host); err == nil { addr := []resolver.Address{{Addr: ipAddr + ":" + port}} - cc.UpdateState(resolver.State{Addresses: addr}) + cc.UpdateState(resolver.State{ + Addresses: addr, + Endpoints: []resolver.Endpoint{{Addresses: addr}}, + }) return deadResolver{}, nil } @@ -342,7 +345,15 @@ func (d *dnsResolver) lookup() (*resolver.State, error) { return nil, hostErr } - state := resolver.State{Addresses: addrs} + eps := make([]resolver.Endpoint, 0, len(addrs)) + for _, addr := range addrs { + eps = append(eps, resolver.Endpoint{Addresses: []resolver.Address{addr}}) + } + + state := resolver.State{ + Addresses: addrs, + Endpoints: eps, + } if len(srv) > 0 { state = grpclbstate.Set(state, &grpclbstate.State{BalancerAddresses: srv}) } diff --git a/vendor/google.golang.org/grpc/internal/stats/metrics_recorder_list.go b/vendor/google.golang.org/grpc/internal/stats/metrics_recorder_list.go index d5f7e4d62d..1c8c2ab303 100644 --- a/vendor/google.golang.org/grpc/internal/stats/metrics_recorder_list.go +++ b/vendor/google.golang.org/grpc/internal/stats/metrics_recorder_list.go @@ -20,6 +20,7 @@ import ( "fmt" estats "google.golang.org/grpc/experimental/stats" + "google.golang.org/grpc/internal" "google.golang.org/grpc/stats" ) @@ -28,6 +29,7 @@ import ( // It eats any record calls where the label values provided do not match the // number of label keys. type MetricsRecorderList struct { + internal.EnforceMetricsRecorderEmbedding // metricsRecorders are the metrics recorders this list will forward to. metricsRecorders []estats.MetricsRecorder } @@ -113,3 +115,61 @@ func (l *MetricsRecorderList) RecordInt64Gauge(handle *estats.Int64GaugeHandle, metricRecorder.RecordInt64Gauge(handle, incr, labels...) } } + +// RegisterAsyncReporter forwards the registration to all underlying metrics +// recorders. +// +// It returns a cleanup function that, when called, invokes the cleanup function +// returned by each underlying recorder, ensuring the reporter is unregistered +// from all of them. +func (l *MetricsRecorderList) RegisterAsyncReporter(reporter estats.AsyncMetricReporter, metrics ...estats.AsyncMetric) func() { + descriptorsMap := make(map[*estats.MetricDescriptor]bool, len(metrics)) + for _, m := range metrics { + descriptorsMap[m.Descriptor()] = true + } + unregisterFns := make([]func(), 0, len(l.metricsRecorders)) + for _, mr := range l.metricsRecorders { + // Wrap the AsyncMetricsRecorder to intercept calls to RecordInt64Gauge + // and validate the labels. + wrappedCallback := func(recorder estats.AsyncMetricsRecorder) error { + wrappedRecorder := &asyncRecorderWrapper{ + delegate: recorder, + descriptors: descriptorsMap, + } + return reporter.Report(wrappedRecorder) + } + unregisterFns = append(unregisterFns, mr.RegisterAsyncReporter(estats.AsyncMetricReporterFunc(wrappedCallback), metrics...)) + } + + // Wrap the cleanup function using the internal delegate. + // In production, this returns realCleanup as-is. + // In tests, the leak checker can swap this to track the registration lifetime. + return internal.AsyncReporterCleanupDelegate(defaultCleanUp(unregisterFns)) +} + +func defaultCleanUp(unregisterFns []func()) func() { + return func() { + for _, unregister := range unregisterFns { + unregister() + } + } +} + +type asyncRecorderWrapper struct { + delegate estats.AsyncMetricsRecorder + descriptors map[*estats.MetricDescriptor]bool +} + +// RecordIntAsync64Gauge records the measurement alongside labels on the int +// gauge associated with the provided handle. +func (w *asyncRecorderWrapper) RecordInt64AsyncGauge(handle *estats.Int64AsyncGaugeHandle, value int64, labels ...string) { + // Ensure only metrics for descriptors passed during callback registration + // are emitted. + d := handle.Descriptor() + if _, ok := w.descriptors[d]; !ok { + return + } + // Validate labels and delegate. + verifyLabels(d, labels...) + w.delegate.RecordInt64AsyncGauge(handle, value, labels...) +} diff --git a/vendor/google.golang.org/grpc/internal/transport/client_stream.go b/vendor/google.golang.org/grpc/internal/transport/client_stream.go index 980452519e..cd8152ef13 100644 --- a/vendor/google.golang.org/grpc/internal/transport/client_stream.go +++ b/vendor/google.golang.org/grpc/internal/transport/client_stream.go @@ -24,6 +24,7 @@ import ( "golang.org/x/net/http2" "google.golang.org/grpc/mem" "google.golang.org/grpc/metadata" + "google.golang.org/grpc/stats" "google.golang.org/grpc/status" ) @@ -46,10 +47,11 @@ type ClientStream struct { // meaningful after headerChan is closed (always call waitOnHeader() before // reading its value). headerValid bool - noHeaders bool // set if the client never received headers (set only after the stream is done). - headerChanClosed uint32 // set when headerChan is closed. Used to avoid closing headerChan multiple times. - bytesReceived atomic.Bool // indicates whether any bytes have been received on this stream - unprocessed atomic.Bool // set if the server sends a refused stream or GOAWAY including this stream + noHeaders bool // set if the client never received headers (set only after the stream is done). + headerChanClosed uint32 // set when headerChan is closed. Used to avoid closing headerChan multiple times. + bytesReceived atomic.Bool // indicates whether any bytes have been received on this stream + unprocessed atomic.Bool // set if the server sends a refused stream or GOAWAY including this stream + statsHandler stats.Handler // nil for internal streams (e.g., health check, ORCA) where telemetry is not supported. } // Read reads an n byte message from the input stream. diff --git a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go index 2dcd1e63bd..7efa524785 100644 --- a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go +++ b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go @@ -24,16 +24,13 @@ import ( "fmt" "net" "runtime" - "strconv" "sync" "sync/atomic" "golang.org/x/net/http2" "golang.org/x/net/http2/hpack" "google.golang.org/grpc/internal/grpclog" - "google.golang.org/grpc/internal/grpcutil" "google.golang.org/grpc/mem" - "google.golang.org/grpc/status" ) var updateHeaderTblSize = func(e *hpack.Encoder, v uint32) { @@ -147,11 +144,9 @@ type cleanupStream struct { func (c *cleanupStream) isTransportResponseFrame() bool { return c.rst } // Results in a RST_STREAM type earlyAbortStream struct { - httpStatus uint32 - streamID uint32 - contentSubtype string - status *status.Status - rst bool + streamID uint32 + rst bool + hf []hpack.HeaderField // Pre-built header fields } func (*earlyAbortStream) isTransportResponseFrame() bool { return false } @@ -843,18 +838,7 @@ func (l *loopyWriter) earlyAbortStreamHandler(eas *earlyAbortStream) error { if l.side == clientSide { return errors.New("earlyAbortStream not handled on client") } - // In case the caller forgets to set the http status, default to 200. - if eas.httpStatus == 0 { - eas.httpStatus = 200 - } - headerFields := []hpack.HeaderField{ - {Name: ":status", Value: strconv.Itoa(int(eas.httpStatus))}, - {Name: "content-type", Value: grpcutil.ContentType(eas.contentSubtype)}, - {Name: "grpc-status", Value: strconv.Itoa(int(eas.status.Code()))}, - {Name: "grpc-message", Value: encodeGrpcMessage(eas.status.Message())}, - } - - if err := l.writeHeader(eas.streamID, true, headerFields, nil); err != nil { + if err := l.writeHeader(eas.streamID, true, eas.hf, nil); err != nil { return err } if eas.rst { diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/vendor/google.golang.org/grpc/internal/transport/http2_client.go index 65b4ab2439..37b1acc340 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_client.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_client.go @@ -370,7 +370,7 @@ func NewHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts }) t.logger = prefixLoggerForClientTransport(t) // Add peer information to the http2client context. - t.ctx = peer.NewContext(t.ctx, t.getPeer()) + t.ctx = peer.NewContext(t.ctx, t.Peer()) if md, ok := addr.Metadata.(*metadata.MD); ok { t.md = *md @@ -478,7 +478,7 @@ func NewHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts return t, nil } -func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *ClientStream { +func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr, handler stats.Handler) *ClientStream { // TODO(zhaoq): Handle uint32 overflow of Stream.id. s := &ClientStream{ Stream: Stream{ @@ -486,10 +486,11 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *ClientSt sendCompress: callHdr.SendCompress, contentSubtype: callHdr.ContentSubtype, }, - ct: t, - done: make(chan struct{}), - headerChan: make(chan struct{}), - doneFunc: callHdr.DoneFunc, + ct: t, + done: make(chan struct{}), + headerChan: make(chan struct{}), + doneFunc: callHdr.DoneFunc, + statsHandler: handler, } s.Stream.buf.init() s.Stream.wq.init(defaultWriteQuota, s.done) @@ -510,7 +511,7 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *ClientSt return s } -func (t *http2Client) getPeer() *peer.Peer { +func (t *http2Client) Peer() *peer.Peer { return &peer.Peer{ Addr: t.remoteAddr, AuthInfo: t.authInfo, // Can be nil @@ -551,6 +552,9 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr) hfLen := 7 // :method, :scheme, :path, :authority, content-type, user-agent, te hfLen += len(authData) + len(callAuthData) registeredCompressors := t.registeredCompressors + if callHdr.AcceptedCompressors != nil { + registeredCompressors = *callHdr.AcceptedCompressors + } if callHdr.PreviousAttempts > 0 { hfLen++ } @@ -741,8 +745,8 @@ func (e NewStreamError) Error() string { // NewStream creates a stream and registers it into the transport as "active" // streams. All non-nil errors returned will be *NewStreamError. -func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*ClientStream, error) { - ctx = peer.NewContext(ctx, t.getPeer()) +func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr, handler stats.Handler) (*ClientStream, error) { + ctx = peer.NewContext(ctx, t.Peer()) // ServerName field of the resolver returned address takes precedence over // Host field of CallHdr to determine the :authority header. This is because, @@ -778,7 +782,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*ClientS if err != nil { return nil, &NewStreamError{Err: err, AllowTransparentRetry: false} } - s := t.newStream(ctx, callHdr) + s := t.newStream(ctx, callHdr, handler) cleanup := func(err error) { if s.swapState(streamDone) == streamDone { // If it was already done, return. @@ -899,7 +903,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*ClientS return nil, &NewStreamError{Err: ErrConnClosing, AllowTransparentRetry: true} } } - if t.statsHandler != nil { + if s.statsHandler != nil { header, ok := metadata.FromOutgoingContext(ctx) if ok { header.Set("user-agent", t.userAgent) @@ -908,7 +912,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*ClientS } // Note: The header fields are compressed with hpack after this call returns. // No WireLength field is set here. - t.statsHandler.HandleRPC(s.ctx, &stats.OutHeader{ + s.statsHandler.HandleRPC(s.ctx, &stats.OutHeader{ Client: true, FullMethod: callHdr.Method, RemoteAddr: t.remoteAddr, @@ -1485,7 +1489,7 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { case "grpc-status": code, err := strconv.ParseInt(hf.Value, 10, 32) if err != nil { - se := status.New(codes.Internal, fmt.Sprintf("transport: malformed grpc-status: %v", err)) + se := status.New(codes.Unknown, fmt.Sprintf("transport: malformed grpc-status: %v", err)) t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream) return } @@ -1584,16 +1588,16 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { } } - if t.statsHandler != nil { + if s.statsHandler != nil { if !endStream { - t.statsHandler.HandleRPC(s.ctx, &stats.InHeader{ + s.statsHandler.HandleRPC(s.ctx, &stats.InHeader{ Client: true, WireLength: int(frame.Header().Length), Header: metadata.MD(mdata).Copy(), Compression: s.recvCompress, }) } else { - t.statsHandler.HandleRPC(s.ctx, &stats.InTrailer{ + s.statsHandler.HandleRPC(s.ctx, &stats.InTrailer{ Client: true, WireLength: int(frame.Header().Length), Trailer: metadata.MD(mdata).Copy(), @@ -1807,8 +1811,6 @@ func (t *http2Client) socketMetrics() *channelz.EphemeralSocketMetrics { } } -func (t *http2Client) RemoteAddr() net.Addr { return t.remoteAddr } - func (t *http2Client) incrMsgSent() { if channelz.IsOn() { t.channelz.SocketMetrics.MessagesSent.Add(1) diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go index 6f78a6b0c8..a1a14e14fc 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go @@ -479,13 +479,7 @@ func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeade if t.logger.V(logLevel) { t.logger.Infof("Aborting the stream early: %v", errMsg) } - t.controlBuf.put(&earlyAbortStream{ - httpStatus: http.StatusBadRequest, - streamID: streamID, - contentSubtype: s.contentSubtype, - status: status.New(codes.Internal, errMsg), - rst: !frame.StreamEnded(), - }) + t.writeEarlyAbort(streamID, s.contentSubtype, status.New(codes.Internal, errMsg), http.StatusBadRequest, !frame.StreamEnded()) return nil } @@ -499,23 +493,11 @@ func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeade return nil } if !isGRPC { - t.controlBuf.put(&earlyAbortStream{ - httpStatus: http.StatusUnsupportedMediaType, - streamID: streamID, - contentSubtype: s.contentSubtype, - status: status.Newf(codes.InvalidArgument, "invalid gRPC request content-type %q", contentType), - rst: !frame.StreamEnded(), - }) + t.writeEarlyAbort(streamID, s.contentSubtype, status.Newf(codes.InvalidArgument, "invalid gRPC request content-type %q", contentType), http.StatusUnsupportedMediaType, !frame.StreamEnded()) return nil } if headerError != nil { - t.controlBuf.put(&earlyAbortStream{ - httpStatus: http.StatusBadRequest, - streamID: streamID, - contentSubtype: s.contentSubtype, - status: headerError, - rst: !frame.StreamEnded(), - }) + t.writeEarlyAbort(streamID, s.contentSubtype, headerError, http.StatusBadRequest, !frame.StreamEnded()) return nil } @@ -569,13 +551,7 @@ func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeade if t.logger.V(logLevel) { t.logger.Infof("Aborting the stream early: %v", errMsg) } - t.controlBuf.put(&earlyAbortStream{ - httpStatus: http.StatusMethodNotAllowed, - streamID: streamID, - contentSubtype: s.contentSubtype, - status: status.New(codes.Internal, errMsg), - rst: !frame.StreamEnded(), - }) + t.writeEarlyAbort(streamID, s.contentSubtype, status.New(codes.Internal, errMsg), http.StatusMethodNotAllowed, !frame.StreamEnded()) s.cancel() return nil } @@ -590,27 +566,16 @@ func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeade if !ok { stat = status.New(codes.PermissionDenied, err.Error()) } - t.controlBuf.put(&earlyAbortStream{ - httpStatus: http.StatusOK, - streamID: s.id, - contentSubtype: s.contentSubtype, - status: stat, - rst: !frame.StreamEnded(), - }) + t.writeEarlyAbort(s.id, s.contentSubtype, stat, http.StatusOK, !frame.StreamEnded()) return nil } } if s.ctx.Err() != nil { t.mu.Unlock() + st := status.New(codes.DeadlineExceeded, context.DeadlineExceeded.Error()) // Early abort in case the timeout was zero or so low it already fired. - t.controlBuf.put(&earlyAbortStream{ - httpStatus: http.StatusOK, - streamID: s.id, - contentSubtype: s.contentSubtype, - status: status.New(codes.DeadlineExceeded, context.DeadlineExceeded.Error()), - rst: !frame.StreamEnded(), - }) + t.writeEarlyAbort(s.id, s.contentSubtype, st, http.StatusOK, !frame.StreamEnded()) return nil } @@ -969,13 +934,12 @@ func appendHeaderFieldsFromMD(headerFields []hpack.HeaderField, md metadata.MD) return headerFields } -func (t *http2Server) checkForHeaderListSize(it any) bool { +func (t *http2Server) checkForHeaderListSize(hf []hpack.HeaderField) bool { if t.maxSendHeaderListSize == nil { return true } - hdrFrame := it.(*headerFrame) var sz int64 - for _, f := range hdrFrame.hf { + for _, f := range hf { if sz += int64(f.Size()); sz > int64(*t.maxSendHeaderListSize) { if t.logger.V(logLevel) { t.logger.Infof("Header list size to send violates the maximum size (%d bytes) set by client", *t.maxSendHeaderListSize) @@ -986,6 +950,42 @@ func (t *http2Server) checkForHeaderListSize(it any) bool { return true } +// writeEarlyAbort sends an early abort response with the given HTTP status and +// gRPC status. If the header list size exceeds the peer's limit, it sends a +// RST_STREAM instead. +func (t *http2Server) writeEarlyAbort(streamID uint32, contentSubtype string, stat *status.Status, httpStatus uint32, rst bool) { + hf := []hpack.HeaderField{ + {Name: ":status", Value: strconv.Itoa(int(httpStatus))}, + {Name: "content-type", Value: grpcutil.ContentType(contentSubtype)}, + {Name: "grpc-status", Value: strconv.Itoa(int(stat.Code()))}, + {Name: "grpc-message", Value: encodeGrpcMessage(stat.Message())}, + } + if p := istatus.RawStatusProto(stat); len(p.GetDetails()) > 0 { + stBytes, err := proto.Marshal(p) + if err != nil { + t.logger.Errorf("Failed to marshal rpc status: %s, error: %v", pretty.ToJSON(p), err) + } + if err == nil { + hf = append(hf, hpack.HeaderField{Name: grpcStatusDetailsBinHeader, Value: encodeBinHeader(stBytes)}) + } + } + success, _ := t.controlBuf.executeAndPut(func() bool { + return t.checkForHeaderListSize(hf) + }, &earlyAbortStream{ + streamID: streamID, + rst: rst, + hf: hf, + }) + if !success { + t.controlBuf.put(&cleanupStream{ + streamID: streamID, + rst: true, + rstCode: http2.ErrCodeInternal, + onWrite: func() {}, + }) + } +} + func (t *http2Server) streamContextErr(s *ServerStream) error { select { case <-t.done: @@ -1041,7 +1041,7 @@ func (t *http2Server) writeHeaderLocked(s *ServerStream) error { endStream: false, onWrite: t.setResetPingStrikes, } - success, err := t.controlBuf.executeAndPut(func() bool { return t.checkForHeaderListSize(hf) }, hf) + success, err := t.controlBuf.executeAndPut(func() bool { return t.checkForHeaderListSize(hf.hf) }, hf) if !success { if err != nil { return err @@ -1111,7 +1111,7 @@ func (t *http2Server) writeStatus(s *ServerStream, st *status.Status) error { } success, err := t.controlBuf.executeAndPut(func() bool { - return t.checkForHeaderListSize(trailingHeader) + return t.checkForHeaderListSize(trailingHeader.hf) }, nil) if !success { if err != nil { diff --git a/vendor/google.golang.org/grpc/internal/transport/http_util.go b/vendor/google.golang.org/grpc/internal/transport/http_util.go index 6209eb23cd..5bbb641ad9 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http_util.go +++ b/vendor/google.golang.org/grpc/internal/transport/http_util.go @@ -411,12 +411,6 @@ var writeBufferPoolMap = make(map[int]*sync.Pool) var writeBufferMutex sync.Mutex func newFramer(conn io.ReadWriter, writeBufferSize, readBufferSize int, sharedWriteBuffer bool, maxHeaderListSize uint32, memPool mem.BufferPool) *framer { - if memPool == nil { - // Note that this is only supposed to be nil in tests. Otherwise, stream - // is always initialized with a BufferPool. - memPool = mem.DefaultBufferPool() - } - if writeBufferSize < 0 { writeBufferSize = 0 } diff --git a/vendor/google.golang.org/grpc/internal/transport/transport.go b/vendor/google.golang.org/grpc/internal/transport/transport.go index 5ff83a7d7d..b86094da94 100644 --- a/vendor/google.golang.org/grpc/internal/transport/transport.go +++ b/vendor/google.golang.org/grpc/internal/transport/transport.go @@ -378,12 +378,28 @@ func (s *Stream) ReadMessageHeader(header []byte) (err error) { return nil } +// ceil returns the ceil after dividing the numerator and denominator while +// avoiding integer overflows. +func ceil(numerator, denominator int) int { + if numerator == 0 { + return 0 + } + return (numerator-1)/denominator + 1 +} + // Read reads n bytes from the wire for this stream. func (s *Stream) read(n int) (data mem.BufferSlice, err error) { // Don't request a read if there was an error earlier if er := s.trReader.er; er != nil { return nil, er } + // gRPC Go accepts data frames with a maximum length of 16KB. Larger + // messages must be split into multiple frames. We pre-allocate the + // buffer to avoid resizing during the read loop, but cap the initial + // capacity to 128 frames (2MB) to prevent over-allocation or panics + // when reading extremely large streams. + allocCap := min(ceil(n, http2MaxFrameLen), 128) + data = make(mem.BufferSlice, 0, allocCap) s.readRequester.requestRead(n) for n != 0 { buf, err := s.trReader.Read(n) @@ -553,6 +569,12 @@ type CallHdr struct { // outbound message. SendCompress string + // AcceptedCompressors overrides the grpc-accept-encoding header for this + // call. When nil, the transport advertises the default set of registered + // compressors. A non-nil pointer overrides that value (including the empty + // string to advertise none). + AcceptedCompressors *string + // Creds specifies credentials.PerRPCCredentials for a call. Creds credentials.PerRPCCredentials @@ -568,9 +590,14 @@ type CallHdr struct { DoneFunc func() // called when the stream is finished - // Authority is used to explicitly override the `:authority` header. If set, - // this value takes precedence over the Host field and will be used as the - // value for the `:authority` header. + // Authority is used to explicitly override the `:authority` header. + // + // This value comes from one of two sources: + // 1. The `CallAuthority` call option, if specified by the user. + // 2. An override provided by the LB picker (e.g. xDS authority rewriting). + // + // The `CallAuthority` call option always takes precedence over the LB + // picker override. Authority string } @@ -590,7 +617,7 @@ type ClientTransport interface { GracefulClose() // NewStream creates a Stream for an RPC. - NewStream(ctx context.Context, callHdr *CallHdr) (*ClientStream, error) + NewStream(ctx context.Context, callHdr *CallHdr, handler stats.Handler) (*ClientStream, error) // Error returns a channel that is closed when some I/O error // happens. Typically the caller should have a goroutine to monitor @@ -608,8 +635,9 @@ type ClientTransport interface { // with a human readable string with debug info. GetGoAwayReason() (GoAwayReason, string) - // RemoteAddr returns the remote network address. - RemoteAddr() net.Addr + // Peer returns information about the peer associated with the Transport. + // The returned information includes authentication and network address details. + Peer() *peer.Peer } // ServerTransport is the common interface for all gRPC server-side transport diff --git a/vendor/google.golang.org/grpc/mem/buffer_pool.go b/vendor/google.golang.org/grpc/mem/buffer_pool.go index f211e72745..2ea763a49a 100644 --- a/vendor/google.golang.org/grpc/mem/buffer_pool.go +++ b/vendor/google.golang.org/grpc/mem/buffer_pool.go @@ -38,9 +38,11 @@ type BufferPool interface { Put(*[]byte) } +const goPageSize = 4 << 10 // 4KiB. N.B. this must be a power of 2. + var defaultBufferPoolSizes = []int{ 256, - 4 << 10, // 4KB (go page size) + goPageSize, 16 << 10, // 16KB (max HTTP/2 frame size used by gRPC) 32 << 10, // 32KB (default buffer size for io.Copy) 1 << 20, // 1MB @@ -51,7 +53,7 @@ var defaultBufferPool BufferPool func init() { defaultBufferPool = NewTieredBufferPool(defaultBufferPoolSizes...) - internal.SetDefaultBufferPoolForTesting = func(pool BufferPool) { + internal.SetDefaultBufferPool = func(pool BufferPool) { defaultBufferPool = pool } @@ -172,7 +174,14 @@ func (p *simpleBufferPool) Get(size int) *[]byte { p.pool.Put(bs) } - b := make([]byte, size) + // If we're going to allocate, round up to the nearest page. This way if + // requests frequently arrive with small variation we don't allocate + // repeatedly if we get unlucky and they increase over time. By default we + // only allocate here if size > 1MiB. Because goPageSize is a power of 2, we + // can round up efficiently. + allocSize := (size + goPageSize - 1) & ^(goPageSize - 1) + + b := make([]byte, size, allocSize) return &b } diff --git a/vendor/google.golang.org/grpc/mem/buffers.go b/vendor/google.golang.org/grpc/mem/buffers.go index ecbf0b9a73..db1620e6ac 100644 --- a/vendor/google.golang.org/grpc/mem/buffers.go +++ b/vendor/google.golang.org/grpc/mem/buffers.go @@ -62,7 +62,6 @@ var ( bufferPoolingThreshold = 1 << 10 bufferObjectPool = sync.Pool{New: func() any { return new(buffer) }} - refObjectPool = sync.Pool{New: func() any { return new(atomic.Int32) }} ) // IsBelowBufferPoolingThreshold returns true if the given size is less than or @@ -73,9 +72,19 @@ func IsBelowBufferPoolingThreshold(size int) bool { } type buffer struct { + refs atomic.Int32 + data []byte + + // rootBuf is the buffer responsible for returning origData to the pool + // once the reference count drops to 0. + // + // When a buffer is split, the new buffer inherits the rootBuf of the + // original and increments the root's reference count. For the + // initial buffer (the root), this field points to itself. + rootBuf *buffer + + // The following fields are only set for root buffers. origData *[]byte - data []byte - refs *atomic.Int32 pool BufferPool } @@ -103,8 +112,8 @@ func NewBuffer(data *[]byte, pool BufferPool) Buffer { b.origData = data b.data = *data b.pool = pool - b.refs = refObjectPool.Get().(*atomic.Int32) - b.refs.Add(1) + b.rootBuf = b + b.refs.Store(1) return b } @@ -127,42 +136,44 @@ func Copy(data []byte, pool BufferPool) Buffer { } func (b *buffer) ReadOnlyData() []byte { - if b.refs == nil { + if b.rootBuf == nil { panic("Cannot read freed buffer") } return b.data } func (b *buffer) Ref() { - if b.refs == nil { + if b.refs.Add(1) <= 1 { panic("Cannot ref freed buffer") } - b.refs.Add(1) } func (b *buffer) Free() { - if b.refs == nil { + refs := b.refs.Add(-1) + if refs < 0 { panic("Cannot free freed buffer") } - - refs := b.refs.Add(-1) - switch { - case refs > 0: + if refs > 0 { return - case refs == 0: + } + + b.data = nil + if b.rootBuf == b { + // This buffer is the owner of the data slice and its ref count reached + // 0, free the slice. if b.pool != nil { b.pool.Put(b.origData) + b.pool = nil } - - refObjectPool.Put(b.refs) b.origData = nil - b.data = nil - b.refs = nil - b.pool = nil - bufferObjectPool.Put(b) - default: - panic("Cannot free freed buffer") + } else { + // This buffer doesn't own the data slice, decrement a ref on the root + // buffer. + b.rootBuf.Free() } + + b.rootBuf = nil + bufferObjectPool.Put(b) } func (b *buffer) Len() int { @@ -170,16 +181,14 @@ func (b *buffer) Len() int { } func (b *buffer) split(n int) (Buffer, Buffer) { - if b.refs == nil { + if b.rootBuf == nil || b.rootBuf.refs.Add(1) <= 1 { panic("Cannot split freed buffer") } - b.refs.Add(1) split := newBuffer() - split.origData = b.origData split.data = b.data[n:] - split.refs = b.refs - split.pool = b.pool + split.rootBuf = b.rootBuf + split.refs.Store(1) b.data = b.data[:n] @@ -187,7 +196,7 @@ func (b *buffer) split(n int) (Buffer, Buffer) { } func (b *buffer) read(buf []byte) (int, Buffer) { - if b.refs == nil { + if b.rootBuf == nil { panic("Cannot read freed buffer") } diff --git a/vendor/google.golang.org/grpc/resolver/resolver.go b/vendor/google.golang.org/grpc/resolver/resolver.go index 8e6af9514b..598ed21a29 100644 --- a/vendor/google.golang.org/grpc/resolver/resolver.go +++ b/vendor/google.golang.org/grpc/resolver/resolver.go @@ -182,6 +182,7 @@ type BuildOptions struct { // An Endpoint is one network endpoint, or server, which may have multiple // addresses with which it can be accessed. +// TODO(i/8773) : make resolver.Endpoint and resolver.Address immutable type Endpoint struct { // Addresses contains a list of addresses used to access this endpoint. Addresses []Address diff --git a/vendor/google.golang.org/grpc/resolver_wrapper.go b/vendor/google.golang.org/grpc/resolver_wrapper.go index 80e16a327c..6e61376437 100644 --- a/vendor/google.golang.org/grpc/resolver_wrapper.go +++ b/vendor/google.golang.org/grpc/resolver_wrapper.go @@ -69,6 +69,7 @@ func (ccr *ccResolverWrapper) start() error { errCh := make(chan error) ccr.serializer.TrySchedule(func(ctx context.Context) { if ctx.Err() != nil { + errCh <- ctx.Err() return } opts := resolver.BuildOptions{ diff --git a/vendor/google.golang.org/grpc/rpc_util.go b/vendor/google.golang.org/grpc/rpc_util.go index 6b04c9e873..8160f94304 100644 --- a/vendor/google.golang.org/grpc/rpc_util.go +++ b/vendor/google.golang.org/grpc/rpc_util.go @@ -33,6 +33,8 @@ import ( "google.golang.org/grpc/credentials" "google.golang.org/grpc/encoding" "google.golang.org/grpc/encoding/proto" + "google.golang.org/grpc/internal" + "google.golang.org/grpc/internal/grpcutil" "google.golang.org/grpc/internal/transport" "google.golang.org/grpc/mem" "google.golang.org/grpc/metadata" @@ -41,6 +43,10 @@ import ( "google.golang.org/grpc/status" ) +func init() { + internal.AcceptCompressors = acceptCompressors +} + // Compressor defines the interface gRPC uses to compress a message. // // Deprecated: use package encoding. @@ -151,16 +157,32 @@ func (d *gzipDecompressor) Type() string { // callInfo contains all related configuration and information about an RPC. type callInfo struct { - compressorName string - failFast bool - maxReceiveMessageSize *int - maxSendMessageSize *int - creds credentials.PerRPCCredentials - contentSubtype string - codec baseCodec - maxRetryRPCBufferSize int - onFinish []func(err error) - authority string + compressorName string + failFast bool + maxReceiveMessageSize *int + maxSendMessageSize *int + creds credentials.PerRPCCredentials + contentSubtype string + codec baseCodec + maxRetryRPCBufferSize int + onFinish []func(err error) + authority string + acceptedResponseCompressors []string +} + +func acceptedCompressorAllows(allowed []string, name string) bool { + if allowed == nil { + return true + } + if name == "" || name == encoding.Identity { + return true + } + for _, a := range allowed { + if a == name { + return true + } + } + return false } func defaultCallInfo() *callInfo { @@ -170,6 +192,29 @@ func defaultCallInfo() *callInfo { } } +func newAcceptedCompressionConfig(names []string) ([]string, error) { + if len(names) == 0 { + return nil, nil + } + var allowed []string + seen := make(map[string]struct{}, len(names)) + for _, name := range names { + name = strings.TrimSpace(name) + if name == "" || name == encoding.Identity { + continue + } + if !grpcutil.IsCompressorNameRegistered(name) { + return nil, status.Errorf(codes.InvalidArgument, "grpc: compressor %q is not registered", name) + } + if _, dup := seen[name]; dup { + continue + } + seen[name] = struct{}{} + allowed = append(allowed, name) + } + return allowed, nil +} + // CallOption configures a Call before it starts or extracts information from // a Call after it completes. type CallOption interface { @@ -471,6 +516,31 @@ func (o CompressorCallOption) before(c *callInfo) error { } func (o CompressorCallOption) after(*callInfo, *csAttempt) {} +// acceptCompressors returns a CallOption that limits the compression algorithms +// advertised in the grpc-accept-encoding header for response messages. +// Compression algorithms not in the provided list will not be advertised, and +// responses compressed with non-listed algorithms will be rejected. +func acceptCompressors(names ...string) CallOption { + cp := append([]string(nil), names...) + return acceptCompressorsCallOption{names: cp} +} + +// acceptCompressorsCallOption is a CallOption that limits response compression. +type acceptCompressorsCallOption struct { + names []string +} + +func (o acceptCompressorsCallOption) before(c *callInfo) error { + allowed, err := newAcceptedCompressionConfig(o.names) + if err != nil { + return err + } + c.acceptedResponseCompressors = allowed + return nil +} + +func (acceptCompressorsCallOption) after(*callInfo, *csAttempt) {} + // CallContentSubtype returns a CallOption that will set the content-subtype // for a call. For example, if content-subtype is "json", the Content-Type over // the wire will be "application/grpc+json". The content-subtype is converted @@ -857,8 +927,7 @@ func (p *payloadInfo) free() { // the buffer is no longer needed. // TODO: Refactor this function to reduce the number of arguments. // See: https://google.github.io/styleguide/go/best-practices.html#function-argument-lists -func recvAndDecompress(p *parser, s recvCompressor, dc Decompressor, maxReceiveMessageSize int, payInfo *payloadInfo, compressor encoding.Compressor, isServer bool, -) (out mem.BufferSlice, err error) { +func recvAndDecompress(p *parser, s recvCompressor, dc Decompressor, maxReceiveMessageSize int, payInfo *payloadInfo, compressor encoding.Compressor, isServer bool) (out mem.BufferSlice, err error) { pf, compressed, err := p.recvMsg(maxReceiveMessageSize) if err != nil { return nil, err diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go index ddd3773411..8efb29a7b9 100644 --- a/vendor/google.golang.org/grpc/server.go +++ b/vendor/google.golang.org/grpc/server.go @@ -42,6 +42,7 @@ import ( "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/binarylog" "google.golang.org/grpc/internal/channelz" + "google.golang.org/grpc/internal/envconfig" "google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/grpcutil" istats "google.golang.org/grpc/internal/stats" @@ -149,6 +150,8 @@ type Server struct { serverWorkerChannel chan func() serverWorkerChannelClose func() + + strictPathCheckingLogEmitted atomic.Bool } type serverOptions struct { @@ -923,9 +926,7 @@ func (s *Server) Serve(lis net.Listener) error { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 - } - if max := 1 * time.Second; tempDelay > max { - tempDelay = max + tempDelay = min(tempDelay, 1*time.Second) } s.mu.Lock() s.printf("Accept error: %v; retrying in %v", err, tempDelay) @@ -1764,6 +1765,24 @@ func (s *Server) processStreamingRPC(ctx context.Context, stream *transport.Serv return ss.s.WriteStatus(statusOK) } +func (s *Server) handleMalformedMethodName(stream *transport.ServerStream, ti *traceInfo) { + if ti != nil { + ti.tr.LazyLog(&fmtStringer{"Malformed method name %q", []any{stream.Method()}}, true) + ti.tr.SetError() + } + errDesc := fmt.Sprintf("malformed method name: %q", stream.Method()) + if err := stream.WriteStatus(status.New(codes.Unimplemented, errDesc)); err != nil { + if ti != nil { + ti.tr.LazyLog(&fmtStringer{"%v", []any{err}}, true) + ti.tr.SetError() + } + channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream failed to write status: %v", err) + } + if ti != nil { + ti.tr.Finish() + } +} + func (s *Server) handleStream(t transport.ServerTransport, stream *transport.ServerStream) { ctx := stream.Context() ctx = contextWithServer(ctx, s) @@ -1784,26 +1803,30 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Ser } sm := stream.Method() - if sm != "" && sm[0] == '/' { + if sm == "" { + s.handleMalformedMethodName(stream, ti) + return + } + if sm[0] != '/' { + // TODO(easwars): Add a link to the CVE in the below log messages once + // published. + if envconfig.DisableStrictPathChecking { + if old := s.strictPathCheckingLogEmitted.Swap(true); !old { + channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream received malformed method name %q. Allowing it because the environment variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING is set to true, but this option will be removed in a future release.", sm) + } + } else { + if old := s.strictPathCheckingLogEmitted.Swap(true); !old { + channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream rejected malformed method name %q. To temporarily allow such requests, set the environment variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to true. Note that this is not recommended as it may allow requests to bypass security policies.", sm) + } + s.handleMalformedMethodName(stream, ti) + return + } + } else { sm = sm[1:] } pos := strings.LastIndex(sm, "/") if pos == -1 { - if ti != nil { - ti.tr.LazyLog(&fmtStringer{"Malformed method name %q", []any{sm}}, true) - ti.tr.SetError() - } - errDesc := fmt.Sprintf("malformed method name: %q", stream.Method()) - if err := stream.WriteStatus(status.New(codes.Unimplemented, errDesc)); err != nil { - if ti != nil { - ti.tr.LazyLog(&fmtStringer{"%v", []any{err}}, true) - ti.tr.SetError() - } - channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream failed to write status: %v", err) - } - if ti != nil { - ti.tr.Finish() - } + s.handleMalformedMethodName(stream, ti) return } service := sm[:pos] diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go index ca87ff9776..eedb5f9b99 100644 --- a/vendor/google.golang.org/grpc/stream.go +++ b/vendor/google.golang.org/grpc/stream.go @@ -25,6 +25,7 @@ import ( "math" rand "math/rand/v2" "strconv" + "strings" "sync" "time" @@ -51,7 +52,8 @@ import ( var metadataFromOutgoingContextRaw = internal.FromOutgoingContextRaw.(func(context.Context) (metadata.MD, [][]string, bool)) // StreamHandler defines the handler called by gRPC server to complete the -// execution of a streaming RPC. +// execution of a streaming RPC. srv is the service implementation on which the +// RPC was invoked. // // If a StreamHandler returns an error, it should either be produced by the // status package, or be one of the context errors. Otherwise, gRPC will use @@ -179,13 +181,41 @@ func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth var emptyMethodConfig = serviceconfig.MethodConfig{} +// endOfClientStream performs cleanup actions required for both successful and +// failed streams. This includes incrementing channelz stats and invoking all +// registered OnFinish call options. +func endOfClientStream(cc *ClientConn, err error, opts ...CallOption) { + if channelz.IsOn() { + if err != nil { + cc.incrCallsFailed() + } else { + cc.incrCallsSucceeded() + } + } + + for _, o := range opts { + if o, ok := o.(OnFinishCallOption); ok { + o.OnFinish(err) + } + } +} + func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) { + if channelz.IsOn() { + cc.incrCallsStarted() + } + defer func() { + if err != nil { + // Ensure cleanup when stream creation fails. + endOfClientStream(cc, err, opts...) + } + }() + // Start tracking the RPC for idleness purposes. This is where a stream is // created for both streaming and unary RPCs, and hence is a good place to // track active RPC count. - if err := cc.idlenessMgr.OnCallBegin(); err != nil { - return nil, err - } + cc.idlenessMgr.OnCallBegin() + // Add a calloption, to decrement the active call count, that gets executed // when the RPC completes. opts = append([]CallOption{OnFinish(func(error) { cc.idlenessMgr.OnCallEnd() })}, opts...) @@ -204,14 +234,6 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth } } } - if channelz.IsOn() { - cc.incrCallsStarted() - defer func() { - if err != nil { - cc.incrCallsFailed() - } - }() - } // Provide an opportunity for the first RPC to see the first service config // provided by the resolver. nameResolutionDelayed, err := cc.waitForResolvedAddrs(ctx) @@ -301,6 +323,10 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client DoneFunc: doneFunc, Authority: callInfo.authority, } + if allowed := callInfo.acceptedResponseCompressors; len(allowed) > 0 { + headerValue := strings.Join(allowed, ",") + callHdr.AcceptedCompressors = &headerValue + } // Set our outgoing compression according to the UseCompressor CallOption, if // set. In that case, also find the compressor from the encoding package. @@ -484,7 +510,7 @@ func (a *csAttempt) getTransport() error { return err } if a.trInfo != nil { - a.trInfo.firstLine.SetRemoteAddr(a.transport.RemoteAddr()) + a.trInfo.firstLine.SetRemoteAddr(a.transport.Peer().Addr) } if pick.blocked && a.statsHandler != nil { a.statsHandler.HandleRPC(a.ctx, &stats.DelayedPickComplete{}) @@ -512,9 +538,17 @@ func (a *csAttempt) newStream() error { md, _ := metadata.FromOutgoingContext(a.ctx) md = metadata.Join(md, a.pickResult.Metadata) a.ctx = metadata.NewOutgoingContext(a.ctx, md) - } - s, err := a.transport.NewStream(a.ctx, cs.callHdr) + // If the `CallAuthority` CallOption is not set, check if the LB picker + // has provided an authority override in the PickResult metadata and + // apply it, as specified in gRFC A81. + if cs.callInfo.authority == "" { + if authMD := a.pickResult.Metadata.Get(":authority"); len(authMD) > 0 { + cs.callHdr.Authority = authMD[0] + } + } + } + s, err := a.transport.NewStream(a.ctx, cs.callHdr, a.statsHandler) if err != nil { nse, ok := err.(*transport.NewStreamError) if !ok { @@ -1042,9 +1076,6 @@ func (cs *clientStream) finish(err error) { return } cs.finished = true - for _, onFinish := range cs.callInfo.onFinish { - onFinish(err) - } cs.commitAttemptLocked() if cs.attempt != nil { cs.attempt.finish(err) @@ -1084,13 +1115,7 @@ func (cs *clientStream) finish(err error) { if err == nil { cs.retryThrottler.successfulRPC() } - if channelz.IsOn() { - if err != nil { - cs.cc.incrCallsFailed() - } else { - cs.cc.incrCallsSucceeded() - } - } + endOfClientStream(cs.cc, err, cs.opts...) cs.cancel() } @@ -1134,6 +1159,10 @@ func (a *csAttempt) recvMsg(m any, payInfo *payloadInfo) (err error) { a.decompressorV0 = nil a.decompressorV1 = encoding.GetCompressor(ct) } + // Validate that the compression method is acceptable for this call. + if !acceptedCompressorAllows(cs.callInfo.acceptedResponseCompressors, ct) { + return status.Errorf(codes.Internal, "grpc: peer compressed the response with %q which is not allowed by AcceptCompressors", ct) + } } else { // No compression is used; disable our decompressor. a.decompressorV0 = nil @@ -1321,10 +1350,12 @@ func newNonRetryClientStream(ctx context.Context, desc *StreamDesc, method strin codec: c.codec, sendCompressorV0: cp, sendCompressorV1: comp, + decompressorV0: ac.cc.dopts.dc, transport: t, } - s, err := as.transport.NewStream(as.ctx, as.callHdr) + // nil stats handler: internal streams like health and ORCA do not support telemetry. + s, err := as.transport.NewStream(as.ctx, as.callHdr, nil) if err != nil { err = toRPCErr(err) return nil, err @@ -1479,6 +1510,10 @@ func (as *addrConnStream) RecvMsg(m any) (err error) { as.decompressorV0 = nil as.decompressorV1 = encoding.GetCompressor(ct) } + // Validate that the compression method is acceptable for this call. + if !acceptedCompressorAllows(as.callInfo.acceptedResponseCompressors, ct) { + return status.Errorf(codes.Internal, "grpc: peer compressed the response with %q which is not allowed by AcceptCompressors", ct) + } } else { // No compression is used; disable our decompressor. as.decompressorV0 = nil diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go index 9e6d018fb7..76c2eed773 100644 --- a/vendor/google.golang.org/grpc/version.go +++ b/vendor/google.golang.org/grpc/version.go @@ -19,4 +19,4 @@ package grpc // Version is the current grpc version. -const Version = "1.77.0" +const Version = "1.79.3" diff --git a/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go b/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go index 669133d04d..c96e448346 100644 --- a/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go +++ b/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go @@ -32,7 +32,7 @@ var byteType = reflect.TypeOf(byte(0)) func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescriptors) protoreflect.FieldDescriptor { f := new(filedesc.Field) f.L0.ParentFile = filedesc.SurrogateProto2 - f.L1.EditionFeatures = f.L0.ParentFile.L1.EditionFeatures + packed := false for len(tag) > 0 { i := strings.IndexByte(tag, ',') if i < 0 { @@ -108,7 +108,7 @@ func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescri f.L1.StringName.InitJSON(jsonName) } case s == "packed": - f.L1.EditionFeatures.IsPacked = true + packed = true case strings.HasPrefix(s, "def="): // The default tag is special in that everything afterwards is the // default regardless of the presence of commas. @@ -121,6 +121,13 @@ func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescri tag = strings.TrimPrefix(tag[i:], ",") } + // Update EditionFeatures after the loop and after we know whether this is + // a proto2 or proto3 field. + f.L1.EditionFeatures = f.L0.ParentFile.L1.EditionFeatures + if packed { + f.L1.EditionFeatures.IsPacked = true + } + // The generator uses the group message name instead of the field name. // We obtain the real field name by lowercasing the group name. if f.L1.Kind == protoreflect.GroupKind { diff --git a/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go b/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go index 099b2bf451..9aa7a9bb77 100644 --- a/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go +++ b/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go @@ -424,27 +424,34 @@ func (d *Decoder) parseFieldName() (tok Token, err error) { return Token{}, d.newSyntaxError("invalid field name: %s", errId(d.in)) } -// parseTypeName parses Any type URL or extension field name. The name is -// enclosed in [ and ] characters. The C++ parser does not handle many legal URL -// strings. This implementation is more liberal and allows for the pattern -// ^[-_a-zA-Z0-9]+([./][-_a-zA-Z0-9]+)*`). Whitespaces and comments are allowed -// in between [ ], '.', '/' and the sub names. +// parseTypeName parses an Any type URL or an extension field name. The name is +// enclosed in [ and ] characters. We allow almost arbitrary type URL prefixes, +// closely following the text-format spec [1,2]. We implement "ExtensionName | +// AnyName" as follows (with some exceptions for backwards compatibility): +// +// char = [-_a-zA-Z0-9] +// url_char = char | [.~!$&'()*+,;=] | "%", hex, hex +// +// Ident = char, { char } +// TypeName = Ident, { ".", Ident } ; +// UrlPrefix = url_char, { url_char | "/" } ; +// ExtensionName = "[", TypeName, "]" ; +// AnyName = "[", UrlPrefix, "/", TypeName, "]" ; +// +// Additionally, we allow arbitrary whitespace and comments between [ and ]. +// +// [1] https://protobuf.dev/reference/protobuf/textformat-spec/#characters +// [2] https://protobuf.dev/reference/protobuf/textformat-spec/#field-names func (d *Decoder) parseTypeName() (Token, error) { - startPos := len(d.orig) - len(d.in) // Use alias s to advance first in order to use d.in for error handling. - // Caller already checks for [ as first character. + // Caller already checks for [ as first character (d.in[0] == '['). s := consume(d.in[1:], 0) if len(s) == 0 { return Token{}, ErrUnexpectedEOF } + // Collect everything between [ and ] in name. var name []byte - for len(s) > 0 && isTypeNameChar(s[0]) { - name = append(name, s[0]) - s = s[1:] - } - s = consume(s, 0) - var closed bool for len(s) > 0 && !closed { switch { @@ -452,23 +459,20 @@ func (d *Decoder) parseTypeName() (Token, error) { s = s[1:] closed = true - case s[0] == '/', s[0] == '.': - if len(name) > 0 && (name[len(name)-1] == '/' || name[len(name)-1] == '.') { - return Token{}, d.newSyntaxError("invalid type URL/extension field name: %s", - d.orig[startPos:len(d.orig)-len(s)+1]) - } + case s[0] == '/' || isTypeNameChar(s[0]) || isUrlExtraChar(s[0]): name = append(name, s[0]) - s = s[1:] - s = consume(s, 0) - for len(s) > 0 && isTypeNameChar(s[0]) { - name = append(name, s[0]) - s = s[1:] + s = consume(s[1:], 0) + + // URL percent-encoded chars + case s[0] == '%': + if len(s) < 3 || !isHexChar(s[1]) || !isHexChar(s[2]) { + return Token{}, d.parseTypeNameError(s, 3) } - s = consume(s, 0) + name = append(name, s[0], s[1], s[2]) + s = consume(s[3:], 0) default: - return Token{}, d.newSyntaxError( - "invalid type URL/extension field name: %s", d.orig[startPos:len(d.orig)-len(s)+1]) + return Token{}, d.parseTypeNameError(s, 1) } } @@ -476,15 +480,38 @@ func (d *Decoder) parseTypeName() (Token, error) { return Token{}, ErrUnexpectedEOF } - // First character cannot be '.'. Last character cannot be '.' or '/'. - size := len(name) - if size == 0 || name[0] == '.' || name[size-1] == '.' || name[size-1] == '/' { - return Token{}, d.newSyntaxError("invalid type URL/extension field name: %s", - d.orig[startPos:len(d.orig)-len(s)]) + // Split collected name on last '/' into urlPrefix and typeName (if '/' is + // present). + typeName := name + if i := bytes.LastIndexByte(name, '/'); i != -1 { + urlPrefix := name[:i] + typeName = name[i+1:] + + // urlPrefix may be empty (for backwards compatibility). + // If non-empty, it must not start with '/'. + if len(urlPrefix) > 0 && urlPrefix[0] == '/' { + return Token{}, d.parseTypeNameError(s, 0) + } } + // typeName must not be empty (note: "" splits to [""]) and all identifier + // parts must not be empty. + for _, ident := range bytes.Split(typeName, []byte{'.'}) { + if len(ident) == 0 { + return Token{}, d.parseTypeNameError(s, 0) + } + } + + // typeName must not contain any percent-encoded or special URL chars. + for _, b := range typeName { + if b == '%' || (b != '.' && isUrlExtraChar(b)) { + return Token{}, d.parseTypeNameError(s, 0) + } + } + + startPos := len(d.orig) - len(d.in) + endPos := len(d.orig) - len(s) d.in = s - endPos := len(d.orig) - len(d.in) d.consume(0) return Token{ @@ -496,16 +523,32 @@ func (d *Decoder) parseTypeName() (Token, error) { }, nil } +func (d *Decoder) parseTypeNameError(s []byte, numUnconsumedChars int) error { + return d.newSyntaxError( + "invalid type URL/extension field name: %s", + d.in[:len(d.in)-len(s)+min(numUnconsumedChars, len(s))], + ) +} + +func isHexChar(b byte) bool { + return ('0' <= b && b <= '9') || + ('a' <= b && b <= 'f') || + ('A' <= b && b <= 'F') +} + func isTypeNameChar(b byte) bool { - return (b == '-' || b == '_' || + return b == '-' || b == '_' || ('0' <= b && b <= '9') || ('a' <= b && b <= 'z') || - ('A' <= b && b <= 'Z')) + ('A' <= b && b <= 'Z') } -func isWhiteSpace(b byte) bool { +// isUrlExtraChar complements isTypeNameChar with extra characters that we allow +// in URLs but not in type names. Note that '/' is not included so that it can +// be treated specially. +func isUrlExtraChar(b byte) bool { switch b { - case ' ', '\n', '\r', '\t': + case '.', '~', '!', '$', '&', '(', ')', '*', '+', ',', ';', '=': return true default: return false diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/desc.go b/vendor/google.golang.org/protobuf/internal/filedesc/desc.go index dbcf90b871..c775e5832f 100644 --- a/vendor/google.golang.org/protobuf/internal/filedesc/desc.go +++ b/vendor/google.golang.org/protobuf/internal/filedesc/desc.go @@ -32,6 +32,7 @@ const ( EditionProto3 Edition = 999 Edition2023 Edition = 1000 Edition2024 Edition = 1001 + EditionUnstable Edition = 9999 EditionUnsupported Edition = 100000 ) diff --git a/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go b/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go index dd31faaeb0..78f02b1b49 100644 --- a/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go +++ b/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go @@ -330,7 +330,6 @@ func (md *Message) unmarshalFull(b []byte, sb *strs.Builder) { md.L1.Extensions.List[extensionIdx].unmarshalFull(v, sb) extensionIdx++ case genid.DescriptorProto_Options_field_number: - md.unmarshalOptions(v) rawOptions = appendOptions(rawOptions, v) } default: @@ -356,27 +355,6 @@ func (md *Message) unmarshalFull(b []byte, sb *strs.Builder) { md.L2.Options = md.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Message, rawOptions) } -func (md *Message) unmarshalOptions(b []byte) { - for len(b) > 0 { - num, typ, n := protowire.ConsumeTag(b) - b = b[n:] - switch typ { - case protowire.VarintType: - v, m := protowire.ConsumeVarint(b) - b = b[m:] - switch num { - case genid.MessageOptions_MapEntry_field_number: - md.L1.IsMapEntry = protowire.DecodeBool(v) - case genid.MessageOptions_MessageSetWireFormat_field_number: - md.L1.IsMessageSet = protowire.DecodeBool(v) - } - default: - m := protowire.ConsumeFieldValue(num, typ, b) - b = b[m:] - } - } -} - func unmarshalMessageReservedRange(b []byte) (r [2]protoreflect.FieldNumber) { for len(b) > 0 { num, typ, n := protowire.ConsumeTag(b) diff --git a/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go b/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go index 950a6a325a..65aaf4d210 100644 --- a/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go +++ b/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go @@ -26,6 +26,7 @@ const ( Edition_EDITION_PROTO3_enum_value = 999 Edition_EDITION_2023_enum_value = 1000 Edition_EDITION_2024_enum_value = 1001 + Edition_EDITION_UNSTABLE_enum_value = 9999 Edition_EDITION_1_TEST_ONLY_enum_value = 1 Edition_EDITION_2_TEST_ONLY_enum_value = 2 Edition_EDITION_99997_TEST_ONLY_enum_value = 99997 diff --git a/vendor/google.golang.org/protobuf/internal/impl/codec_map.go b/vendor/google.golang.org/protobuf/internal/impl/codec_map.go index 229c698013..4a3bf393ef 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/codec_map.go +++ b/vendor/google.golang.org/protobuf/internal/impl/codec_map.go @@ -113,6 +113,9 @@ func sizeMap(mapv reflect.Value, mapi *mapInfo, f *coderFieldInfo, opts marshalO } func consumeMap(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi *mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { + if opts.depth--; opts.depth < 0 { + return out, errRecursionDepth + } if wtyp != protowire.BytesType { return out, errUnknown } @@ -170,6 +173,9 @@ func consumeMap(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi *mapInfo } func consumeMapOfMessage(b []byte, mapv reflect.Value, wtyp protowire.Type, mapi *mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { + if opts.depth--; opts.depth < 0 { + return out, errRecursionDepth + } if wtyp != protowire.BytesType { return out, errUnknown } diff --git a/vendor/google.golang.org/protobuf/internal/impl/decode.go b/vendor/google.golang.org/protobuf/internal/impl/decode.go index e0dd21fa5f..1228b5c8c2 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/decode.go +++ b/vendor/google.golang.org/protobuf/internal/impl/decode.go @@ -102,8 +102,7 @@ var errUnknown = errors.New("unknown") func (mi *MessageInfo) unmarshalPointer(b []byte, p pointer, groupTag protowire.Number, opts unmarshalOptions) (out unmarshalOutput, err error) { mi.init() - opts.depth-- - if opts.depth < 0 { + if opts.depth--; opts.depth < 0 { return out, errRecursionDepth } if flags.ProtoLegacy && mi.isMessageSet { diff --git a/vendor/google.golang.org/protobuf/internal/impl/validate.go b/vendor/google.golang.org/protobuf/internal/impl/validate.go index 7b2995dde5..99a1eb95f7 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/validate.go +++ b/vendor/google.golang.org/protobuf/internal/impl/validate.go @@ -68,9 +68,13 @@ func Validate(mt protoreflect.MessageType, in protoiface.UnmarshalInput) (out pr if in.Resolver == nil { in.Resolver = protoregistry.GlobalTypes } + if in.Depth == 0 { + in.Depth = protowire.DefaultRecursionLimit + } o, st := mi.validate(in.Buf, 0, unmarshalOptions{ flags: in.Flags, resolver: in.Resolver, + depth: in.Depth, }) if o.initialized { out.Flags |= protoiface.UnmarshalInitialized @@ -257,6 +261,9 @@ func (mi *MessageInfo) validate(b []byte, groupTag protowire.Number, opts unmars states[0].typ = validationTypeGroup states[0].endGroup = groupTag } + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } initialized := true start := len(b) State: @@ -451,6 +458,13 @@ State: mi: vi.mi, tail: b, }) + if vi.typ == validationTypeMessage || + vi.typ == validationTypeGroup || + vi.typ == validationTypeMap { + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } + } b = v continue State case validationTypeRepeatedVarint: @@ -499,6 +513,9 @@ State: mi: vi.mi, endGroup: num, }) + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } continue State case flags.ProtoLegacy && vi.typ == validationTypeMessageSetItem: typeid, v, n, err := messageset.ConsumeFieldValue(b, false) @@ -521,6 +538,13 @@ State: mi: xvi.mi, tail: b[n:], }) + if xvi.typ == validationTypeMessage || + xvi.typ == validationTypeGroup || + xvi.typ == validationTypeMap { + if opts.depth--; opts.depth < 0 { + return out, ValidationInvalid + } + } b = v continue State } @@ -547,12 +571,14 @@ State: switch st.typ { case validationTypeMessage, validationTypeGroup: numRequiredFields = int(st.mi.numRequiredFields) + opts.depth++ case validationTypeMap: // If this is a map field with a message value that contains // required fields, require that the value be present. if st.mi != nil && st.mi.numRequiredFields > 0 { numRequiredFields = 1 } + opts.depth++ } // If there are more than 64 required fields, this check will // always fail and we will report that the message is potentially diff --git a/vendor/google.golang.org/protobuf/internal/version/version.go b/vendor/google.golang.org/protobuf/internal/version/version.go index 77de0f238c..763fd82841 100644 --- a/vendor/google.golang.org/protobuf/internal/version/version.go +++ b/vendor/google.golang.org/protobuf/internal/version/version.go @@ -52,7 +52,7 @@ import ( const ( Major = 1 Minor = 36 - Patch = 10 + Patch = 11 PreRelease = "" ) diff --git a/vendor/google.golang.org/protobuf/proto/decode.go b/vendor/google.golang.org/protobuf/proto/decode.go index 4cbf1aeaf7..889d8511d2 100644 --- a/vendor/google.golang.org/protobuf/proto/decode.go +++ b/vendor/google.golang.org/protobuf/proto/decode.go @@ -121,9 +121,8 @@ func (o UnmarshalOptions) unmarshal(b []byte, m protoreflect.Message) (out proto out, err = methods.Unmarshal(in) } else { - o.RecursionLimit-- - if o.RecursionLimit < 0 { - return out, errors.New("exceeded max recursion depth") + if o.RecursionLimit--; o.RecursionLimit < 0 { + return out, errRecursionDepth } err = o.unmarshalMessageSlow(b, m) } @@ -220,6 +219,9 @@ func (o UnmarshalOptions) unmarshalSingular(b []byte, wtyp protowire.Type, m pro } func (o UnmarshalOptions) unmarshalMap(b []byte, wtyp protowire.Type, mapv protoreflect.Map, fd protoreflect.FieldDescriptor) (n int, err error) { + if o.RecursionLimit--; o.RecursionLimit < 0 { + return 0, errRecursionDepth + } if wtyp != protowire.BytesType { return 0, errUnknown } @@ -305,3 +307,5 @@ func (o UnmarshalOptions) unmarshalMap(b []byte, wtyp protowire.Type, mapv proto var errUnknown = errors.New("BUG: internal error (unknown)") var errDecode = errors.New("cannot parse invalid wire-format data") + +var errRecursionDepth = errors.New("exceeded maximum recursion depth") diff --git a/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go b/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go index 9196288e4a..40f17af4e3 100644 --- a/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go +++ b/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go @@ -108,7 +108,9 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot if f.L1.Path == "" { return nil, errors.New("file path must be populated") } - if f.L1.Syntax == protoreflect.Editions && (fd.GetEdition() < editionssupport.Minimum || fd.GetEdition() > editionssupport.Maximum) { + if f.L1.Syntax == protoreflect.Editions && + (fd.GetEdition() < editionssupport.Minimum || fd.GetEdition() > editionssupport.Maximum) && + fd.GetEdition() != descriptorpb.Edition_EDITION_UNSTABLE { // Allow cmd/protoc-gen-go/testdata to use any edition for easier // testing of upcoming edition features. if !strings.HasPrefix(fd.GetName(), "cmd/protoc-gen-go/testdata/") { @@ -152,6 +154,7 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot imp := &f.L2.Imports[i] imps.importPublic(imp.Imports()) } + optionImps := importSet{f.Path(): true} if len(fd.GetOptionDependency()) > 0 { optionImports := make(filedesc.FileImports, len(fd.GetOptionDependency())) for i, path := range fd.GetOptionDependency() { @@ -165,10 +168,12 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot } imp.FileDescriptor = f - if imps[imp.Path()] { + if imps[imp.Path()] || optionImps[imp.Path()] { return nil, errors.New("already imported %q", path) } - imps[imp.Path()] = true + // This needs to be a separate map so that we don't recognize non-options + // symbols coming from option imports. + optionImps[imp.Path()] = true } f.L2.OptionImports = func() protoreflect.FileImports { return &optionImports diff --git a/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go b/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go index 697a61b290..147b8c7398 100644 --- a/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go +++ b/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go @@ -46,6 +46,8 @@ func toEditionProto(ed filedesc.Edition) descriptorpb.Edition { return descriptorpb.Edition_EDITION_2023 case filedesc.Edition2024: return descriptorpb.Edition_EDITION_2024 + case filedesc.EditionUnstable: + return descriptorpb.Edition_EDITION_UNSTABLE default: panic(fmt.Sprintf("unknown value for edition: %v", ed)) } @@ -58,7 +60,7 @@ func getFeatureSetFor(ed filedesc.Edition) *descriptorpb.FeatureSet { return def } edpb := toEditionProto(ed) - if defaults.GetMinimumEdition() > edpb || defaults.GetMaximumEdition() < edpb { + if (defaults.GetMinimumEdition() > edpb || defaults.GetMaximumEdition() < edpb) && edpb != descriptorpb.Edition_EDITION_UNSTABLE { // This should never happen protodesc.(FileOptions).New would fail when // initializing the file descriptor. // This most likely means the embedded defaults were not updated. diff --git a/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go b/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go index 4eacb523c3..0b23faa957 100644 --- a/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go +++ b/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go @@ -69,6 +69,8 @@ const ( // comparison. Edition_EDITION_2023 Edition = 1000 Edition_EDITION_2024 Edition = 1001 + // A placeholder edition for developing and testing unscheduled features. + Edition_EDITION_UNSTABLE Edition = 9999 // Placeholder editions for testing feature resolution. These should not be // used or relied on outside of tests. Edition_EDITION_1_TEST_ONLY Edition = 1 @@ -91,6 +93,7 @@ var ( 999: "EDITION_PROTO3", 1000: "EDITION_2023", 1001: "EDITION_2024", + 9999: "EDITION_UNSTABLE", 1: "EDITION_1_TEST_ONLY", 2: "EDITION_2_TEST_ONLY", 99997: "EDITION_99997_TEST_ONLY", @@ -105,6 +108,7 @@ var ( "EDITION_PROTO3": 999, "EDITION_2023": 1000, "EDITION_2024": 1001, + "EDITION_UNSTABLE": 9999, "EDITION_1_TEST_ONLY": 1, "EDITION_2_TEST_ONLY": 2, "EDITION_99997_TEST_ONLY": 99997, @@ -4793,11 +4797,11 @@ const file_google_protobuf_descriptor_proto_rawDesc = "" + "\x18EnumValueDescriptorProto\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n" + "\x06number\x18\x02 \x01(\x05R\x06number\x12;\n" + - "\aoptions\x18\x03 \x01(\v2!.google.protobuf.EnumValueOptionsR\aoptions\"\xa7\x01\n" + + "\aoptions\x18\x03 \x01(\v2!.google.protobuf.EnumValueOptionsR\aoptions\"\xb5\x01\n" + "\x16ServiceDescriptorProto\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12>\n" + "\x06method\x18\x02 \x03(\v2&.google.protobuf.MethodDescriptorProtoR\x06method\x129\n" + - "\aoptions\x18\x03 \x01(\v2\x1f.google.protobuf.ServiceOptionsR\aoptions\"\x89\x02\n" + + "\aoptions\x18\x03 \x01(\v2\x1f.google.protobuf.ServiceOptionsR\aoptionsJ\x04\b\x04\x10\x05R\x06stream\"\x89\x02\n" + "\x15MethodDescriptorProto\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n" + "\n" + @@ -5033,14 +5037,15 @@ const file_google_protobuf_descriptor_proto_rawDesc = "" + "\bSemantic\x12\b\n" + "\x04NONE\x10\x00\x12\a\n" + "\x03SET\x10\x01\x12\t\n" + - "\x05ALIAS\x10\x02*\xa7\x02\n" + + "\x05ALIAS\x10\x02*\xbe\x02\n" + "\aEdition\x12\x13\n" + "\x0fEDITION_UNKNOWN\x10\x00\x12\x13\n" + "\x0eEDITION_LEGACY\x10\x84\a\x12\x13\n" + "\x0eEDITION_PROTO2\x10\xe6\a\x12\x13\n" + "\x0eEDITION_PROTO3\x10\xe7\a\x12\x11\n" + "\fEDITION_2023\x10\xe8\a\x12\x11\n" + - "\fEDITION_2024\x10\xe9\a\x12\x17\n" + + "\fEDITION_2024\x10\xe9\a\x12\x15\n" + + "\x10EDITION_UNSTABLE\x10\x8fN\x12\x17\n" + "\x13EDITION_1_TEST_ONLY\x10\x01\x12\x17\n" + "\x13EDITION_2_TEST_ONLY\x10\x02\x12\x1d\n" + "\x17EDITION_99997_TEST_ONLY\x10\x9d\x8d\x06\x12\x1d\n" + diff --git a/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go b/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go index 06d584c14b..484c21fd53 100644 --- a/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go +++ b/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go @@ -172,13 +172,14 @@ import ( // ) to obtain a formatter capable of generating timestamps in this format. type Timestamp struct { state protoimpl.MessageState `protogen:"open.v1"` - // Represents seconds of UTC time since Unix epoch - // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to - // 9999-12-31T23:59:59Z inclusive. + // Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must + // be between -315576000000 and 315576000000 inclusive (which corresponds to + // 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z). Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` - // Non-negative fractions of a second at nanosecond resolution. Negative - // second values with fractions must still have non-negative nanos values - // that count forward in time. Must be from 0 to 999,999,999 + // Non-negative fractions of a second at nanosecond resolution. This field is + // the nanosecond portion of the duration, not an alternative to seconds. + // Negative second values with fractions must still have non-negative nanos + // values that count forward in time. Must be between 0 and 999,999,999 // inclusive. Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` unknownFields protoimpl.UnknownFields diff --git a/vendor/k8s.io/kube-openapi/pkg/validation/spec/ref.go b/vendor/k8s.io/kube-openapi/pkg/validation/spec/ref.go index 775b3b0c36..29cec61930 100644 --- a/vendor/k8s.io/kube-openapi/pkg/validation/spec/ref.go +++ b/vendor/k8s.io/kube-openapi/pkg/validation/spec/ref.go @@ -16,10 +16,6 @@ package spec import ( "encoding/json" - "net/http" - "os" - "path/filepath" - "github.com/go-openapi/jsonreference" "k8s.io/kube-openapi/pkg/internal" @@ -56,52 +52,6 @@ func (r *Ref) RemoteURI() string { return u.String() } -// IsValidURI returns true when the url the ref points to can be found -func (r *Ref) IsValidURI(basepaths ...string) bool { - if r.String() == "" { - return true - } - - v := r.RemoteURI() - if v == "" { - return true - } - - if r.HasFullURL { - rr, err := http.Get(v) - if err != nil { - return false - } - - return rr.StatusCode/100 == 2 - } - - if !(r.HasFileScheme || r.HasFullFilePath || r.HasURLPathOnly) { - return false - } - - // check for local file - pth := v - if r.HasURLPathOnly { - base := "." - if len(basepaths) > 0 { - base = filepath.Dir(filepath.Join(basepaths...)) - } - p, e := filepath.Abs(filepath.ToSlash(filepath.Join(base, pth))) - if e != nil { - return false - } - pth = p - } - - fi, err := os.Stat(filepath.ToSlash(pth)) - if err != nil { - return false - } - - return !fi.IsDir() -} - // Inherits creates a new reference from a parent and a child // If the child cannot inherit from the parent, an error is returned func (r *Ref) Inherits(child Ref) (*Ref, error) { diff --git a/vendor/modules.txt b/vendor/modules.txt index 4238fe93d5..a738e8abae 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# cel.dev/expr v0.25.0 +# cel.dev/expr v0.25.1 ## explicit; go 1.23.0 cel.dev/expr # cyphar.com/go-pathrs v0.2.1 @@ -219,50 +219,50 @@ github.com/go-logr/stdr # github.com/go-logr/zapr v1.3.0 ## explicit; go 1.18 github.com/go-logr/zapr -# github.com/go-openapi/jsonpointer v0.22.1 +# github.com/go-openapi/jsonpointer v0.22.4 ## explicit; go 1.24.0 github.com/go-openapi/jsonpointer -# github.com/go-openapi/jsonreference v0.21.3 +# github.com/go-openapi/jsonreference v0.21.4 ## explicit; go 1.24.0 github.com/go-openapi/jsonreference github.com/go-openapi/jsonreference/internal -# github.com/go-openapi/swag v0.25.1 +# github.com/go-openapi/swag v0.25.4 ## explicit; go 1.24.0 github.com/go-openapi/swag -# github.com/go-openapi/swag/cmdutils v0.25.1 +# github.com/go-openapi/swag/cmdutils v0.25.4 ## explicit; go 1.24.0 github.com/go-openapi/swag/cmdutils -# github.com/go-openapi/swag/conv v0.25.1 +# github.com/go-openapi/swag/conv v0.25.4 ## explicit; go 1.24.0 github.com/go-openapi/swag/conv -# github.com/go-openapi/swag/fileutils v0.25.1 +# github.com/go-openapi/swag/fileutils v0.25.4 ## explicit; go 1.24.0 github.com/go-openapi/swag/fileutils -# github.com/go-openapi/swag/jsonname v0.25.1 +# github.com/go-openapi/swag/jsonname v0.25.4 ## explicit; go 1.24.0 github.com/go-openapi/swag/jsonname -# github.com/go-openapi/swag/jsonutils v0.25.1 +# github.com/go-openapi/swag/jsonutils v0.25.4 ## explicit; go 1.24.0 github.com/go-openapi/swag/jsonutils github.com/go-openapi/swag/jsonutils/adapters github.com/go-openapi/swag/jsonutils/adapters/ifaces github.com/go-openapi/swag/jsonutils/adapters/stdlib/json -# github.com/go-openapi/swag/loading v0.25.1 +# github.com/go-openapi/swag/loading v0.25.4 ## explicit; go 1.24.0 github.com/go-openapi/swag/loading -# github.com/go-openapi/swag/mangling v0.25.1 +# github.com/go-openapi/swag/mangling v0.25.4 ## explicit; go 1.24.0 github.com/go-openapi/swag/mangling -# github.com/go-openapi/swag/netutils v0.25.1 +# github.com/go-openapi/swag/netutils v0.25.4 ## explicit; go 1.24.0 github.com/go-openapi/swag/netutils -# github.com/go-openapi/swag/stringutils v0.25.1 +# github.com/go-openapi/swag/stringutils v0.25.4 ## explicit; go 1.24.0 github.com/go-openapi/swag/stringutils -# github.com/go-openapi/swag/typeutils v0.25.1 +# github.com/go-openapi/swag/typeutils v0.25.4 ## explicit; go 1.24.0 github.com/go-openapi/swag/typeutils -# github.com/go-openapi/swag/yamlutils v0.25.1 +# github.com/go-openapi/swag/yamlutils v0.25.4 ## explicit; go 1.24.0 github.com/go-openapi/swag/yamlutils # github.com/gogo/protobuf v1.3.2 @@ -275,8 +275,8 @@ github.com/golang/mock/gomock # github.com/google/btree v1.1.3 ## explicit; go 1.18 github.com/google/btree -# github.com/google/cel-go v0.26.1 -## explicit; go 1.22.0 +# github.com/google/cel-go v0.27.0 +## explicit; go 1.23.0 github.com/google/cel-go/cel github.com/google/cel-go/checker github.com/google/cel-go/checker/decls @@ -300,7 +300,7 @@ github.com/google/cel-go/interpreter github.com/google/cel-go/interpreter/functions github.com/google/cel-go/parser github.com/google/cel-go/parser/gen -# github.com/google/gnostic-models v0.7.0 +# github.com/google/gnostic-models v0.7.1 ## explicit; go 1.22 github.com/google/gnostic-models/compiler github.com/google/gnostic-models/extensions @@ -317,7 +317,7 @@ github.com/google/go-cmp/cmp/internal/value # github.com/google/uuid v1.6.0 ## explicit github.com/google/uuid -# github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 +# github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 ## explicit; go 1.24.0 github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule github.com/grpc-ecosystem/grpc-gateway/v2/runtime @@ -571,8 +571,8 @@ github.com/openshift/machine-config-operator/pkg/apihelpers github.com/openshift/machine-config-operator/pkg/controller/common github.com/openshift/machine-config-operator/pkg/daemon/constants github.com/openshift/machine-config-operator/pkg/version -# github.com/operator-framework/api v0.37.0 -## explicit; go 1.24.6 +# github.com/operator-framework/api v0.42.0 +## explicit; go 1.25.7 github.com/operator-framework/api/pkg/lib/release github.com/operator-framework/api/pkg/lib/version github.com/operator-framework/api/pkg/operators @@ -611,7 +611,7 @@ github.com/prometheus/client_golang/prometheus/promhttp/internal # github.com/prometheus/client_model v0.6.2 ## explicit; go 1.22.0 github.com/prometheus/client_model/go -# github.com/prometheus/common v0.67.2 +# github.com/prometheus/common v0.67.5 ## explicit; go 1.24.0 github.com/prometheus/common/expfmt github.com/prometheus/common/model @@ -649,9 +649,6 @@ github.com/spf13/cobra # github.com/spf13/pflag v1.0.10 ## explicit; go 1.12 github.com/spf13/pflag -# github.com/stoewer/go-strcase v1.3.1 -## explicit; go 1.11 -github.com/stoewer/go-strcase # github.com/stolostron/kubernetes-dependency-watches v0.10.2 ## explicit; go 1.23.0 github.com/stolostron/kubernetes-dependency-watches/client @@ -679,8 +676,8 @@ github.com/x448/float16 ## explicit; go 1.24.0 go.opentelemetry.io/auto/sdk go.opentelemetry.io/auto/sdk/internal/telemetry -# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 -## explicit; go 1.23.0 +# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 +## explicit; go 1.24.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv @@ -698,34 +695,37 @@ go.opentelemetry.io/otel/internal/global go.opentelemetry.io/otel/propagation go.opentelemetry.io/otel/semconv/v1.17.0 go.opentelemetry.io/otel/semconv/v1.37.0 -go.opentelemetry.io/otel/semconv/v1.37.0/httpconv -go.opentelemetry.io/otel/semconv/v1.37.0/otelconv go.opentelemetry.io/otel/semconv/v1.39.0 -# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 -## explicit; go 1.23.0 +go.opentelemetry.io/otel/semconv/v1.39.0/httpconv +go.opentelemetry.io/otel/semconv/v1.39.0/otelconv +# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 +## explicit; go 1.24.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform -# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 -## explicit; go 1.23.0 +# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 +## explicit; go 1.24.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/counter go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/envconfig +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x # go.opentelemetry.io/otel/metric v1.41.0 ## explicit; go 1.24.0 go.opentelemetry.io/otel/metric go.opentelemetry.io/otel/metric/embedded go.opentelemetry.io/otel/metric/noop -# go.opentelemetry.io/otel/sdk v1.38.0 -## explicit; go 1.23.0 +# go.opentelemetry.io/otel/sdk v1.40.0 +## explicit; go 1.24.0 go.opentelemetry.io/otel/sdk go.opentelemetry.io/otel/sdk/instrumentation -go.opentelemetry.io/otel/sdk/internal/env go.opentelemetry.io/otel/sdk/internal/x go.opentelemetry.io/otel/sdk/resource go.opentelemetry.io/otel/sdk/trace -go.opentelemetry.io/otel/sdk/trace/internal/x +go.opentelemetry.io/otel/sdk/trace/internal/env +go.opentelemetry.io/otel/sdk/trace/internal/observ # go.opentelemetry.io/otel/trace v1.41.0 ## explicit; go 1.24.0 go.opentelemetry.io/otel/trace @@ -776,8 +776,8 @@ golang.org/x/crypto/pbkdf2 golang.org/x/crypto/scrypt golang.org/x/crypto/ssh golang.org/x/crypto/ssh/internal/bcrypt_pbkdf -# golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b -## explicit; go 1.23.0 +# golang.org/x/exp v0.0.0-20260112195511-716be5621a96 +## explicit; go 1.24.0 golang.org/x/exp/slices # golang.org/x/mod v0.32.0 ## explicit; go 1.24.0 @@ -864,15 +864,15 @@ golang.org/x/tools/internal/versions # gomodules.xyz/jsonpatch/v2 v2.5.0 ## explicit; go 1.20 gomodules.xyz/jsonpatch/v2 -# google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba +# google.golang.org/genproto/googleapis/api v0.0.0-20260202165425-ce8ad4cf556b ## explicit; go 1.24.0 google.golang.org/genproto/googleapis/api/expr/v1alpha1 google.golang.org/genproto/googleapis/api/httpbody -# google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 +# google.golang.org/genproto/googleapis/rpc v0.0.0-20260202165425-ce8ad4cf556b ## explicit; go 1.24.0 google.golang.org/genproto/googleapis/rpc/errdetails google.golang.org/genproto/googleapis/rpc/status -# google.golang.org/grpc v1.77.0 +# google.golang.org/grpc v1.79.3 ## explicit; go 1.24.0 google.golang.org/grpc google.golang.org/grpc/attributes @@ -901,6 +901,7 @@ google.golang.org/grpc/health/grpc_health_v1 google.golang.org/grpc/internal google.golang.org/grpc/internal/backoff google.golang.org/grpc/internal/balancer/gracefulswitch +google.golang.org/grpc/internal/balancer/weight google.golang.org/grpc/internal/balancerload google.golang.org/grpc/internal/binarylog google.golang.org/grpc/internal/buffer @@ -936,7 +937,7 @@ google.golang.org/grpc/serviceconfig google.golang.org/grpc/stats google.golang.org/grpc/status google.golang.org/grpc/tap -# google.golang.org/protobuf v1.36.10 +# google.golang.org/protobuf v1.36.11 ## explicit; go 1.23 google.golang.org/protobuf/encoding/protodelim google.golang.org/protobuf/encoding/protojson @@ -1549,7 +1550,7 @@ k8s.io/kube-aggregator/pkg/apis/apiregistration/v1 k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1 k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1 -# k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 +# k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 ## explicit; go 1.23.0 k8s.io/kube-openapi/cmd/openapi-gen k8s.io/kube-openapi/cmd/openapi-gen/args @@ -1590,14 +1591,14 @@ open-cluster-management.io/config-policy-controller/api/v1 # open-cluster-management.io/governance-policy-propagator v0.17.0 ## explicit; go 1.24.0 open-cluster-management.io/governance-policy-propagator/api/v1 -# sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.1 +# sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 ## explicit; go 1.21 sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/client/metrics sigs.k8s.io/apiserver-network-proxy/konnectivity-client/pkg/common/metrics sigs.k8s.io/apiserver-network-proxy/konnectivity-client/proto/client -# sigs.k8s.io/controller-runtime v0.22.5 -## explicit; go 1.24.0 +# sigs.k8s.io/controller-runtime v0.23.3 +## explicit; go 1.25.0 sigs.k8s.io/controller-runtime sigs.k8s.io/controller-runtime/pkg/builder sigs.k8s.io/controller-runtime/pkg/cache diff --git a/vendor/sigs.k8s.io/controller-runtime/.golangci.yml b/vendor/sigs.k8s.io/controller-runtime/.golangci.yml index 1741432a01..5c86af65a3 100644 --- a/vendor/sigs.k8s.io/controller-runtime/.golangci.yml +++ b/vendor/sigs.k8s.io/controller-runtime/.golangci.yml @@ -1,6 +1,6 @@ version: "2" run: - go: "1.24" + go: "1.25" timeout: 10m allow-parallel-runners: true linters: @@ -11,6 +11,7 @@ linters: - bidichk - bodyclose - copyloopvar + - depguard - dogsled - dupl - errcheck @@ -22,12 +23,15 @@ linters: - goconst - gocritic - gocyclo + - godoclint - goprintffuncname - govet - importas - ineffassign + - iotamixing - makezero - misspell + - modernize - nakedret - nilerr - nolintlint @@ -40,6 +44,12 @@ linters: - unused - whitespace settings: + depguard: + rules: + forbid-pkg-errors: + deny: + - pkg: sort + desc: Should be replaced with slices package forbidigo: forbid: - pattern: context.Background @@ -50,6 +60,7 @@ linters: disable: - fieldalignment - shadow + - buildtag enable-all: true importas: alias: @@ -66,6 +77,10 @@ linters: - pkg: sigs.k8s.io/controller-runtime alias: ctrl no-unaliased: true + modernize: + disable: + - omitzero + - fmtappendf revive: rules: # The following rules are recommended https://github.com/mgechev/revive#recommended-configuration diff --git a/vendor/sigs.k8s.io/controller-runtime/.gomodcheck.yaml b/vendor/sigs.k8s.io/controller-runtime/.gomodcheck.yaml index 3608de331d..3eaff8dc47 100644 --- a/vendor/sigs.k8s.io/controller-runtime/.gomodcheck.yaml +++ b/vendor/sigs.k8s.io/controller-runtime/.gomodcheck.yaml @@ -9,6 +9,10 @@ upstreamRefs: # k8s.io/utils -> conflicts with k/k deps excludedModules: + # Needs a newer version to fix https://github.com/kubernetes-sigs/controller-runtime/issues/3418 + # This should not be needed by the time we update to 1.36 + - sigs.k8s.io/structured-merge-diff/v6 + # --- test dependencies: - github.com/onsi/ginkgo/v2 - github.com/onsi/gomega diff --git a/vendor/sigs.k8s.io/controller-runtime/Makefile b/vendor/sigs.k8s.io/controller-runtime/Makefile index b8e9cfa877..1c1fb7f429 100644 --- a/vendor/sigs.k8s.io/controller-runtime/Makefile +++ b/vendor/sigs.k8s.io/controller-runtime/Makefile @@ -27,7 +27,7 @@ SHELL:=/usr/bin/env bash # # Go. # -GO_VERSION ?= 1.24.0 +GO_VERSION ?= 1.25.0 # Use GOPROXY environment variable if set GOPROXY := $(shell go env GOPROXY) @@ -69,18 +69,14 @@ help: ## Display this help ## -------------------------------------- .PHONY: test -test: test-tools ## Run the script check-everything.sh which will check all. +test: ## Run the script check-everything.sh which will check all. TRACE=1 ./hack/check-everything.sh -.PHONY: test-tools -test-tools: ## tests the tools codebase (setup-envtest) - cd tools/setup-envtest && go test ./... - ## -------------------------------------- ## Binaries ## -------------------------------------- -GO_APIDIFF_VER := v0.8.2 +GO_APIDIFF_VER := v0.8.3 GO_APIDIFF_BIN := go-apidiff GO_APIDIFF := $(abspath $(TOOLS_BIN_DIR)/$(GO_APIDIFF_BIN)-$(GO_APIDIFF_VER)) GO_APIDIFF_PKG := github.com/joelanford/go-apidiff @@ -88,7 +84,7 @@ GO_APIDIFF_PKG := github.com/joelanford/go-apidiff $(GO_APIDIFF): # Build go-apidiff from tools folder. GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) $(GO_APIDIFF_PKG) $(GO_APIDIFF_BIN) $(GO_APIDIFF_VER) -CONTROLLER_GEN_VER := v0.17.1 +CONTROLLER_GEN_VER := v0.20.0 CONTROLLER_GEN_BIN := controller-gen CONTROLLER_GEN := $(abspath $(TOOLS_BIN_DIR)/$(CONTROLLER_GEN_BIN)-$(CONTROLLER_GEN_VER)) CONTROLLER_GEN_PKG := sigs.k8s.io/controller-tools/cmd/controller-gen diff --git a/vendor/sigs.k8s.io/controller-runtime/README.md b/vendor/sigs.k8s.io/controller-runtime/README.md index 54bacad42e..8549f4e880 100644 --- a/vendor/sigs.k8s.io/controller-runtime/README.md +++ b/vendor/sigs.k8s.io/controller-runtime/README.md @@ -53,6 +53,7 @@ Compatible k8s.io/*, client-go and minimum Go versions can be looked up in our [ | | k8s.io/*, client-go | minimum Go version | |----------|:-------------------:|:------------------:| +| CR v0.22 | v0.34 | 1.24 | | CR v0.21 | v0.33 | 1.24 | | CR v0.20 | v0.32 | 1.23 | | CR v0.19 | v0.31 | 1.22 | diff --git a/vendor/sigs.k8s.io/controller-runtime/alias.go b/vendor/sigs.k8s.io/controller-runtime/alias.go index 01ba012dcc..e2ac45a5e0 100644 --- a/vendor/sigs.k8s.io/controller-runtime/alias.go +++ b/vendor/sigs.k8s.io/controller-runtime/alias.go @@ -18,6 +18,7 @@ package controllerruntime import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client/config" @@ -104,15 +105,20 @@ var ( // NewControllerManagedBy returns a new controller builder that will be started by the provided Manager. NewControllerManagedBy = builder.ControllerManagedBy - // NewWebhookManagedBy returns a new webhook builder that will be started by the provided Manager. - NewWebhookManagedBy = builder.WebhookManagedBy - // NewManager returns a new Manager for creating Controllers. // Note that if ContentType in the given config is not set, "application/vnd.kubernetes.protobuf" // will be used for all built-in resources of Kubernetes, and "application/json" is for other types // including all CRD resources. NewManager = manager.New + // CreateOrPatch creates or patches the given object obj in the Kubernetes + // cluster. The object's desired state should be reconciled with the existing + // state using the passed in ReconcileFn. obj must be a struct pointer so that + // obj can be patched with the content returned by the Server. + // + // It returns the executed operation and an error. + CreateOrPatch = controllerutil.CreateOrPatch + // CreateOrUpdate creates or updates the given object obj in the Kubernetes // cluster. The object's desired state should be reconciled with the existing // state using the passed in ReconcileFn. obj must be a struct pointer so that @@ -155,3 +161,8 @@ var ( // SetLogger sets a concrete logging implementation for all deferred Loggers. SetLogger = log.SetLogger ) + +// NewWebhookManagedBy returns a new webhook builder for the provided type T. +func NewWebhookManagedBy[T runtime.Object](mgr manager.Manager, obj T) *builder.WebhookBuilder[T] { + return builder.WebhookManagedBy(mgr, obj) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go index 6d906f6e52..840e27b679 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/controller.go @@ -312,7 +312,7 @@ func (blder *TypedBuilder[request]) doWatch() error { return err } - if reflect.TypeFor[request]() != reflect.TypeOf(reconcile.Request{}) { + if reflect.TypeFor[request]() != reflect.TypeFor[reconcile.Request]() { return fmt.Errorf("For() can only be used with reconcile.Request, got %T", *new(request)) } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go index 6263f030a0..d9c57c5e8b 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/builder/webhook.go @@ -17,6 +17,7 @@ limitations under the License. package builder import ( + "context" "errors" "net/http" "net/url" @@ -36,63 +37,84 @@ import ( ) // WebhookBuilder builds a Webhook. -type WebhookBuilder struct { +type WebhookBuilder[T runtime.Object] struct { apiType runtime.Object - customDefaulter admission.CustomDefaulter + customDefaulter admission.CustomDefaulter //nolint:staticcheck + defaulter admission.Defaulter[T] customDefaulterOpts []admission.DefaulterOption - customValidator admission.CustomValidator + customValidator admission.CustomValidator //nolint:staticcheck + validator admission.Validator[T] customPath string customValidatorCustomPath string customDefaulterCustomPath string + converterConstructor func(*runtime.Scheme) (conversion.Converter, error) gvk schema.GroupVersionKind mgr manager.Manager config *rest.Config recoverPanic *bool logConstructor func(base logr.Logger, req *admission.Request) logr.Logger + contextFunc func(context.Context, *http.Request) context.Context err error } // WebhookManagedBy returns a new webhook builder. -func WebhookManagedBy(m manager.Manager) *WebhookBuilder { - return &WebhookBuilder{mgr: m} +func WebhookManagedBy[T runtime.Object](m manager.Manager, object T) *WebhookBuilder[T] { + return &WebhookBuilder[T]{mgr: m, apiType: object} } -// TODO(droot): update the GoDoc for conversion. - -// For takes a runtime.Object which should be a CR. -// If the given object implements the admission.Defaulter interface, a MutatingWebhook will be wired for this type. -// If the given object implements the admission.Validator interface, a ValidatingWebhook will be wired for this type. -func (blder *WebhookBuilder) For(apiType runtime.Object) *WebhookBuilder { - if blder.apiType != nil { - blder.err = errors.New("For(...) should only be called once, could not assign multiple objects for webhook registration") - } - blder.apiType = apiType +// WithCustomDefaulter takes an admission.CustomDefaulter interface, a MutatingWebhook with the provided opts (admission.DefaulterOption) +// will be wired for this type. +// +// Deprecated: Use WithDefaulter instead. +func (blder *WebhookBuilder[T]) WithCustomDefaulter(defaulter admission.CustomDefaulter, opts ...admission.DefaulterOption) *WebhookBuilder[T] { + blder.customDefaulter = defaulter + blder.customDefaulterOpts = opts return blder } -// WithDefaulter takes an admission.CustomDefaulter interface, a MutatingWebhook with the provided opts (admission.DefaulterOption) -// will be wired for this type. -func (blder *WebhookBuilder) WithDefaulter(defaulter admission.CustomDefaulter, opts ...admission.DefaulterOption) *WebhookBuilder { - blder.customDefaulter = defaulter +// WithDefaulter sets up the provided admission.Defaulter in a defaulting webhook. +func (blder *WebhookBuilder[T]) WithDefaulter(defaulter admission.Defaulter[T], opts ...admission.DefaulterOption) *WebhookBuilder[T] { + blder.defaulter = defaulter blder.customDefaulterOpts = opts return blder } -// WithValidator takes a admission.CustomValidator interface, a ValidatingWebhook will be wired for this type. -func (blder *WebhookBuilder) WithValidator(validator admission.CustomValidator) *WebhookBuilder { +// WithCustomValidator takes a admission.CustomValidator interface, a ValidatingWebhook will be wired for this type. +// +// Deprecated: Use WithValidator instead. +func (blder *WebhookBuilder[T]) WithCustomValidator(validator admission.CustomValidator) *WebhookBuilder[T] { blder.customValidator = validator return blder } +// WithValidator sets up the provided admission.Validator in a validating webhook. +func (blder *WebhookBuilder[T]) WithValidator(validator admission.Validator[T]) *WebhookBuilder[T] { + blder.validator = validator + return blder +} + +// WithConverter takes a func that constructs a converter.Converter. +// The Converter will then be used by the conversion endpoint for the type passed into NewWebhookManagedBy() +func (blder *WebhookBuilder[T]) WithConverter(converterConstructor func(*runtime.Scheme) (conversion.Converter, error)) *WebhookBuilder[T] { + blder.converterConstructor = converterConstructor + return blder +} + // WithLogConstructor overrides the webhook's LogConstructor. -func (blder *WebhookBuilder) WithLogConstructor(logConstructor func(base logr.Logger, req *admission.Request) logr.Logger) *WebhookBuilder { +func (blder *WebhookBuilder[T]) WithLogConstructor(logConstructor func(base logr.Logger, req *admission.Request) logr.Logger) *WebhookBuilder[T] { blder.logConstructor = logConstructor return blder } +// WithContextFunc overrides the webhook's WithContextFunc. +func (blder *WebhookBuilder[T]) WithContextFunc(contextFunc func(context.Context, *http.Request) context.Context) *WebhookBuilder[T] { + blder.contextFunc = contextFunc + return blder +} + // RecoverPanic indicates whether panics caused by the webhook should be recovered. // Defaults to true. -func (blder *WebhookBuilder) RecoverPanic(recoverPanic bool) *WebhookBuilder { +func (blder *WebhookBuilder[T]) RecoverPanic(recoverPanic bool) *WebhookBuilder[T] { blder.recoverPanic = &recoverPanic return blder } @@ -101,25 +123,25 @@ func (blder *WebhookBuilder) RecoverPanic(recoverPanic bool) *WebhookBuilder { // // Deprecated: WithCustomPath should not be used anymore. // Please use WithValidatorCustomPath or WithDefaulterCustomPath instead. -func (blder *WebhookBuilder) WithCustomPath(customPath string) *WebhookBuilder { +func (blder *WebhookBuilder[T]) WithCustomPath(customPath string) *WebhookBuilder[T] { blder.customPath = customPath return blder } // WithValidatorCustomPath overrides the path of the Validator. -func (blder *WebhookBuilder) WithValidatorCustomPath(customPath string) *WebhookBuilder { +func (blder *WebhookBuilder[T]) WithValidatorCustomPath(customPath string) *WebhookBuilder[T] { blder.customValidatorCustomPath = customPath return blder } // WithDefaulterCustomPath overrides the path of the Defaulter. -func (blder *WebhookBuilder) WithDefaulterCustomPath(customPath string) *WebhookBuilder { +func (blder *WebhookBuilder[T]) WithDefaulterCustomPath(customPath string) *WebhookBuilder[T] { blder.customDefaulterCustomPath = customPath return blder } // Complete builds the webhook. -func (blder *WebhookBuilder) Complete() error { +func (blder *WebhookBuilder[T]) Complete() error { // Set the Config blder.loadRestConfig() @@ -130,13 +152,13 @@ func (blder *WebhookBuilder) Complete() error { return blder.registerWebhooks() } -func (blder *WebhookBuilder) loadRestConfig() { +func (blder *WebhookBuilder[T]) loadRestConfig() { if blder.config == nil { blder.config = blder.mgr.GetConfig() } } -func (blder *WebhookBuilder) setLogConstructor() { +func (blder *WebhookBuilder[T]) setLogConstructor() { if blder.logConstructor == nil { blder.logConstructor = func(base logr.Logger, req *admission.Request) logr.Logger { log := base.WithValues( @@ -156,11 +178,11 @@ func (blder *WebhookBuilder) setLogConstructor() { } } -func (blder *WebhookBuilder) isThereCustomPathConflict() bool { +func (blder *WebhookBuilder[T]) isThereCustomPathConflict() bool { return (blder.customPath != "" && blder.customDefaulter != nil && blder.customValidator != nil) || (blder.customPath != "" && blder.customDefaulterCustomPath != "") || (blder.customPath != "" && blder.customValidatorCustomPath != "") } -func (blder *WebhookBuilder) registerWebhooks() error { +func (blder *WebhookBuilder[T]) registerWebhooks() error { typ, err := blder.getType() if err != nil { return err @@ -201,10 +223,14 @@ func (blder *WebhookBuilder) registerWebhooks() error { } // registerDefaultingWebhook registers a defaulting webhook if necessary. -func (blder *WebhookBuilder) registerDefaultingWebhook() error { - mwh := blder.getDefaultingWebhook() +func (blder *WebhookBuilder[T]) registerDefaultingWebhook() error { + mwh, err := blder.getDefaultingWebhook() + if err != nil { + return err + } if mwh != nil { mwh.LogConstructor = blder.logConstructor + mwh.WithContextFunc = blder.contextFunc path := generateMutatePath(blder.gvk) if blder.customDefaulterCustomPath != "" { generatedCustomPath, err := generateCustomPath(blder.customDefaulterCustomPath) @@ -227,22 +253,31 @@ func (blder *WebhookBuilder) registerDefaultingWebhook() error { return nil } -func (blder *WebhookBuilder) getDefaultingWebhook() *admission.Webhook { - if defaulter := blder.customDefaulter; defaulter != nil { - w := admission.WithCustomDefaulter(blder.mgr.GetScheme(), blder.apiType, defaulter, blder.customDefaulterOpts...) - if blder.recoverPanic != nil { - w = w.WithRecoverPanic(*blder.recoverPanic) +func (blder *WebhookBuilder[T]) getDefaultingWebhook() (*admission.Webhook, error) { + var w *admission.Webhook + if blder.defaulter != nil { + if blder.customDefaulter != nil { + return nil, errors.New("only one of Defaulter or CustomDefaulter can be set") } - return w + w = admission.WithDefaulter(blder.mgr.GetScheme(), blder.defaulter, blder.customDefaulterOpts...) + } else if blder.customDefaulter != nil { + w = admission.WithCustomDefaulter(blder.mgr.GetScheme(), blder.apiType, blder.customDefaulter, blder.customDefaulterOpts...) } - return nil + if w != nil && blder.recoverPanic != nil { + w = w.WithRecoverPanic(*blder.recoverPanic) + } + return w, nil } // registerValidatingWebhook registers a validating webhook if necessary. -func (blder *WebhookBuilder) registerValidatingWebhook() error { - vwh := blder.getValidatingWebhook() +func (blder *WebhookBuilder[T]) registerValidatingWebhook() error { + vwh, err := blder.getValidatingWebhook() + if err != nil { + return err + } if vwh != nil { vwh.LogConstructor = blder.logConstructor + vwh.WithContextFunc = blder.contextFunc path := generateValidatePath(blder.gvk) if blder.customValidatorCustomPath != "" { generatedCustomPath, err := generateCustomPath(blder.customValidatorCustomPath) @@ -265,41 +300,60 @@ func (blder *WebhookBuilder) registerValidatingWebhook() error { return nil } -func (blder *WebhookBuilder) getValidatingWebhook() *admission.Webhook { - if validator := blder.customValidator; validator != nil { - w := admission.WithCustomValidator(blder.mgr.GetScheme(), blder.apiType, validator) - if blder.recoverPanic != nil { - w = w.WithRecoverPanic(*blder.recoverPanic) +func (blder *WebhookBuilder[T]) getValidatingWebhook() (*admission.Webhook, error) { + var w *admission.Webhook + if blder.validator != nil { + if blder.customValidator != nil { + return nil, errors.New("only one of Validator or CustomValidator can be set") } - return w + w = admission.WithValidator(blder.mgr.GetScheme(), blder.validator) + } else if blder.customValidator != nil { + //nolint:staticcheck + w = admission.WithCustomValidator(blder.mgr.GetScheme(), blder.apiType, blder.customValidator) } - return nil + if w != nil && blder.recoverPanic != nil { + w = w.WithRecoverPanic(*blder.recoverPanic) + } + return w, nil } -func (blder *WebhookBuilder) registerConversionWebhook() error { - ok, err := conversion.IsConvertible(blder.mgr.GetScheme(), blder.apiType) - if err != nil { - log.Error(err, "conversion check failed", "GVK", blder.gvk) - return err - } - if ok { - if !blder.isAlreadyHandled("/convert") { - blder.mgr.GetWebhookServer().Register("/convert", conversion.NewWebhookHandler(blder.mgr.GetScheme())) +func (blder *WebhookBuilder[T]) registerConversionWebhook() error { + if blder.converterConstructor != nil { + converter, err := blder.converterConstructor(blder.mgr.GetScheme()) + if err != nil { + return err + } + + if err := blder.mgr.GetConverterRegistry().RegisterConverter(blder.gvk.GroupKind(), converter); err != nil { + return err } - log.Info("Conversion webhook enabled", "GVK", blder.gvk) + } else { + ok, err := conversion.IsConvertible(blder.mgr.GetScheme(), blder.apiType) + if err != nil { + log.Error(err, "conversion check failed", "GVK", blder.gvk) + return err + } + if !ok { + return nil + } + } + + if !blder.isAlreadyHandled("/convert") { + blder.mgr.GetWebhookServer().Register("/convert", conversion.NewWebhookHandler(blder.mgr.GetScheme(), blder.mgr.GetConverterRegistry())) } + log.Info("Conversion webhook enabled", "GVK", blder.gvk) return nil } -func (blder *WebhookBuilder) getType() (runtime.Object, error) { +func (blder *WebhookBuilder[T]) getType() (runtime.Object, error) { if blder.apiType != nil { return blder.apiType, nil } - return nil, errors.New("For() must be called with a valid object") + return nil, errors.New("NewWebhookManagedBy() must be called with a valid object") } -func (blder *WebhookBuilder) isAlreadyHandled(path string) bool { +func (blder *WebhookBuilder[T]) isAlreadyHandled(path string) bool { if blder.mgr.GetWebhookServer().WebhookMux() == nil { return false } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go index a94ec6cc32..b814170de1 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/cache.go @@ -22,7 +22,6 @@ import ( "maps" "net/http" "slices" - "sort" "time" corev1 "k8s.io/api/core/v1" @@ -657,7 +656,8 @@ func defaultConfig(toDefault, defaultFrom Config) Config { func namespaceAllSelector(namespaces []string) []fields.Selector { selectors := make([]fields.Selector, 0, len(namespaces)-1) - sort.Strings(namespaces) + slices.Sort(namespaces) + for _, namespace := range namespaces { if namespace != metav1.NamespaceAll { selectors = append(selectors, fields.OneTermNotEqualSelector("metadata.namespace", namespace)) diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/delegating_by_gvk_cache.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/delegating_by_gvk_cache.go index 46bd243c66..adc5d957a4 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/delegating_by_gvk_cache.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/delegating_by_gvk_cache.go @@ -81,13 +81,11 @@ func (dbt *delegatingByGVKCache) Start(ctx context.Context) error { errs := make(chan error) for idx := range allCaches { cache := allCaches[idx] - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { if err := cache.Start(ctx); err != nil { errs <- err } - }() + }) } select { diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go index 091667b7fa..50dd9a8be1 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/informer_cache.go @@ -51,16 +51,7 @@ var _ error = (*ErrCacheNotStarted)(nil) // ErrResourceNotCached indicates that the resource type // the client asked the cache for is not cached, i.e. the // corresponding informer does not exist yet. -type ErrResourceNotCached struct { - GVK schema.GroupVersionKind -} - -// Error returns the error -func (r ErrResourceNotCached) Error() string { - return fmt.Sprintf("%s is not cached", r.GVK.String()) -} - -var _ error = (*ErrResourceNotCached)(nil) +type ErrResourceNotCached = internal.ErrResourceNotCached // informerCache is a Kubernetes Object cache populated from internal.Informers. // informerCache wraps internal.Informers. @@ -157,7 +148,7 @@ func (ic *informerCache) GetInformerForKind(ctx context.Context, gvk schema.Grou return nil, err } - _, i, err := ic.Informers.Get(ctx, gvk, obj, applyGetOptions(opts...)) + _, i, err := ic.Informers.Get(ctx, gvk, obj, false, applyGetOptions(opts...)) if err != nil { return nil, err } @@ -171,7 +162,7 @@ func (ic *informerCache) GetInformer(ctx context.Context, obj client.Object, opt return nil, err } - _, i, err := ic.Informers.Get(ctx, gvk, obj, applyGetOptions(opts...)) + _, i, err := ic.Informers.Get(ctx, gvk, obj, false, applyGetOptions(opts...)) if err != nil { return nil, err } @@ -179,15 +170,11 @@ func (ic *informerCache) GetInformer(ctx context.Context, obj client.Object, opt } func (ic *informerCache) getInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object) (bool, *internal.Cache, error) { - if ic.readerFailOnMissingInformer { - cache, started, ok := ic.Informers.Peek(gvk, obj) - if !ok { - return false, nil, &ErrResourceNotCached{GVK: gvk} - } - return started, cache, nil + started, cache, err := ic.Informers.Get(ctx, gvk, obj, ic.readerFailOnMissingInformer, &internal.GetOptions{}) + if err != nil { + return false, nil, err } - - return ic.Informers.Get(ctx, gvk, obj, &internal.GetOptions{}) + return started, cache, nil } // RemoveInformer deactivates and removes the informer from the cache. @@ -221,7 +208,7 @@ func (ic *informerCache) IndexField(ctx context.Context, obj client.Object, fiel } func indexByField(informer Informer, field string, extractValue client.IndexerFunc) error { - indexFunc := func(objRaw interface{}) ([]string, error) { + indexFunc := func(objRaw any) ([]string, error) { // TODO(directxman12): check if this is the correct type? obj, isObj := objRaw.(client.Object) if !isObj { diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go index eb6b544855..624869f590 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "reflect" + "slices" apierrors "k8s.io/apimachinery/pkg/api/errors" apimeta "k8s.io/apimachinery/pkg/api/meta" @@ -109,7 +110,7 @@ func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Ob // List lists items out of the indexer and writes them to out. func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...client.ListOption) error { - var objs []interface{} + var objs []any var err error listOpts := client.ListOptions{} @@ -186,10 +187,10 @@ func (c *CacheReader) List(_ context.Context, out client.ObjectList, opts ...cli return nil } -func byIndexes(indexer cache.Indexer, requires fields.Requirements, namespace string) ([]interface{}, error) { +func byIndexes(indexer cache.Indexer, requires fields.Requirements, namespace string) ([]any, error) { var ( err error - objs []interface{} + objs []any vals []string ) indexers := indexer.GetIndexers() @@ -213,17 +214,14 @@ func byIndexes(indexer cache.Indexer, requires fields.Requirements, namespace st if !exist { return nil, fmt.Errorf("index with name %s does not exist", indexName) } - filteredObjects := make([]interface{}, 0, len(objs)) + filteredObjects := make([]any, 0, len(objs)) for _, obj := range objs { vals, err = fn(obj) if err != nil { return nil, err } - for _, val := range vals { - if val == indexedValue { - filteredObjects = append(filteredObjects, obj) - break - } + if slices.Contains(vals, indexedValue) { + filteredObjects = append(filteredObjects, obj) } } if len(filteredObjects) == 0 { diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go index f216be0d9e..619e36abd3 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cache/internal/informers.go @@ -45,6 +45,20 @@ import ( var log = logf.RuntimeLog.WithName("cache") +// ErrResourceNotCached indicates that the resource type +// the client asked the cache for is not cached, i.e. the +// corresponding informer does not exist yet. +type ErrResourceNotCached struct { + GVK schema.GroupVersionKind +} + +// Error returns the error +func (r ErrResourceNotCached) Error() string { + return fmt.Sprintf("%s is not cached", r.GVK.String()) +} + +var _ error = (*ErrResourceNotCached)(nil) + // InformersOpts configures an InformerMap. type InformersOpts struct { HTTPClient *http.Client @@ -242,11 +256,9 @@ func (ip *Informers) startInformerLocked(cacheEntry *Cache) { return } - ip.waitGroup.Add(1) - go func() { - defer ip.waitGroup.Done() + ip.waitGroup.Go(func() { cacheEntry.Start(ip.ctx.Done()) - }() + }) } func (ip *Informers) waitForStarted(ctx context.Context) bool { @@ -296,10 +308,13 @@ func (ip *Informers) Peek(gvk schema.GroupVersionKind, obj runtime.Object) (res // Get will create a new Informer and add it to the map of specificInformersMap if none exists. Returns // the Informer from the map. -func (ip *Informers) Get(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object, opts *GetOptions) (bool, *Cache, error) { +func (ip *Informers) Get(ctx context.Context, gvk schema.GroupVersionKind, obj runtime.Object, readerFailOnMissingInformer bool, opts *GetOptions) (bool, *Cache, error) { // Return the informer if it is found i, started, ok := ip.Peek(gvk, obj) if !ok { + if readerFailOnMissingInformer { + return false, nil, &ErrResourceNotCached{GVK: gvk} + } var err error if i, started, err = ip.addInformerToMap(gvk, obj); err != nil { return started, nil, err diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go index b132cb2d4d..217990dece 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/apimachinery.go @@ -231,7 +231,7 @@ func (t targetZeroingDecoder) Decode(data []byte, defaults *schema.GroupVersionK } // zero zeros the value of a pointer. -func zero(x interface{}) { +func zero(x any) { if x == nil { return } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/errors.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/errors.go index c216c49d2a..b00e071232 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/errors.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/apiutil/errors.go @@ -18,7 +18,7 @@ package apiutil import ( "fmt" - "sort" + "slices" "strings" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -38,7 +38,7 @@ func (e *ErrResourceDiscoveryFailed) Error() string { for k, v := range *e { subErrors = append(subErrors, fmt.Sprintf("%s: %v", k, v)) } - sort.Strings(subErrors) + slices.Sort(subErrors) return fmt.Sprintf("unable to retrieve the complete list of server APIs: %s", strings.Join(subErrors, ", ")) } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go index e9f731453b..ad946daeaa 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/client.go @@ -52,6 +52,25 @@ type Options struct { // DryRun instructs the client to only perform dry run requests. DryRun *bool + + // FieldOwner, if provided, sets the default field manager for all write operations + // (Create, Update, Patch, Apply) performed by this client. The field manager is used by + // the server for Server-Side Apply to track field ownership. + // For more details, see: https://kubernetes.io/docs/reference/using-api/server-side-apply/#field-management + // + // This default can be overridden for a specific call by passing a [FieldOwner] option + // to the method. + FieldOwner string + + // FieldValidation sets the field validation strategy for all mutating operations performed by this client + // and subresource clients created from it. + // The exception are apply requests which are always strict, regardless of the FieldValidation setting. + // Available values for this option can be found in "k8s.io/apimachinery/pkg/apis/meta/v1" package and are: + // - FieldValidationIgnore + // - FieldValidationWarn + // - FieldValidationStrict + // For more details, see: https://kubernetes.io/docs/reference/using-api/api-concepts/#field-validation + FieldValidation string } // CacheOptions are options for creating a cache-backed client. @@ -99,6 +118,13 @@ func New(config *rest.Config, options Options) (c Client, err error) { if err == nil && options.DryRun != nil && *options.DryRun { c = NewDryRunClient(c) } + if fo := options.FieldOwner; fo != "" { + c = WithFieldOwner(c, fo) + } + if fv := options.FieldValidation; fv != "" { + c = WithFieldValidation(c, FieldValidation(fv)) + } + return c, err } @@ -543,6 +569,30 @@ func (po *SubResourcePatchOptions) ApplyToSubResourcePatch(o *SubResourcePatchOp } } +// SubResourceApplyOptions are the options for a subresource +// apply request. +type SubResourceApplyOptions struct { + ApplyOptions + SubResourceBody runtime.ApplyConfiguration +} + +// ApplyOpts applies the given options. +func (ao *SubResourceApplyOptions) ApplyOpts(opts []SubResourceApplyOption) *SubResourceApplyOptions { + for _, o := range opts { + o.ApplyToSubResourceApply(ao) + } + + return ao +} + +// ApplyToSubResourceApply applies the configuration on the given patch options. +func (ao *SubResourceApplyOptions) ApplyToSubResourceApply(o *SubResourceApplyOptions) { + ao.ApplyOptions.ApplyToApply(&o.ApplyOptions) + if ao.SubResourceBody != nil { + o.SubResourceBody = ao.SubResourceBody + } +} + func (sc *subResourceClient) Get(ctx context.Context, obj Object, subResource Object, opts ...SubResourceGetOption) error { switch obj.(type) { case runtime.Unstructured: @@ -594,3 +644,13 @@ func (sc *subResourceClient) Patch(ctx context.Context, obj Object, patch Patch, return sc.client.typedClient.PatchSubResource(ctx, obj, sc.subResource, patch, opts...) } } + +func (sc *subResourceClient) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...SubResourceApplyOption) error { + switch obj := obj.(type) { + case *unstructuredApplyConfiguration: + defer sc.client.resetGroupVersionKind(obj, obj.GetObjectKind().GroupVersionKind()) + return sc.client.unstructuredClient.ApplySubResource(ctx, obj, sc.subResource, opts...) + default: + return sc.client.typedClient.ApplySubResource(ctx, obj, sc.subResource, opts...) + } +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go index 70389dfa90..1c39f4d854 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/config/config.go @@ -64,9 +64,6 @@ func RegisterFlags(fs *flag.FlagSet) { // The returned `*rest.Config` has client-side ratelimting disabled as we can rely on API priority and // fairness. Set its QPS to a value equal or bigger than 0 to re-enable it. // -// It also applies saner defaults for QPS and burst based on the Kubernetes -// controller manager defaults (20 QPS, 30 burst) -// // Config precedence: // // * --kubeconfig flag pointing at a file @@ -87,9 +84,6 @@ func GetConfig() (*rest.Config, error) { // The returned `*rest.Config` has client-side ratelimting disabled as we can rely on API priority and // fairness. Set its QPS to a value equal or bigger than 0 to re-enable it. // -// It also applies saner defaults for QPS and burst based on the Kubernetes -// controller manager defaults (20 QPS, 30 burst) -// // Config precedence: // // * --kubeconfig flag pointing at a file diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go index a185860d33..fb7012200f 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/dryrun.go @@ -132,3 +132,7 @@ func (sw *dryRunSubResourceClient) Update(ctx context.Context, obj Object, opts func (sw *dryRunSubResourceClient) Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error { return sw.client.Patch(ctx, obj, patch, append(opts, DryRunAll)...) } + +func (sw *dryRunSubResourceClient) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...SubResourceApplyOption) error { + return sw.client.Apply(ctx, obj, append(opts, DryRunAll)...) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go index f88a44edd2..2a07bd40b2 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/client.go @@ -17,13 +17,11 @@ limitations under the License. package fake import ( - "bytes" "context" "errors" "fmt" "reflect" - "runtime/debug" - "strconv" + "slices" "strings" "sync" "time" @@ -65,7 +63,6 @@ import ( utilrand "k8s.io/apimachinery/pkg/util/rand" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/strategicpatch" - "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/watch" clientgoapplyconfigurations "k8s.io/client-go/applyconfigurations" "k8s.io/client-go/kubernetes/scheme" @@ -79,13 +76,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/internal/objectutil" ) -type versionedTracker struct { - testing.ObjectTracker - scheme *runtime.Scheme - withStatusSubresource sets.Set[schema.GroupVersionKind] - usesFieldManagedObjectTracker bool -} - type fakeClient struct { // trackerWriteLock must be acquired before writing to // the tracker or performing reads that affect a following @@ -313,7 +303,7 @@ func (f *ClientBuilder) Build() client.WithWatch { usesFieldManagedObjectTracker = true } tracker := versionedTracker{ - ObjectTracker: f.objectTracker, + upstream: f.objectTracker, scheme: f.scheme, withStatusSubresource: withStatusSubResource, usesFieldManagedObjectTracker: usesFieldManagedObjectTracker, @@ -354,83 +344,6 @@ func (f *ClientBuilder) Build() client.WithWatch { const trackerAddResourceVersion = "999" -func (t versionedTracker) Add(obj runtime.Object) error { - var objects []runtime.Object - if meta.IsListType(obj) { - var err error - objects, err = meta.ExtractList(obj) - if err != nil { - return err - } - } else { - objects = []runtime.Object{obj} - } - for _, obj := range objects { - accessor, err := meta.Accessor(obj) - if err != nil { - return fmt.Errorf("failed to get accessor for object: %w", err) - } - if accessor.GetDeletionTimestamp() != nil && len(accessor.GetFinalizers()) == 0 { - return fmt.Errorf("refusing to create obj %s with metadata.deletionTimestamp but no finalizers", accessor.GetName()) - } - if accessor.GetResourceVersion() == "" { - // We use a "magic" value of 999 here because this field - // is parsed as uint and and 0 is already used in Update. - // As we can't go lower, go very high instead so this can - // be recognized - accessor.SetResourceVersion(trackerAddResourceVersion) - } - - obj, err = convertFromUnstructuredIfNecessary(t.scheme, obj) - if err != nil { - return err - } - - // If the fieldManager can not decode fields, it will just silently clear them. This is pretty - // much guaranteed not to be what someone that initializes a fake client with objects that - // have them set wants, so validate them here. - // Ref https://github.com/kubernetes/kubernetes/blob/a956ef4862993b825bcd524a19260192ff1da72d/staging/src/k8s.io/apimachinery/pkg/util/managedfields/internal/fieldmanager.go#L105 - if t.usesFieldManagedObjectTracker { - if err := managedfields.ValidateManagedFields(accessor.GetManagedFields()); err != nil { - return fmt.Errorf("invalid managedFields on %T: %w", obj, err) - } - } - if err := t.ObjectTracker.Add(obj); err != nil { - return err - } - } - - return nil -} - -func (t versionedTracker) Create(gvr schema.GroupVersionResource, obj runtime.Object, ns string, opts ...metav1.CreateOptions) error { - accessor, err := meta.Accessor(obj) - if err != nil { - return fmt.Errorf("failed to get accessor for object: %w", err) - } - if accessor.GetName() == "" { - gvk, _ := apiutil.GVKForObject(obj, t.scheme) - return apierrors.NewInvalid( - gvk.GroupKind(), - accessor.GetName(), - field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - } - if accessor.GetResourceVersion() != "" { - return apierrors.NewBadRequest("resourceVersion can not be set for Create requests") - } - accessor.SetResourceVersion("1") - obj, err = convertFromUnstructuredIfNecessary(t.scheme, obj) - if err != nil { - return err - } - if err := t.ObjectTracker.Create(gvr, obj, ns, opts...); err != nil { - accessor.SetResourceVersion("") - return err - } - - return nil -} - // convertFromUnstructuredIfNecessary will convert runtime.Unstructured for a GVK that is recognized // by the schema into the whatever the schema produces with New() for said GVK. // This is required because the tracker unconditionally saves on manipulations, but its List() implementation @@ -465,151 +378,6 @@ func convertFromUnstructuredIfNecessary(s *runtime.Scheme, o runtime.Object) (ru return typed, nil } -func (t versionedTracker) Update(gvr schema.GroupVersionResource, obj runtime.Object, ns string, opts ...metav1.UpdateOptions) error { - updateOpts, err := getSingleOrZeroOptions(opts) - if err != nil { - return err - } - - return t.update(gvr, obj, ns, false, false, updateOpts) -} - -func (t versionedTracker) update(gvr schema.GroupVersionResource, obj runtime.Object, ns string, isStatus, deleting bool, opts metav1.UpdateOptions) error { - gvk, err := apiutil.GVKForObject(obj, t.scheme) - if err != nil { - return err - } - obj, err = t.updateObject(gvr, obj, ns, isStatus, deleting, opts.DryRun) - if err != nil { - return err - } - if obj == nil { - return nil - } - - if u, unstructured := obj.(*unstructured.Unstructured); unstructured { - u.SetGroupVersionKind(gvk) - } - - return t.ObjectTracker.Update(gvr, obj, ns, opts) -} - -func (t versionedTracker) Patch(gvr schema.GroupVersionResource, obj runtime.Object, ns string, opts ...metav1.PatchOptions) error { - patchOptions, err := getSingleOrZeroOptions(opts) - if err != nil { - return err - } - - // We apply patches using a client-go reaction that ends up calling the trackers Patch. As we can't change - // that reaction, we use the callstack to figure out if this originated from the status client. - isStatus := bytes.Contains(debug.Stack(), []byte("sigs.k8s.io/controller-runtime/pkg/client/fake.(*fakeSubResourceClient).statusPatch")) - - obj, err = t.updateObject(gvr, obj, ns, isStatus, false, patchOptions.DryRun) - if err != nil { - return err - } - if obj == nil { - return nil - } - - return t.ObjectTracker.Patch(gvr, obj, ns, patchOptions) -} - -func (t versionedTracker) updateObject(gvr schema.GroupVersionResource, obj runtime.Object, ns string, isStatus, deleting bool, dryRun []string) (runtime.Object, error) { - accessor, err := meta.Accessor(obj) - if err != nil { - return nil, fmt.Errorf("failed to get accessor for object: %w", err) - } - - if accessor.GetName() == "" { - gvk, _ := apiutil.GVKForObject(obj, t.scheme) - return nil, apierrors.NewInvalid( - gvk.GroupKind(), - accessor.GetName(), - field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) - } - - gvk, err := apiutil.GVKForObject(obj, t.scheme) - if err != nil { - return nil, err - } - - oldObject, err := t.ObjectTracker.Get(gvr, ns, accessor.GetName()) - if err != nil { - // If the resource is not found and the resource allows create on update, issue a - // create instead. - if apierrors.IsNotFound(err) && allowsCreateOnUpdate(gvk) { - return nil, t.Create(gvr, obj, ns) - } - return nil, err - } - - if t.withStatusSubresource.Has(gvk) { - if isStatus { // copy everything but status and metadata.ResourceVersion from original object - if err := copyStatusFrom(obj, oldObject); err != nil { - return nil, fmt.Errorf("failed to copy non-status field for object with status subresouce: %w", err) - } - passedRV := accessor.GetResourceVersion() - if err := copyFrom(oldObject, obj); err != nil { - return nil, fmt.Errorf("failed to restore non-status fields: %w", err) - } - accessor.SetResourceVersion(passedRV) - } else { // copy status from original object - if err := copyStatusFrom(oldObject, obj); err != nil { - return nil, fmt.Errorf("failed to copy the status for object with status subresource: %w", err) - } - } - } else if isStatus { - return nil, apierrors.NewNotFound(gvr.GroupResource(), accessor.GetName()) - } - - oldAccessor, err := meta.Accessor(oldObject) - if err != nil { - return nil, err - } - - // If the new object does not have the resource version set and it allows unconditional update, - // default it to the resource version of the existing resource - if accessor.GetResourceVersion() == "" { - switch { - case allowsUnconditionalUpdate(gvk): - accessor.SetResourceVersion(oldAccessor.GetResourceVersion()) - // This is needed because if the patch explicitly sets the RV to null, the client-go reaction we use - // to apply it and whose output we process here will have it unset. It is not clear why the Kubernetes - // apiserver accepts such a patch, but it does so we just copy that behavior. - // Kubernetes apiserver behavior can be checked like this: - // `kubectl patch configmap foo --patch '{"metadata":{"annotations":{"foo":"bar"},"resourceVersion":null}}' -v=9` - case bytes. - Contains(debug.Stack(), []byte("sigs.k8s.io/controller-runtime/pkg/client/fake.(*fakeClient).Patch")): - // We apply patches using a client-go reaction that ends up calling the trackers Update. As we can't change - // that reaction, we use the callstack to figure out if this originated from the "fakeClient.Patch" func. - accessor.SetResourceVersion(oldAccessor.GetResourceVersion()) - } - } - - if accessor.GetResourceVersion() != oldAccessor.GetResourceVersion() { - return nil, apierrors.NewConflict(gvr.GroupResource(), accessor.GetName(), errors.New("object was modified")) - } - if oldAccessor.GetResourceVersion() == "" { - oldAccessor.SetResourceVersion("0") - } - intResourceVersion, err := strconv.ParseUint(oldAccessor.GetResourceVersion(), 10, 64) - if err != nil { - return nil, fmt.Errorf("can not convert resourceVersion %q to int: %w", oldAccessor.GetResourceVersion(), err) - } - intResourceVersion++ - accessor.SetResourceVersion(strconv.FormatUint(intResourceVersion, 10)) - - if !deleting && !deletionTimestampEqual(accessor, oldAccessor) { - return nil, fmt.Errorf("error: Unable to edit %s: metadata.deletionTimestamp field is immutable", accessor.GetName()) - } - - if !accessor.GetDeletionTimestamp().IsZero() && len(accessor.GetFinalizers()) == 0 { - return nil, t.ObjectTracker.Delete(gvr, accessor.GetNamespace(), accessor.GetName(), metav1.DeleteOptions{DryRun: dryRun}) - } - return convertFromUnstructuredIfNecessary(t.scheme, obj) -} - func (c *fakeClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error { if err := c.addToSchemeIfUnknownAndUnstructuredOrPartial(obj); err != nil { return err @@ -821,13 +589,7 @@ func (c *fakeClient) objMatchesFieldSelector(o runtime.Object, extractIndex clie panic(fmt.Errorf("expected object %v to be of type client.Object, but it's not", o)) } - for _, extractedVal := range extractIndex(obj) { - if extractedVal == val { - return true - } - } - - return false + return slices.Contains(extractIndex(obj), val) } func (c *fakeClient) Scheme() *runtime.Scheme { @@ -859,10 +621,8 @@ func (c *fakeClient) Create(ctx context.Context, obj client.Object, opts ...clie createOptions := &client.CreateOptions{} createOptions.ApplyOptions(opts) - for _, dryRunOpt := range createOptions.DryRun { - if dryRunOpt == metav1.DryRunAll { - return nil - } + if slices.Contains(createOptions.DryRun, metav1.DryRunAll) { + return nil } gvr, err := getGVRFromObject(obj, c.scheme) @@ -926,10 +686,8 @@ func (c *fakeClient) Delete(ctx context.Context, obj client.Object, opts ...clie delOptions := client.DeleteOptions{} delOptions.ApplyOptions(opts) - for _, dryRunOpt := range delOptions.DryRun { - if dryRunOpt == metav1.DryRunAll { - return nil - } + if slices.Contains(delOptions.DryRun, metav1.DryRunAll) { + return nil } c.trackerWriteLock.Lock() @@ -975,10 +733,8 @@ func (c *fakeClient) DeleteAllOf(ctx context.Context, obj client.Object, opts .. dcOptions := client.DeleteAllOfOptions{} dcOptions.ApplyOptions(opts) - for _, dryRunOpt := range dcOptions.DryRun { - if dryRunOpt == metav1.DryRunAll { - return nil - } + if slices.Contains(dcOptions.DryRun, metav1.DryRunAll) { + return nil } c.trackerWriteLock.Lock() @@ -1026,10 +782,8 @@ func (c *fakeClient) update(obj client.Object, isStatus bool, opts ...client.Upd updateOptions := &client.UpdateOptions{} updateOptions.ApplyOptions(opts) - for _, dryRunOpt := range updateOptions.DryRun { - if dryRunOpt == metav1.DryRunAll { - return nil - } + if slices.Contains(updateOptions.DryRun, metav1.DryRunAll) { + return nil } gvr, err := getGVRFromObject(obj, c.scheme) @@ -1141,10 +895,8 @@ func (c *fakeClient) patch(obj client.Object, patch client.Patch, opts ...client c.schemeLock.RLock() defer c.schemeLock.RUnlock() - for _, dryRunOpt := range patchOptions.DryRun { - if dryRunOpt == metav1.DryRunAll { - return nil - } + if slices.Contains(patchOptions.DryRun, metav1.DryRunAll) { + return nil } gvr, err := getGVRFromObject(obj, c.scheme) @@ -1176,9 +928,15 @@ func (c *fakeClient) patch(obj client.Object, patch client.Patch, opts ...client return err } - // SSA deletionTimestamp updates are silently ignored - if patch.Type() == types.ApplyPatchType && !isApplyCreate { - obj.SetDeletionTimestamp(oldAccessor.GetDeletionTimestamp()) + if patch.Type() == types.ApplyPatchType { + if isApplyCreate { + // Overwrite it unconditionally, this matches the apiserver behavior + // which allows to set it on create, but will then ignore it. + obj.SetResourceVersion("1") + } else { + // SSA deletionTimestamp updates are silently ignored + obj.SetDeletionTimestamp(oldAccessor.GetDeletionTimestamp()) + } } data, err := patch.Data(obj) @@ -1227,6 +985,15 @@ func (c *fakeClient) patch(obj client.Object, patch client.Patch, opts ...client reaction := testing.ObjectReaction(c.tracker) handled, o, err := reaction(action) if err != nil { + // The reaction calls tracker.Get after tracker.Apply to return the object, + // but we may have deleted it in tracker.Apply if there was no finalizer + // left. + if apierrors.IsNotFound(err) && + patch.Type() == types.ApplyPatchType && + oldAccessor.GetDeletionTimestamp() != nil && + len(obj.GetFinalizers()) == 0 { + return nil + } return err } if !handled { @@ -1553,6 +1320,42 @@ func (sw *fakeSubResourceClient) statusPatch(body client.Object, patch client.Pa return sw.client.patch(body, patch, &patchOptions.PatchOptions) } +func (sw *fakeSubResourceClient) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.SubResourceApplyOption) error { + if sw.subResource != "status" { + return errors.New("fakeSubResourceClient currently only supports Apply for status subresource") + } + + applyOpts := &client.SubResourceApplyOptions{} + applyOpts.ApplyOpts(opts) + + data, err := json.Marshal(obj) + if err != nil { + return fmt.Errorf("failed to marshal apply configuration: %w", err) + } + + u := &unstructured.Unstructured{} + if err := json.Unmarshal(data, u); err != nil { + return fmt.Errorf("failed to unmarshal apply configuration: %w", err) + } + + patchOpts := &client.SubResourcePatchOptions{} + patchOpts.Raw = applyOpts.AsPatchOptions() + + if applyOpts.SubResourceBody != nil { + subResourceBodySerialized, err := json.Marshal(applyOpts.SubResourceBody) + if err != nil { + return fmt.Errorf("failed to serialize subresource body: %w", err) + } + subResourceBody := &unstructured.Unstructured{} + if err := json.Unmarshal(subResourceBodySerialized, subResourceBody); err != nil { + return fmt.Errorf("failed to unmarshal subresource body: %w", err) + } + patchOpts.SubResourceBody = subResourceBody + } + + return sw.Patch(ctx, u, &fakeApplyPatch{}, patchOpts) +} + func allowsUnconditionalUpdate(gvk schema.GroupVersionKind) bool { switch gvk.Group { case "apps": @@ -1689,7 +1492,7 @@ func inTreeResourcesWithStatus() []schema.GroupVersionKind { } // zero zeros the value of a pointer. -func zero(x interface{}) { +func zero(x any) { if x == nil { return } @@ -1883,6 +1686,12 @@ func (c *fakeClient) addToSchemeIfUnknownAndUnstructuredOrPartial(obj runtime.Ob return err } + if isUnstructuredList || isPartialList { + if !strings.HasSuffix(gvk.Kind, "List") { + gvk.Kind += "List" + } + } + if !c.scheme.Recognizes(gvk) { c.scheme.AddKnownTypeWithName(gvk, obj) } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/versioned_tracker.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/versioned_tracker.go new file mode 100644 index 0000000000..62ccac2bec --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fake/versioned_tracker.go @@ -0,0 +1,368 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fake + +import ( + "bytes" + "errors" + "fmt" + "runtime/debug" + "strconv" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/managedfields" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/testing" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" +) + +var _ testing.ObjectTracker = (*versionedTracker)(nil) + +type versionedTracker struct { + upstream testing.ObjectTracker + scheme *runtime.Scheme + withStatusSubresource sets.Set[schema.GroupVersionKind] + usesFieldManagedObjectTracker bool +} + +func (t versionedTracker) Add(obj runtime.Object) error { + var objects []runtime.Object + if meta.IsListType(obj) { + var err error + objects, err = meta.ExtractList(obj) + if err != nil { + return err + } + } else { + objects = []runtime.Object{obj} + } + for _, obj := range objects { + accessor, err := meta.Accessor(obj) + if err != nil { + return fmt.Errorf("failed to get accessor for object: %w", err) + } + if accessor.GetDeletionTimestamp() != nil && len(accessor.GetFinalizers()) == 0 { + return fmt.Errorf("refusing to create obj %s with metadata.deletionTimestamp but no finalizers", accessor.GetName()) + } + if accessor.GetResourceVersion() == "" { + // We use a "magic" value of 999 here because this field + // is parsed as uint and and 0 is already used in Update. + // As we can't go lower, go very high instead so this can + // be recognized + accessor.SetResourceVersion(trackerAddResourceVersion) + } + + obj, err = convertFromUnstructuredIfNecessary(t.scheme, obj) + if err != nil { + return err + } + + // If the fieldManager can not decode fields, it will just silently clear them. This is pretty + // much guaranteed not to be what someone that initializes a fake client with objects that + // have them set wants, so validate them here. + // Ref https://github.com/kubernetes/kubernetes/blob/a956ef4862993b825bcd524a19260192ff1da72d/staging/src/k8s.io/apimachinery/pkg/util/managedfields/internal/fieldmanager.go#L105 + if t.usesFieldManagedObjectTracker { + if err := managedfields.ValidateManagedFields(accessor.GetManagedFields()); err != nil { + return fmt.Errorf("invalid managedFields on %T: %w", obj, err) + } + } + if err := t.upstream.Add(obj); err != nil { + return err + } + } + + return nil +} + +func (t versionedTracker) Create(gvr schema.GroupVersionResource, obj runtime.Object, ns string, opts ...metav1.CreateOptions) error { + accessor, err := meta.Accessor(obj) + if err != nil { + return fmt.Errorf("failed to get accessor for object: %w", err) + } + if accessor.GetName() == "" { + gvk, _ := apiutil.GVKForObject(obj, t.scheme) + return apierrors.NewInvalid( + gvk.GroupKind(), + accessor.GetName(), + field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + } + if accessor.GetResourceVersion() != "" { + return apierrors.NewBadRequest("resourceVersion can not be set for Create requests") + } + accessor.SetResourceVersion("1") + obj, err = convertFromUnstructuredIfNecessary(t.scheme, obj) + if err != nil { + return err + } + if err := t.upstream.Create(gvr, obj, ns, opts...); err != nil { + accessor.SetResourceVersion("") + return err + } + + return nil +} + +func (t versionedTracker) Update(gvr schema.GroupVersionResource, obj runtime.Object, ns string, opts ...metav1.UpdateOptions) error { + updateOpts, err := getSingleOrZeroOptions(opts) + if err != nil { + return err + } + + return t.update(gvr, obj, ns, false, false, updateOpts) +} + +func (t versionedTracker) update(gvr schema.GroupVersionResource, obj runtime.Object, ns string, isStatus, deleting bool, opts metav1.UpdateOptions) error { + gvk, err := apiutil.GVKForObject(obj, t.scheme) + if err != nil { + return err + } + obj, needsCreate, err := t.updateObject(gvr, gvk, obj, ns, isStatus, deleting, allowsCreateOnUpdate(gvk), opts.DryRun) + if err != nil { + return err + } + + if needsCreate { + opts := metav1.CreateOptions{DryRun: opts.DryRun, FieldManager: opts.FieldManager} + return t.Create(gvr, obj, ns, opts) + } + + if obj == nil { // Object was deleted in updateObject + return nil + } + + if u, unstructured := obj.(*unstructured.Unstructured); unstructured { + u.SetGroupVersionKind(gvk) + } + + return t.upstream.Update(gvr, obj, ns, opts) +} + +func (t versionedTracker) Patch(gvr schema.GroupVersionResource, obj runtime.Object, ns string, opts ...metav1.PatchOptions) error { + patchOptions, err := getSingleOrZeroOptions(opts) + if err != nil { + return err + } + + gvk, err := apiutil.GVKForObject(obj, t.scheme) + if err != nil { + return err + } + + // We apply patches using a client-go reaction that ends up calling the trackers Patch. As we can't change + // that reaction, we use the callstack to figure out if this originated from the status client. + isStatus := bytes.Contains(debug.Stack(), []byte("sigs.k8s.io/controller-runtime/pkg/client/fake.(*fakeSubResourceClient).statusPatch")) + + obj, needsCreate, err := t.updateObject(gvr, gvk, obj, ns, isStatus, false, allowsCreateOnUpdate(gvk), patchOptions.DryRun) + if err != nil { + return err + } + if needsCreate { + opts := metav1.CreateOptions{DryRun: patchOptions.DryRun, FieldManager: patchOptions.FieldManager} + return t.Create(gvr, obj, ns, opts) + } + + if obj == nil { // Object was deleted in updateObject + return nil + } + + return t.upstream.Patch(gvr, obj, ns, patchOptions) +} + +// updateObject performs a number of validations and changes related to +// object updates, such as checking and updating the resourceVersion. +func (t versionedTracker) updateObject( + gvr schema.GroupVersionResource, + gvk schema.GroupVersionKind, + obj runtime.Object, + ns string, + isStatus bool, + deleting bool, + allowCreateOnUpdate bool, + dryRun []string, +) (result runtime.Object, needsCreate bool, _ error) { + accessor, err := meta.Accessor(obj) + if err != nil { + return nil, false, fmt.Errorf("failed to get accessor for object: %w", err) + } + + if accessor.GetName() == "" { + return nil, false, apierrors.NewInvalid( + gvk.GroupKind(), + accessor.GetName(), + field.ErrorList{field.Required(field.NewPath("metadata.name"), "name is required")}) + } + + oldObject, err := t.Get(gvr, ns, accessor.GetName()) + if err != nil { + // If the resource is not found and the resource allows create on update, issue a + // create instead. + if apierrors.IsNotFound(err) && allowCreateOnUpdate { + // Pass this info to the caller rather than create, because in the SSA case it + // must be created by calling Apply in the upstream tracker, not Create. + // This is because SSA considers Apply and Non-Apply operations to be different + // even when they use the same fieldManager. This behavior is also observable + // with a real Kubernetes apiserver. + // + // Ref https://kubernetes.slack.com/archives/C0EG7JC6T/p1757868204458989?thread_ts=1757808656.002569&cid=C0EG7JC6T + return obj, true, nil + } + return obj, false, err + } + + if t.withStatusSubresource.Has(gvk) { + if isStatus { // copy everything but status, managedFields and metadata.ResourceVersion from original object + if err := copyStatusFrom(obj, oldObject); err != nil { + return nil, false, fmt.Errorf("failed to copy non-status field for object with status subresouce: %w", err) + } + passedRV := accessor.GetResourceVersion() + passedManagedFields := accessor.GetManagedFields() + if err := copyFrom(oldObject, obj); err != nil { + return nil, false, fmt.Errorf("failed to restore non-status fields: %w", err) + } + accessor.SetResourceVersion(passedRV) + accessor.SetManagedFields(passedManagedFields) + } else { // copy status from original object + if err := copyStatusFrom(oldObject, obj); err != nil { + return nil, false, fmt.Errorf("failed to copy the status for object with status subresource: %w", err) + } + } + } else if isStatus { + return nil, false, apierrors.NewNotFound(gvr.GroupResource(), accessor.GetName()) + } + + oldAccessor, err := meta.Accessor(oldObject) + if err != nil { + return nil, false, err + } + + // If the new object does not have the resource version set and it allows unconditional update, + // default it to the resource version of the existing resource + if accessor.GetResourceVersion() == "" { + switch { + case allowsUnconditionalUpdate(gvk): + accessor.SetResourceVersion(oldAccessor.GetResourceVersion()) + // This is needed because if the patch explicitly sets the RV to null, the client-go reaction we use + // to apply it and whose output we process here will have it unset. It is not clear why the Kubernetes + // apiserver accepts such a patch, but it does so we just copy that behavior. + // Kubernetes apiserver behavior can be checked like this: + // `kubectl patch configmap foo --patch '{"metadata":{"annotations":{"foo":"bar"},"resourceVersion":null}}' -v=9` + case bytes.Contains(debug.Stack(), []byte("sigs.k8s.io/controller-runtime/pkg/client/fake.(*fakeClient).Patch")), + bytes.Contains(debug.Stack(), []byte("sigs.k8s.io/controller-runtime/pkg/client/fake.(*fakeSubResourceClient).Patch")): + // We apply patches using a client-go reaction that ends up calling the trackers Update. As we can't change + // that reaction, we use the callstack to figure out if this originated from the "fake[SubResource]Client.Patch" func. + accessor.SetResourceVersion(oldAccessor.GetResourceVersion()) + case bytes.Contains(debug.Stack(), []byte("sigs.k8s.io/controller-runtime/pkg/client/fake.(*fakeClient).Apply")), + bytes.Contains(debug.Stack(), []byte("sigs.k8s.io/controller-runtime/pkg/client/fake.(*fakeSubResourceClient).Apply")): + // We apply patches using a client-go reaction that ends up calling the trackers Update. As we can't change + // that reaction, we use the callstack to figure out if this originated from the "fake[SubResource]Client.Apply" func. + accessor.SetResourceVersion(oldAccessor.GetResourceVersion()) + } + } + + if accessor.GetResourceVersion() != oldAccessor.GetResourceVersion() { + return nil, false, apierrors.NewConflict(gvr.GroupResource(), accessor.GetName(), errors.New("object was modified")) + } + if oldAccessor.GetResourceVersion() == "" { + oldAccessor.SetResourceVersion("0") + } + intResourceVersion, err := strconv.ParseUint(oldAccessor.GetResourceVersion(), 10, 64) + if err != nil { + return nil, false, fmt.Errorf("can not convert resourceVersion %q to int: %w", oldAccessor.GetResourceVersion(), err) + } + intResourceVersion++ + accessor.SetResourceVersion(strconv.FormatUint(intResourceVersion, 10)) + + if !deleting && !deletionTimestampEqual(accessor, oldAccessor) { + return nil, false, fmt.Errorf("error: Unable to edit %s: metadata.deletionTimestamp field is immutable", accessor.GetName()) + } + + if !accessor.GetDeletionTimestamp().IsZero() && len(accessor.GetFinalizers()) == 0 { + return nil, false, t.Delete(gvr, accessor.GetNamespace(), accessor.GetName(), metav1.DeleteOptions{DryRun: dryRun}) + } + + obj, err = convertFromUnstructuredIfNecessary(t.scheme, obj) + return obj, false, err +} + +func (t versionedTracker) Apply(gvr schema.GroupVersionResource, applyConfiguration runtime.Object, ns string, opts ...metav1.PatchOptions) error { + patchOptions, err := getSingleOrZeroOptions(opts) + if err != nil { + return err + } + gvk, err := apiutil.GVKForObject(applyConfiguration, t.scheme) + if err != nil { + return err + } + isStatus := bytes.Contains(debug.Stack(), []byte("sigs.k8s.io/controller-runtime/pkg/client/fake.(*fakeSubResourceClient).statusPatch")) + + applyConfiguration, needsCreate, err := t.updateObject(gvr, gvk, applyConfiguration, ns, isStatus, false, true, patchOptions.DryRun) + if err != nil { + return err + } + + if needsCreate { + // https://github.com/kubernetes/kubernetes/blob/81affffa1b8d8079836f4cac713ea8d1b2bbf10f/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go#L606 + accessor, err := meta.Accessor(applyConfiguration) + if err != nil { + return fmt.Errorf("failed to get accessor for object: %w", err) + } + if accessor.GetUID() != "" { + return apierrors.NewConflict(gvr.GroupResource(), accessor.GetName(), fmt.Errorf("uid mismatch: the provided object specified uid %s, and no existing object was found", accessor.GetUID())) + } + + if t.withStatusSubresource.Has(gvk) { + // Clear out status for create, for update this is handled in updateObject + if err := copyStatusFrom(&unstructured.Unstructured{}, applyConfiguration); err != nil { + return err + } + } + } + + if applyConfiguration == nil { // Object was deleted in updateObject + return nil + } + + if isStatus { + // We restore everything but status from the tracker where we don't put GVK + // into the object but it must be set for the ManagedFieldsObjectTracker + applyConfiguration.GetObjectKind().SetGroupVersionKind(gvk) + } + return t.upstream.Apply(gvr, applyConfiguration, ns, opts...) +} + +func (t versionedTracker) Delete(gvr schema.GroupVersionResource, ns, name string, opts ...metav1.DeleteOptions) error { + return t.upstream.Delete(gvr, ns, name, opts...) +} + +func (t versionedTracker) Get(gvr schema.GroupVersionResource, ns, name string, opts ...metav1.GetOptions) (runtime.Object, error) { + return t.upstream.Get(gvr, ns, name, opts...) +} + +func (t versionedTracker) List(gvr schema.GroupVersionResource, gvk schema.GroupVersionKind, ns string, opts ...metav1.ListOptions) (runtime.Object, error) { + return t.upstream.List(gvr, gvk, ns, opts...) +} + +func (t versionedTracker) Watch(gvr schema.GroupVersionResource, ns string, opts ...metav1.ListOptions) (watch.Interface, error) { + return t.upstream.Watch(gvr, ns, opts...) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldowner.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldowner.go index 93274f9500..5d9437ba91 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldowner.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldowner.go @@ -108,3 +108,7 @@ func (f *subresourceClientWithFieldOwner) Update(ctx context.Context, obj Object func (f *subresourceClientWithFieldOwner) Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error { return f.subresourceWriter.Patch(ctx, obj, patch, append([]SubResourcePatchOption{FieldOwner(f.owner)}, opts...)...) } + +func (f *subresourceClientWithFieldOwner) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...SubResourceApplyOption) error { + return f.subresourceWriter.Apply(ctx, obj, append([]SubResourceApplyOption{FieldOwner(f.owner)}, opts...)...) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldvalidation.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldvalidation.go index ce8d0576c7..b0f660854e 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldvalidation.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/fieldvalidation.go @@ -27,6 +27,9 @@ import ( // WithFieldValidation wraps a Client and configures field validation, by // default, for all write requests from this client. Users can override field // validation for individual write requests. +// +// This wrapper has no effect on apply requests, as they do not support a +// custom fieldValidation setting, it is always strict. func WithFieldValidation(c Client, validation FieldValidation) Client { return &clientWithFieldValidation{ validation: validation, @@ -108,3 +111,7 @@ func (c *subresourceClientWithFieldValidation) Update(ctx context.Context, obj O func (c *subresourceClientWithFieldValidation) Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error { return c.subresourceWriter.Patch(ctx, obj, patch, append([]SubResourcePatchOption{c.validation}, opts...)...) } + +func (c *subresourceClientWithFieldValidation) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...SubResourceApplyOption) error { + return c.subresourceWriter.Apply(ctx, obj, opts...) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/interceptor/intercept.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/interceptor/intercept.go index 7ff73bd8da..b98af1a693 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/interceptor/intercept.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/interceptor/intercept.go @@ -26,6 +26,7 @@ type Funcs struct { SubResourceCreate func(ctx context.Context, client client.Client, subResourceName string, obj client.Object, subResource client.Object, opts ...client.SubResourceCreateOption) error SubResourceUpdate func(ctx context.Context, client client.Client, subResourceName string, obj client.Object, opts ...client.SubResourceUpdateOption) error SubResourcePatch func(ctx context.Context, client client.Client, subResourceName string, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error + SubResourceApply func(ctx context.Context, client client.Client, subResourceName string, obj runtime.ApplyConfiguration, opts ...client.SubResourceApplyOption) error } // NewClient returns a new interceptor client that calls the functions in funcs instead of the underlying client's methods, if they are not nil. @@ -173,3 +174,10 @@ func (s subResourceInterceptor) Patch(ctx context.Context, obj client.Object, pa } return s.client.SubResource(s.subResourceName).Patch(ctx, obj, patch, opts...) } + +func (s subResourceInterceptor) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...client.SubResourceApplyOption) error { + if s.funcs.SubResourceApply != nil { + return s.funcs.SubResourceApply(ctx, s.client, s.subResourceName, obj, opts...) + } + return s.client.SubResource(s.subResourceName).Apply(ctx, obj, opts...) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go index 61559ecbe1..1af1f3a368 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/interfaces.go @@ -155,6 +155,9 @@ type SubResourceWriter interface { // pointer so that obj can be updated with the content returned by the // Server. Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error + + // Apply applies the given apply configurations subresource. + Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...SubResourceApplyOption) error } // SubResourceClient knows how to perform CRU operations on Kubernetes objects. diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go index d4223eda26..ebbbc4fddf 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/namespaced_client.go @@ -150,7 +150,7 @@ func (n *namespacedClient) Patch(ctx context.Context, obj Object, patch Patch, o return n.client.Patch(ctx, obj, patch, opts...) } -func (n *namespacedClient) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...ApplyOption) error { +func (n *namespacedClient) setNamespaceForApplyConfigIfNamespaceScoped(obj runtime.ApplyConfiguration) error { var gvk schema.GroupVersionKind switch o := obj.(type) { case applyConfiguration: @@ -193,6 +193,14 @@ func (n *namespacedClient) Apply(ctx context.Context, obj runtime.ApplyConfigura } } + return nil +} + +func (n *namespacedClient) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...ApplyOption) error { + if err := n.setNamespaceForApplyConfigIfNamespaceScoped(obj); err != nil { + return err + } + return n.client.Apply(ctx, obj, opts...) } @@ -231,7 +239,10 @@ func (n *namespacedClient) Status() SubResourceWriter { // SubResource implements client.SubResourceClient. func (n *namespacedClient) SubResource(subResource string) SubResourceClient { - return &namespacedClientSubResourceClient{client: n.client.SubResource(subResource), namespace: n.namespace, namespacedclient: n} + return &namespacedClientSubResourceClient{ + client: n.client.SubResource(subResource), + namespacedclient: n, + } } // ensure namespacedClientSubResourceClient implements client.SubResourceClient. @@ -239,8 +250,7 @@ var _ SubResourceClient = &namespacedClientSubResourceClient{} type namespacedClientSubResourceClient struct { client SubResourceClient - namespace string - namespacedclient Client + namespacedclient *namespacedClient } func (nsw *namespacedClientSubResourceClient) Get(ctx context.Context, obj, subResource Object, opts ...SubResourceGetOption) error { @@ -250,12 +260,12 @@ func (nsw *namespacedClientSubResourceClient) Get(ctx context.Context, obj, subR } objectNamespace := obj.GetNamespace() - if objectNamespace != nsw.namespace && objectNamespace != "" { - return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespace) + if objectNamespace != nsw.namespacedclient.namespace && objectNamespace != "" { + return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespacedclient.namespace) } if isNamespaceScoped && objectNamespace == "" { - obj.SetNamespace(nsw.namespace) + obj.SetNamespace(nsw.namespacedclient.namespace) } return nsw.client.Get(ctx, obj, subResource, opts...) @@ -268,12 +278,12 @@ func (nsw *namespacedClientSubResourceClient) Create(ctx context.Context, obj, s } objectNamespace := obj.GetNamespace() - if objectNamespace != nsw.namespace && objectNamespace != "" { - return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespace) + if objectNamespace != nsw.namespacedclient.namespace && objectNamespace != "" { + return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespacedclient.namespace) } if isNamespaceScoped && objectNamespace == "" { - obj.SetNamespace(nsw.namespace) + obj.SetNamespace(nsw.namespacedclient.namespace) } return nsw.client.Create(ctx, obj, subResource, opts...) @@ -287,12 +297,12 @@ func (nsw *namespacedClientSubResourceClient) Update(ctx context.Context, obj Ob } objectNamespace := obj.GetNamespace() - if objectNamespace != nsw.namespace && objectNamespace != "" { - return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespace) + if objectNamespace != nsw.namespacedclient.namespace && objectNamespace != "" { + return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespacedclient.namespace) } if isNamespaceScoped && objectNamespace == "" { - obj.SetNamespace(nsw.namespace) + obj.SetNamespace(nsw.namespacedclient.namespace) } return nsw.client.Update(ctx, obj, opts...) } @@ -305,12 +315,19 @@ func (nsw *namespacedClientSubResourceClient) Patch(ctx context.Context, obj Obj } objectNamespace := obj.GetNamespace() - if objectNamespace != nsw.namespace && objectNamespace != "" { - return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespace) + if objectNamespace != nsw.namespacedclient.namespace && objectNamespace != "" { + return fmt.Errorf("namespace %s of the object %s does not match the namespace %s on the client", objectNamespace, obj.GetName(), nsw.namespacedclient.namespace) } if isNamespaceScoped && objectNamespace == "" { - obj.SetNamespace(nsw.namespace) + obj.SetNamespace(nsw.namespacedclient.namespace) } return nsw.client.Patch(ctx, obj, patch, opts...) } + +func (nsw *namespacedClientSubResourceClient) Apply(ctx context.Context, obj runtime.ApplyConfiguration, opts ...SubResourceApplyOption) error { + if err := nsw.namespacedclient.setNamespaceForApplyConfigIfNamespaceScoped(obj); err != nil { + return err + } + return nsw.client.Apply(ctx, obj, opts...) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go index 33c460738c..a6b921171a 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/options.go @@ -97,6 +97,12 @@ type SubResourcePatchOption interface { ApplyToSubResourcePatch(*SubResourcePatchOptions) } +// SubResourceApplyOption configures a subresource apply request. +type SubResourceApplyOption interface { + // ApplyToSubResourceApply applies the configuration on the given patch options. + ApplyToSubResourceApply(*SubResourceApplyOptions) +} + // }}} // {{{ Multi-Type Options @@ -148,6 +154,10 @@ func (dryRunAll) ApplyToSubResourcePatch(opts *SubResourcePatchOptions) { opts.DryRun = []string{metav1.DryRunAll} } +func (dryRunAll) ApplyToSubResourceApply(opts *SubResourceApplyOptions) { + opts.DryRun = []string{metav1.DryRunAll} +} + // FieldOwner set the field manager name for the given server-side apply patch. type FieldOwner string @@ -186,6 +196,11 @@ func (f FieldOwner) ApplyToSubResourceUpdate(opts *SubResourceUpdateOptions) { opts.FieldManager = string(f) } +// ApplyToSubResourceApply applies this configuration to the given apply options. +func (f FieldOwner) ApplyToSubResourceApply(opts *SubResourceApplyOptions) { + opts.FieldManager = string(f) +} + // FieldValidation configures field validation for the given requests. type FieldValidation string @@ -949,6 +964,10 @@ func (forceOwnership) ApplyToApply(opts *ApplyOptions) { opts.Force = ptr.To(true) } +func (forceOwnership) ApplyToSubResourceApply(opts *SubResourceApplyOptions) { + opts.Force = ptr.To(true) +} + // }}} // {{{ DeleteAllOf Options diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go index b99d7663bd..3d914eea22 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/patch.go @@ -28,10 +28,7 @@ import ( var ( // Apply uses server-side apply to patch the given object. // - // This should now only be used to patch sub resources, e.g. with client.Client.Status().Patch(). - // Use client.Client.Apply() instead of client.Client.Patch(..., client.Apply, ...) - // This will be deprecated once the Apply method has been added for sub resources. - // See the following issue for more details: https://github.com/kubernetes-sigs/controller-runtime/issues/3183 + // Deprecated: Use client.Client.Apply() and client.Client.SubResource("subrsource").Apply() instead. Apply Patch = applyPatch{} // Merge uses the raw object as a merge patch, without modifications. @@ -91,7 +88,7 @@ type MergeFromOptions struct { type mergeFromPatch struct { patchType types.PatchType - createPatch func(originalJSON, modifiedJSON []byte, dataStruct interface{}) ([]byte, error) + createPatch func(originalJSON, modifiedJSON []byte, dataStruct any) ([]byte, error) from Object opts MergeFromOptions } @@ -137,11 +134,11 @@ func (s *mergeFromPatch) Data(obj Object) ([]byte, error) { return data, nil } -func createMergePatch(originalJSON, modifiedJSON []byte, _ interface{}) ([]byte, error) { +func createMergePatch(originalJSON, modifiedJSON []byte, _ any) ([]byte, error) { return jsonpatch.CreateMergePatch(originalJSON, modifiedJSON) } -func createStrategicMergePatch(originalJSON, modifiedJSON []byte, dataStruct interface{}) ([]byte, error) { +func createStrategicMergePatch(originalJSON, modifiedJSON []byte, dataStruct any) ([]byte, error) { return strategicpatch.CreateTwoWayMergePatch(originalJSON, modifiedJSON, dataStruct) } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go index 3bd762a638..66ae2e4a5c 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/typed_client.go @@ -304,3 +304,36 @@ func (c *typedClient) PatchSubResource(ctx context.Context, obj Object, subResou Do(ctx). Into(body) } + +func (c *typedClient) ApplySubResource(ctx context.Context, obj runtime.ApplyConfiguration, subResource string, opts ...SubResourceApplyOption) error { + o, err := c.resources.getObjMeta(obj) + if err != nil { + return err + } + + applyOpts := &SubResourceApplyOptions{} + applyOpts.ApplyOpts(opts) + + body := obj + if applyOpts.SubResourceBody != nil { + body = applyOpts.SubResourceBody + } + + req, err := apply.NewRequest(o, body) + if err != nil { + return fmt.Errorf("failed to create apply request: %w", err) + } + + return req. + NamespaceIfScoped(o.namespace, o.isNamespaced()). + Resource(o.resource()). + Name(o.name). + SubResource(subResource). + VersionedParams(applyOpts.AsPatchOptions(), c.paramCodec). + Do(ctx). + // This is hacky, it is required because `Into` takes a `runtime.Object` and + // that is not implemented by the ApplyConfigurations. The generated clients + // don't have this problem because they deserialize into the api type, not the + // apply configuration: https://github.com/kubernetes/kubernetes/blob/22f5e01a37c0bc6a5f494dec14dd4e3688ee1d55/staging/src/k8s.io/client-go/gentype/type.go#L296-L317 + Into(runtimeObjectFromApplyConfiguration(obj)) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go b/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go index e636c3beef..d2ea6d7a32 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/client/unstructured_client.go @@ -386,3 +386,35 @@ func (uc *unstructuredClient) PatchSubResource(ctx context.Context, obj Object, u.GetObjectKind().SetGroupVersionKind(gvk) return result } + +func (uc *unstructuredClient) ApplySubResource(ctx context.Context, obj runtime.ApplyConfiguration, subResource string, opts ...SubResourceApplyOption) error { + unstructuredApplyConfig, ok := obj.(*unstructuredApplyConfiguration) + if !ok { + return fmt.Errorf("bug: unstructured client got an applyconfiguration that was not %T but %T", &unstructuredApplyConfiguration{}, obj) + } + o, err := uc.resources.getObjMeta(unstructuredApplyConfig.Unstructured) + if err != nil { + return err + } + + applyOpts := &SubResourceApplyOptions{} + applyOpts.ApplyOpts(opts) + + body := obj + if applyOpts.SubResourceBody != nil { + body = applyOpts.SubResourceBody + } + req, err := apply.NewRequest(o, body) + if err != nil { + return fmt.Errorf("failed to create apply request: %w", err) + } + + return req. + NamespaceIfScoped(o.namespace, o.isNamespaced()). + Resource(o.resource()). + Name(o.name). + SubResource(subResource). + VersionedParams(applyOpts.AsPatchOptions(), uc.paramCodec). + Do(ctx). + Into(unstructuredApplyConfig.Unstructured) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go index 0603f4cde5..ee14638c3f 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/cluster.go @@ -19,13 +19,16 @@ package cluster import ( "context" "errors" + "fmt" "net/http" "github.com/go-logr/logr" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/scheme" + eventsv1client "k8s.io/client-go/kubernetes/typed/events/v1" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/events" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/cache" @@ -33,10 +36,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/apiutil" logf "sigs.k8s.io/controller-runtime/pkg/internal/log" intrec "sigs.k8s.io/controller-runtime/pkg/internal/recorder" + "sigs.k8s.io/controller-runtime/pkg/recorder" ) // Cluster provides various methods to interact with a cluster. type Cluster interface { + recorder.Provider + // GetHTTPClient returns an HTTP client that can be used to talk to the apiserver GetHTTPClient() *http.Client @@ -58,9 +64,6 @@ type Cluster interface { // GetFieldIndexer returns a client.FieldIndexer configured with the client GetFieldIndexer() client.FieldIndexer - // GetEventRecorderFor returns a new EventRecorder for the provided name - GetEventRecorderFor(name string) record.EventRecorder - // GetRESTMapper returns a RESTMapper GetRESTMapper() meta.RESTMapper @@ -160,8 +163,7 @@ func New(config *rest.Config, opts ...Option) (Cluster, error) { } options, err := setOptionsDefaults(options, config) if err != nil { - options.Logger.Error(err, "Failed to set defaults") - return nil, err + return nil, fmt.Errorf("failed setting cluster default options: %w", err) } // Create the mapper provider @@ -281,16 +283,24 @@ func setOptionsDefaults(options Options, config *rest.Config) (Options, error) { options.newRecorderProvider = intrec.NewProvider } + // This is duplicated with pkg/manager, we need it here to provide + // the user with an EventBroadcaster and there for the Leader election + evtCl, err := eventsv1client.NewForConfigAndClient(config, options.HTTPClient) + if err != nil { + return options, err + } + // This is duplicated with pkg/manager, we need it here to provide // the user with an EventBroadcaster and there for the Leader election if options.EventBroadcaster == nil { // defer initialization to avoid leaking by default - options.makeBroadcaster = func() (record.EventBroadcaster, bool) { - return record.NewBroadcaster(), true + options.makeBroadcaster = func() (record.EventBroadcaster, events.EventBroadcaster, bool) { + return record.NewBroadcaster(), events.NewBroadcaster(&events.EventSinkImpl{Interface: evtCl}), true } } else { - options.makeBroadcaster = func() (record.EventBroadcaster, bool) { - return options.EventBroadcaster, false + // keep supporting the options.EventBroadcaster in the old API, but do not introduce it for the new one. + options.makeBroadcaster = func() (record.EventBroadcaster, events.EventBroadcaster, bool) { + return options.EventBroadcaster, events.NewBroadcaster(&events.EventSinkImpl{Interface: evtCl}), false } } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go b/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go index 2742764231..755f83b546 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/cluster/internal.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/events" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/cache" @@ -87,6 +88,10 @@ func (c *cluster) GetEventRecorderFor(name string) record.EventRecorder { return c.recorderProvider.GetEventRecorderFor(name) } +func (c *cluster) GetEventRecorder(name string) events.EventRecorder { + return c.recorderProvider.GetEventRecorder(name) +} + func (c *cluster) GetRESTMapper() meta.RESTMapper { return c.mapper } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/config/controller.go b/vendor/sigs.k8s.io/controller-runtime/pkg/config/controller.go index 3dafaef93b..5eea2965f6 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/config/controller.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/config/controller.go @@ -79,7 +79,7 @@ type Controller struct { // UsePriorityQueue configures the controllers queue to use the controller-runtime provided // priority queue. // - // Note: This flag is disabled by default until a future version. This feature is currently in beta. + // Note: This flag is enabled by default. // For more details, see: https://github.com/kubernetes-sigs/controller-runtime/issues/2374. UsePriorityQueue *bool diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go index afa15aebec..853788d52f 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controller.go @@ -91,7 +91,7 @@ type TypedOptions[request comparable] struct { // UsePriorityQueue configures the controllers queue to use the controller-runtime provided // priority queue. // - // Note: This flag is disabled by default until a future version. This feature is currently in beta. + // Note: This flag is enabled by default. // For more details, see: https://github.com/kubernetes-sigs/controller-runtime/issues/2374. UsePriorityQueue *bool @@ -250,7 +250,7 @@ func NewTypedUnmanaged[request comparable](name string, options TypedOptions[req } if options.RateLimiter == nil { - if ptr.Deref(options.UsePriorityQueue, false) { + if ptr.Deref(options.UsePriorityQueue, true) { options.RateLimiter = workqueue.NewTypedItemExponentialFailureRateLimiter[request](5*time.Millisecond, 1000*time.Second) } else { options.RateLimiter = workqueue.DefaultTypedControllerRateLimiter[request]() @@ -259,7 +259,7 @@ func NewTypedUnmanaged[request comparable](name string, options TypedOptions[req if options.NewQueue == nil { options.NewQueue = func(controllerName string, rateLimiter workqueue.TypedRateLimiter[request]) workqueue.TypedRateLimitingInterface[request] { - if ptr.Deref(options.UsePriorityQueue, false) { + if ptr.Deref(options.UsePriorityQueue, true) { return priorityqueue.New(controllerName, func(o *priorityqueue.Opts[request]) { o.Log = options.Logger.WithValues("controller", controllerName) o.RateLimiter = rateLimiter diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go index 0088f88e5d..0f12b934ee 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/controllerutil/controllerutil.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "reflect" + "slices" "k8s.io/apimachinery/pkg/api/equality" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -501,10 +502,8 @@ type MutateFn func() error // It returns an indication of whether it updated the object's list of finalizers. func AddFinalizer(o client.Object, finalizer string) (finalizersUpdated bool) { f := o.GetFinalizers() - for _, e := range f { - if e == finalizer { - return false - } + if slices.Contains(f, finalizer) { + return false } o.SetFinalizers(append(f, finalizer)) return true @@ -517,7 +516,7 @@ func RemoveFinalizer(o client.Object, finalizer string) (finalizersUpdated bool) length := len(f) index := 0 - for i := 0; i < length; i++ { + for i := range length { if f[i] == finalizer { continue } @@ -531,10 +530,5 @@ func RemoveFinalizer(o client.Object, finalizer string) (finalizersUpdated bool) // ContainsFinalizer checks an Object that the provided finalizer is present. func ContainsFinalizer(o client.Object, finalizer string) bool { f := o.GetFinalizers() - for _, e := range f { - if e == finalizer { - return true - } - } - return false + return slices.Contains(f, finalizer) } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/priorityqueue/priorityqueue.go b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/priorityqueue/priorityqueue.go index 71363f0d17..fd10a6c050 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/controller/priorityqueue/priorityqueue.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/controller/priorityqueue/priorityqueue.go @@ -1,7 +1,6 @@ package priorityqueue import ( - "math" "sync" "sync/atomic" "time" @@ -30,6 +29,13 @@ type AddOpts struct { // internally de-duplicates all items that are added to // it. It will use the max of the passed priorities and the // min of possible durations. +// +// When an item that is already enqueued at a lower priority +// is re-enqueued with a higher priority, it will be placed at +// the end among items of the new priority, in order to +// preserve FIFO semantics within each priority level. +// The effective duration (i.e. the ready time) is still +// computed as the minimum across all enqueues. type PriorityQueue[T comparable] interface { workqueue.TypedRateLimitingInterface[T] AddWithOpts(o AddOpts, Items ...T) @@ -48,6 +54,11 @@ type Opts[T comparable] struct { // Opt allows to configure a PriorityQueue. type Opt[T comparable] func(*Opts[T]) +type bufferItem[T comparable] struct { + opts AddOpts + items []T +} + // New constructs a new PriorityQueue. func New[T comparable](name string, o ...Opt[T]) PriorityQueue[T] { opts := &Opts[T]{} @@ -64,25 +75,29 @@ func New[T comparable](name string, o ...Opt[T]) PriorityQueue[T] { } pq := &priorityqueue[T]{ - log: opts.Log, - items: map[T]*item[T]{}, - queue: btree.NewG(32, less[T]), - becameReady: sets.Set[T]{}, - metrics: newQueueMetrics[T](opts.MetricProvider, name, clock.RealClock{}), - // itemOrWaiterAdded indicates that an item or + log: opts.Log, + itemAddedToAddBuffer: make(chan struct{}, 1), + items: map[T]*item[T]{}, + ready: btree.NewG(32, lessReady[T]), + waiting: btree.NewG(32, lessWaiting[T]), + metrics: newQueueMetrics[T](opts.MetricProvider, name, clock.RealClock{}), + // readyItemOrWaiterAdded indicates that a ready item or // waiter was added. It must be buffered, because // if we currently process items we can't tell // if that included the new item/waiter. - itemOrWaiterAdded: make(chan struct{}, 1), - rateLimiter: opts.RateLimiter, - locked: sets.Set[T]{}, - done: make(chan struct{}), - get: make(chan item[T]), - now: time.Now, - tick: time.Tick, + readyItemOrWaiterAdded: make(chan struct{}, 1), + waitingItemAddedOrUpdated: make(chan struct{}, 1), + rateLimiter: opts.RateLimiter, + locked: sets.Set[T]{}, + done: make(chan struct{}), + get: make(chan item[T]), + now: time.Now, + tick: time.Tick, } - go pq.spin() + go pq.handleAddBuffer() + go pq.handleReadyItems() + go pq.handleWaitingItems() go pq.logState() if _, ok := pq.metrics.(noMetrics[T]); !ok { go pq.updateUnfinishedWorkLoop() @@ -93,30 +108,33 @@ func New[T comparable](name string, o ...Opt[T]) PriorityQueue[T] { type priorityqueue[T comparable] struct { log logr.Logger - // lock has to be acquired for any access any of items, queue, addedCounter - // or becameReady - lock sync.Mutex - items map[T]*item[T] - queue bTree[*item[T]] + + addBufferLock sync.Mutex + addBuffer []bufferItem[T] + itemAddedToAddBuffer chan struct{} + + // lock has to be acquired for any access to any of items, ready, waiting, + // addedCounter or waiters. + lock sync.Mutex + items map[T]*item[T] + ready bTree[*item[T]] + waiting bTree[*item[T]] // addedCounter is a counter of elements added, we need it - // because unixNano is not guaranteed to be unique. + // to provide FIFO semantics. addedCounter uint64 - // becameReady holds items that are in the queue, were added - // with non-zero after and became ready. We need it to call the - // metrics add exactly once for them. - becameReady sets.Set[T] - metrics queueMetrics[T] + metrics queueMetrics[T] - itemOrWaiterAdded chan struct{} + readyItemOrWaiterAdded chan struct{} + waitingItemAddedOrUpdated chan struct{} rateLimiter workqueue.TypedRateLimiter[T] // locked contains the keys we handed out through Get() and that haven't // yet been returned through Done(). locked sets.Set[T] - lockedLock sync.RWMutex + lockedLock sync.Mutex shutdown atomic.Bool done chan struct{} @@ -137,8 +155,52 @@ func (w *priorityqueue[T]) AddWithOpts(o AddOpts, items ...T) { return } - w.lock.Lock() - defer w.lock.Unlock() + if len(items) == 0 { + return + } + + w.addBufferLock.Lock() + w.addBuffer = append(w.addBuffer, bufferItem[T]{ + opts: o, + items: items, + }) + w.addBufferLock.Unlock() + + w.notifyItemAddedToAddBuffer() +} + +func (w *priorityqueue[T]) handleAddBuffer() { + for { + select { + case <-w.done: + return + case <-w.itemAddedToAddBuffer: + } + + w.lock.Lock() + w.lockedFlushAddBuffer() + w.lock.Unlock() + } +} + +func (w *priorityqueue[T]) lockedFlushAddBuffer() { + w.addBufferLock.Lock() + buffer := w.addBuffer + w.addBuffer = make([]bufferItem[T], 0, len(buffer)) + w.addBufferLock.Unlock() + + for _, v := range buffer { + w.lockedAddWithOpts(v.opts, v.items...) + } +} + +func (w *priorityqueue[T]) lockedAddWithOpts(o AddOpts, items ...T) { + if w.shutdown.Load() { + return + } + + var readyItemAdded bool + var waitingItemAddedOrUpdated bool for _, key := range items { after := o.After @@ -161,68 +223,162 @@ func (w *priorityqueue[T]) AddWithOpts(o AddOpts, items ...T) { Priority: ptr.Deref(o.Priority, 0), ReadyAt: readyAt, } + w.addedCounter++ w.items[key] = item - w.queue.ReplaceOrInsert(item) - if item.ReadyAt == nil { + if readyAt != nil { + w.waiting.ReplaceOrInsert(item) + waitingItemAddedOrUpdated = true + } else { + w.ready.ReplaceOrInsert(item) w.metrics.add(key, item.Priority) + readyItemAdded = true } - w.addedCounter++ continue } - // The b-tree de-duplicates based on ordering and any change here - // will affect the order - Just delete and re-add. - item, _ := w.queue.Delete(w.items[key]) - if newPriority := ptr.Deref(o.Priority, 0); newPriority > item.Priority { - // Update depth metric only if the item in the queue was already added to the depth metric. - if item.ReadyAt == nil || w.becameReady.Has(key) { - w.metrics.updateDepthWithPriorityMetric(item.Priority, newPriority) - } - item.Priority = newPriority + if w.items[key].ReadyAt == nil { + readyAt = nil + } else if readyAt != nil && w.items[key].ReadyAt.Before(*readyAt) { + readyAt = w.items[key].ReadyAt } - if item.ReadyAt != nil && (readyAt == nil || readyAt.Before(*item.ReadyAt)) { - if readyAt == nil && !w.becameReady.Has(key) { - w.metrics.add(key, item.Priority) + priority := w.items[key].Priority + addedCounter := w.items[key].AddedCounter + if newPriority := ptr.Deref(o.Priority, 0); newPriority > w.items[key].Priority { + // Update depth metric only if the item was already ready + if w.items[key].ReadyAt == nil { + w.metrics.updateDepthWithPriorityMetric(w.items[key].Priority, newPriority) } - item.ReadyAt = readyAt + priority = newPriority + addedCounter = w.addedCounter + w.addedCounter++ } - w.queue.ReplaceOrInsert(item) + var tree, previousTree bTree[*item[T]] + switch { + case readyAt == nil && w.items[key].ReadyAt == nil: + tree, previousTree = w.ready, w.ready + case readyAt == nil && w.items[key].ReadyAt != nil: + tree, previousTree = w.ready, w.waiting + readyItemAdded = true + w.metrics.add(key, priority) + case readyAt != nil: + // We are in the update path and we set readyAt to nil if the + // existing item has a nil readyAt, so we can be sure here that + // it has a non-nil readyAt/is in w.waiting. + tree, previousTree = w.waiting, w.waiting + waitingItemAddedOrUpdated = true + } + + item, _ := previousTree.Delete(w.items[key]) + item.ReadyAt = readyAt + item.Priority = priority + item.AddedCounter = addedCounter + tree.ReplaceOrInsert(item) + } + + if readyItemAdded { + w.notifyReadyItemOrWaiterAdded() + } + if waitingItemAddedOrUpdated { + w.notifyWaitingItemAddedOrUpdated() + } +} + +func (w *priorityqueue[T]) notifyItemAddedToAddBuffer() { + select { + case w.itemAddedToAddBuffer <- struct{}{}: + default: } +} - if len(items) > 0 { - w.notifyItemOrWaiterAdded() +func (w *priorityqueue[T]) notifyReadyItemOrWaiterAdded() { + select { + case w.readyItemOrWaiterAdded <- struct{}{}: + default: } } -func (w *priorityqueue[T]) notifyItemOrWaiterAdded() { +func (w *priorityqueue[T]) notifyWaitingItemAddedOrUpdated() { select { - case w.itemOrWaiterAdded <- struct{}{}: + case w.waitingItemAddedOrUpdated <- struct{}{}: default: } } -func (w *priorityqueue[T]) spin() { +func (w *priorityqueue[T]) handleWaitingItems() { blockForever := make(chan time.Time) var nextReady <-chan time.Time nextReady = blockForever - var nextItemReadyAt time.Time for { select { case <-w.done: return - case <-w.itemOrWaiterAdded: + case <-w.waitingItemAddedOrUpdated: case <-nextReady: nextReady = blockForever - nextItemReadyAt = time.Time{} } func() { w.lock.Lock() defer w.lock.Unlock() + var toMove []*item[T] + w.waiting.Ascend(func(item *item[T]) bool { + readyIn := item.ReadyAt.Sub(w.now()) // Store this to prevent TOCTOU issues + if readyIn <= 0 { + toMove = append(toMove, item) + return true + } + + nextReady = w.tick(readyIn) + return false + }) + + // Don't manipulate the tree from within Ascend + for _, toMove := range toMove { + w.waiting.Delete(toMove) + toMove.ReadyAt = nil + + // Bump added counter so items get sorted by when + // they became ready, not when they were added. + toMove.AddedCounter = w.addedCounter + w.addedCounter++ + + w.metrics.add(toMove.Key, toMove.Priority) + w.ready.ReplaceOrInsert(toMove) + } + + if len(toMove) > 0 { + w.notifyReadyItemOrWaiterAdded() + } + }() + } +} + +func (w *priorityqueue[T]) handleReadyItems() { + for { + select { + case <-w.done: + return + case <-w.readyItemOrWaiterAdded: + } + + func() { + w.lock.Lock() + defer w.lock.Unlock() + + // Flush is performed before reading items to avoid errors caused by asynchronous behavior, + // primarily for unit testing purposes. + // Successfully adding a ready item may result in an additional call to handleReadyItems(), + // but the cost is negligible. + w.lockedFlushAddBuffer() + + if w.waiters == 0 { + return + } + w.lockedLock.Lock() defer w.lockedLock.Unlock() @@ -230,69 +386,24 @@ func (w *priorityqueue[T]) spin() { // track what we want to delete and do it after we are done ascending. var toDelete []*item[T] - var key T - - // Items in the queue tree are sorted first by priority and second by readiness, so - // items with a lower priority might be ready further down in the queue. - // We iterate through the priorities high to low until we find a ready item - pivot := item[T]{ - Key: key, - AddedCounter: 0, - Priority: math.MaxInt, - ReadyAt: nil, - } - - for { - pivotChange := false - - w.queue.AscendGreaterOrEqual(&pivot, func(item *item[T]) bool { - // Item is locked, we can not hand it out - if w.locked.Has(item.Key) { - return true - } - - if item.ReadyAt != nil { - if readyAt := item.ReadyAt.Sub(w.now()); readyAt > 0 { - if nextItemReadyAt.After(*item.ReadyAt) || nextItemReadyAt.IsZero() { - nextReady = w.tick(readyAt) - nextItemReadyAt = *item.ReadyAt - } - - // Adjusting the pivot item moves the ascend to the next lower priority - pivot.Priority = item.Priority - 1 - pivotChange = true - return false - } - if !w.becameReady.Has(item.Key) { - w.metrics.add(item.Key, item.Priority) - w.becameReady.Insert(item.Key) - } - } - - if w.waiters == 0 { - // Have to keep iterating here to ensure we update metrics - // for further items that became ready and set nextReady. - return true - } - - w.metrics.get(item.Key, item.Priority) - w.locked.Insert(item.Key) - w.waiters-- - delete(w.items, item.Key) - toDelete = append(toDelete, item) - w.becameReady.Delete(item.Key) - w.get <- *item - + w.ready.Ascend(func(item *item[T]) bool { + // Item is locked, we can not hand it out + if w.locked.Has(item.Key) { return true - }) - - if !pivotChange { - break } - } + + w.metrics.get(item.Key, item.Priority) + w.locked.Insert(item.Key) + w.waiters-- + delete(w.items, item.Key) + toDelete = append(toDelete, item) + w.get <- *item + + return w.waiters > 0 + }) for _, item := range toDelete { - w.queue.Delete(item) + w.ready.Delete(item) } }() } @@ -320,7 +431,7 @@ func (w *priorityqueue[T]) GetWithPriority() (_ T, priority int, shutdown bool) w.waiters++ w.lock.Unlock() - w.notifyItemOrWaiterAdded() + w.notifyReadyItemOrWaiterAdded() select { case <-w.done: @@ -358,7 +469,7 @@ func (w *priorityqueue[T]) Done(item T) { defer w.lockedLock.Unlock() w.locked.Delete(item) w.metrics.done(item) - w.notifyItemOrWaiterAdded() + w.notifyReadyItemOrWaiterAdded() } func (w *priorityqueue[T]) ShutDown() { @@ -379,16 +490,11 @@ func (w *priorityqueue[T]) Len() int { w.lock.Lock() defer w.lock.Unlock() - var result int - w.queue.Ascend(func(item *item[T]) bool { - if item.ReadyAt == nil || item.ReadyAt.Compare(w.now()) <= 0 { - result++ - return true - } - return false - }) + // Flush is performed before reading items to avoid errors caused by asynchronous behavior, + // primarily for unit testing purposes. + w.lockedFlushAddBuffer() - return result + return w.ready.Len() } func (w *priorityqueue[T]) logState() { @@ -408,7 +514,11 @@ func (w *priorityqueue[T]) logState() { } w.lock.Lock() items := make([]*item[T], 0, len(w.items)) - w.queue.Ascend(func(item *item[T]) bool { + w.waiting.Ascend(func(item *item[T]) bool { + items = append(items, item) + return true + }) + w.ready.Ascend(func(item *item[T]) bool { items = append(items, item) return true }) @@ -418,20 +528,17 @@ func (w *priorityqueue[T]) logState() { } } -func less[T comparable](a, b *item[T]) bool { - if a.Priority != b.Priority { - return a.Priority > b.Priority - } - if a.ReadyAt == nil && b.ReadyAt != nil { - return true - } - if b.ReadyAt == nil && a.ReadyAt != nil { - return false - } - if a.ReadyAt != nil && b.ReadyAt != nil && !a.ReadyAt.Equal(*b.ReadyAt) { +func lessWaiting[T comparable](a, b *item[T]) bool { + if !a.ReadyAt.Equal(*b.ReadyAt) { return a.ReadyAt.Before(*b.ReadyAt) } + return lessReady(a, b) +} +func lessReady[T comparable](a, b *item[T]) bool { + if a.Priority != b.Priority { + return a.Priority > b.Priority + } return a.AddedCounter < b.AddedCounter } @@ -455,8 +562,8 @@ func (w *priorityqueue[T]) updateUnfinishedWorkLoop() { } type bTree[T any] interface { - ReplaceOrInsert(item T) (_ T, _ bool) + ReplaceOrInsert(item T) (T, bool) Delete(item T) (T, bool) Ascend(iterator btree.ItemIteratorG[T]) - AscendGreaterOrEqual(pivot T, iterator btree.ItemIteratorG[T]) + Len() int } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/healthz/healthz.go b/vendor/sigs.k8s.io/controller-runtime/pkg/healthz/healthz.go index cfb5dc8d02..149b02ec98 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/healthz/healthz.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/healthz/healthz.go @@ -20,7 +20,7 @@ import ( "fmt" "net/http" "path" - "sort" + "slices" "strings" "k8s.io/apimachinery/pkg/util/sets" @@ -75,7 +75,7 @@ func (h *Handler) serveAggregated(resp http.ResponseWriter, req *http.Request) { } // ...sort to be consistent... - sort.Slice(parts, func(i, j int) bool { return parts[i].name < parts[j].name }) + slices.SortStableFunc(parts, func(i, j checkStatus) int { return strings.Compare(i.name, j.name) }) // ...and write out the result // TODO(directxman12): this should also accept a request for JSON content (via a accept header) diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go index ea79681862..f2638b9d9b 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/controller.go @@ -39,6 +39,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" ) +// errReconciliationTimeout is the error used as the cause when the ReconciliationTimeout guardrail fires. +// This allows us to distinguish wrapper timeouts from user-initiated context cancellations. +var errReconciliationTimeout = errors.New("reconciliation timeout") + // Options are the arguments for creating a new Controller. type Options[request comparable] struct { // Reconciler is a function that can be called at any time with the Name / Namespace of an object and @@ -207,13 +211,26 @@ func (c *Controller[request]) Reconcile(ctx context.Context, req request) (_ rec } }() + var timeoutCause error if c.ReconciliationTimeout > 0 { + timeoutCause = errReconciliationTimeout var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, c.ReconciliationTimeout) + ctx, cancel = context.WithTimeoutCause(ctx, c.ReconciliationTimeout, timeoutCause) defer cancel() } - return c.Do.Reconcile(ctx, req) + res, err := c.Do.Reconcile(ctx, req) + + // Check if the reconciliation timed out due to our wrapper timeout guardrail. + // We check ctx.Err() == context.DeadlineExceeded first to ensure the context was actually + // cancelled due to a deadline (not parent cancellation or other reasons), then verify it was + // our specific timeout cause. This prevents false positives from parent context cancellations + // or other timeout scenarios. + if timeoutCause != nil && ctx.Err() == context.DeadlineExceeded && errors.Is(context.Cause(ctx), timeoutCause) { + ctrlmetrics.ReconcileTimeouts.WithLabelValues(c.Name).Inc() + } + + return res, err } // Watch implements controller.Controller. @@ -437,6 +454,7 @@ func (c *Controller[request]) initMetrics() { ctrlmetrics.ReconcileErrors.WithLabelValues(c.Name).Add(0) ctrlmetrics.TerminalReconcileErrors.WithLabelValues(c.Name).Add(0) ctrlmetrics.ReconcilePanics.WithLabelValues(c.Name).Add(0) + ctrlmetrics.ReconcileTimeouts.WithLabelValues(c.Name).Add(0) ctrlmetrics.WorkerCount.WithLabelValues(c.Name).Set(float64(c.MaxConcurrentReconciles)) ctrlmetrics.ActiveWorkers.WithLabelValues(c.Name).Set(0) } @@ -459,6 +477,9 @@ func (c *Controller[request]) reconcileHandler(ctx context.Context, req request, // resource to be synced. log.V(5).Info("Reconciling") result, err := c.Reconcile(ctx, req) + if result.Priority != nil { + priority = *result.Priority + } switch { case err != nil: if errors.Is(err, reconcile.TerminalError(nil)) { @@ -468,8 +489,8 @@ func (c *Controller[request]) reconcileHandler(ctx context.Context, req request, } ctrlmetrics.ReconcileErrors.WithLabelValues(c.Name).Inc() ctrlmetrics.ReconcileTotal.WithLabelValues(c.Name, labelError).Inc() - if !result.IsZero() { - log.Info("Warning: Reconciler returned both a non-zero result and a non-nil error. The result will always be ignored if the error is non-nil and the non-nil error causes requeuing with exponential backoff. For more details, see: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/reconcile#Reconciler") + if result.RequeueAfter > 0 || result.Requeue { //nolint: staticcheck // We have to handle Requeue until it is removed + log.Info("Warning: Reconciler returned both a result with either RequeueAfter or Requeue set and a non-nil error. RequeueAfter and Requeue will always be ignored if the error is non-nil. For more details, see: https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/reconcile#Reconciler") } log.Error(err, "Reconciler error") case result.RequeueAfter > 0: diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go index 450e9ae25b..39b435c453 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/controller/metrics/metrics.go @@ -80,6 +80,15 @@ var ( Name: "controller_runtime_active_workers", Help: "Number of currently used workers per controller", }, []string{"controller"}) + + // ReconcileTimeouts is a prometheus counter metric which holds the total + // number of reconciliations that timed out due to the ReconciliationTimeout + // context timeout. This metric only increments when the wrapper timeout fires, + // not when user reconcilers cancels the context or completes before the timeout. + ReconcileTimeouts = prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "controller_runtime_reconcile_timeouts_total", + Help: "Total number of reconciliation timeouts per controller", + }, []string{"controller"}) ) func init() { @@ -91,6 +100,7 @@ func init() { ReconcileTime, WorkerCount, ActiveWorkers, + ReconcileTimeouts, // expose process metrics like CPU, Memory, file descriptor usage etc. collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), // expose all Go runtime metrics like GC stats, memory stats etc. diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/metrics/workqueue.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/metrics/workqueue.go index 402319817b..49180457a6 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/metrics/workqueue.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/metrics/workqueue.go @@ -18,9 +18,11 @@ package metrics import ( "strconv" + "sync" "time" "github.com/prometheus/client_golang/prometheus" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/util/workqueue" "sigs.k8s.io/controller-runtime/pkg/metrics" ) @@ -154,17 +156,55 @@ type DepthMetricWithPriority interface { var _ MetricsProviderWithPriority = WorkqueueMetricsProvider{} func (WorkqueueMetricsProvider) NewDepthMetricWithPriority(name string) DepthMetricWithPriority { - return &depthWithPriorityMetric{lvs: []string{name, name}} + return &depthWithPriorityMetric{depth: depth, lvs: []string{name, name}, observedPriorities: sets.Set[int]{}} } +type prometheusGaugeVec interface { + WithLabelValues(lvs ...string) prometheus.Gauge +} + +const ( + priorityCardinalityExceededPlaceholder = "exceeded_cardinality_limit" + // maxRecommendedUniquePriorities is not scientifically chosen, we assume + // that the 99% use-case is to only use the two priorities that c-r itself + // uses and then leave a bit of leeway for other use-cases. + // We may decide to update this value in the future if we find that a + // a different value is more appropriate. + maxRecommendedUniquePriorities = 25 +) + type depthWithPriorityMetric struct { - lvs []string + depth prometheusGaugeVec + lvs []string + + observedPrioritiesLock sync.Mutex + priorityCardinalityLimitReached bool + observedPriorities sets.Set[int] +} + +func (g *depthWithPriorityMetric) priorityLabel(priority int) string { + g.observedPrioritiesLock.Lock() + defer g.observedPrioritiesLock.Unlock() + + if g.priorityCardinalityLimitReached { + return priorityCardinalityExceededPlaceholder + } + + g.observedPriorities.Insert(priority) + + if g.observedPriorities.Len() > maxRecommendedUniquePriorities { + g.observedPriorities = nil + g.priorityCardinalityLimitReached = true + return priorityCardinalityExceededPlaceholder + } + + return strconv.Itoa(priority) } func (g *depthWithPriorityMetric) Inc(priority int) { - depth.WithLabelValues(append(g.lvs, strconv.Itoa(priority))...).Inc() + g.depth.WithLabelValues(append(g.lvs, g.priorityLabel(priority))...).Inc() } func (g *depthWithPriorityMetric) Dec(priority int) { - depth.WithLabelValues(append(g.lvs, strconv.Itoa(priority))...).Dec() + g.depth.WithLabelValues(append(g.lvs, g.priorityLabel(priority))...).Dec() } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go index 21f0146ba3..bbc1604835 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/recorder/recorder.go @@ -24,16 +24,19 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + eventsv1 "k8s.io/api/events/v1" "k8s.io/apimachinery/pkg/runtime" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/events" "k8s.io/client-go/tools/record" ) // EventBroadcasterProducer makes an event broadcaster, returning // whether or not the broadcaster should be stopped with the Provider, // or not (e.g. if it's shared, it shouldn't be stopped with the Provider). -type EventBroadcasterProducer func() (caster record.EventBroadcaster, stopWithProvider bool) +// This producer currently produces both an old API and a new API broadcaster. +type EventBroadcasterProducer func() (deprecatedCaster record.EventBroadcaster, caster events.EventBroadcaster, stopWithProvider bool) // Provider is a recorder.Provider that records events to the k8s API server // and to a logr Logger. @@ -48,9 +51,13 @@ type Provider struct { evtClient corev1client.EventInterface makeBroadcaster EventBroadcasterProducer - broadcasterOnce sync.Once - broadcaster record.EventBroadcaster - stopBroadcaster bool + broadcasterOnce sync.Once + broadcaster events.EventBroadcaster + cancelSinkRecordingFunc context.CancelFunc + stopWatcherFunc func() + // Deprecated: will be removed in a future release. Use the broadcaster above instead. + deprecatedBroadcaster record.EventBroadcaster + stopBroadcaster bool } // NB(directxman12): this manually implements Stop instead of Being a runnable because we need to @@ -71,10 +78,13 @@ func (p *Provider) Stop(shutdownCtx context.Context) { // almost certainly already been started (e.g. by leader election). We // need to invoke this to ensure that we don't inadvertently race with // an invocation of getBroadcaster. - broadcaster := p.getBroadcaster() + deprecatedBroadcaster, broadcaster := p.getBroadcaster() if p.stopBroadcaster { p.lock.Lock() broadcaster.Shutdown() + p.cancelSinkRecordingFunc() + p.stopWatcherFunc() + deprecatedBroadcaster.Shutdown() p.stopped = true p.lock.Unlock() } @@ -89,7 +99,7 @@ func (p *Provider) Stop(shutdownCtx context.Context) { // getBroadcaster ensures that a broadcaster is started for this // provider, and returns it. It's threadsafe. -func (p *Provider) getBroadcaster() record.EventBroadcaster { +func (p *Provider) getBroadcaster() (record.EventBroadcaster, events.EventBroadcaster) { // NB(directxman12): this can technically still leak if something calls // "getBroadcaster" (i.e. Emits an Event) but never calls Start, but if we // create the broadcaster in start, we could race with other things that @@ -97,17 +107,37 @@ func (p *Provider) getBroadcaster() record.EventBroadcaster { // silently swallowing events and more locking, but that seems suboptimal. p.broadcasterOnce.Do(func() { - broadcaster, stop := p.makeBroadcaster() - broadcaster.StartRecordingToSink(&corev1client.EventSinkImpl{Interface: p.evtClient}) - broadcaster.StartEventWatcher( + p.deprecatedBroadcaster, p.broadcaster, p.stopBroadcaster = p.makeBroadcaster() + + // init deprecated broadcaster + p.deprecatedBroadcaster.StartRecordingToSink(&corev1client.EventSinkImpl{Interface: p.evtClient}) + p.deprecatedBroadcaster.StartEventWatcher( func(e *corev1.Event) { p.logger.V(1).Info(e.Message, "type", e.Type, "object", e.InvolvedObject, "reason", e.Reason) }) - p.broadcaster = broadcaster - p.stopBroadcaster = stop + + // init new broadcaster + ctx, cancel := context.WithCancel(context.Background()) + p.cancelSinkRecordingFunc = cancel + if err := p.broadcaster.StartRecordingToSinkWithContext(ctx); err != nil { + p.logger.Error(err, "error starting recording for broadcaster") + return + } + + stopWatcher, err := p.broadcaster.StartEventWatcher(func(event runtime.Object) { + e, isEvt := event.(*eventsv1.Event) + if isEvt { + p.logger.V(1).Info(e.Note, "type", e.Type, "object", e.Related, "action", e.Action, "reason", e.Reason) + } + }) + if err != nil { + p.logger.Error(err, "error starting event watcher for broadcaster") + } + + p.stopWatcherFunc = stopWatcher }) - return p.broadcaster + return p.deprecatedBroadcaster, p.broadcaster } // NewProvider create a new Provider instance. @@ -128,6 +158,15 @@ func NewProvider(config *rest.Config, httpClient *http.Client, scheme *runtime.S // GetEventRecorderFor returns an event recorder that broadcasts to this provider's // broadcaster. All events will be associated with a component of the given name. func (p *Provider) GetEventRecorderFor(name string) record.EventRecorder { + return &deprecatedRecorder{ + prov: p, + name: name, + } +} + +// GetEventRecorder returns an event recorder that broadcasts to this provider's +// broadcaster. All events will be associated with a component of the given name. +func (p *Provider) GetEventRecorder(name string) events.EventRecorder { return &lazyRecorder{ prov: p, name: name, @@ -141,18 +180,47 @@ type lazyRecorder struct { name string recOnce sync.Once - rec record.EventRecorder + rec events.EventRecorder } // ensureRecording ensures that a concrete recorder is populated for this recorder. func (l *lazyRecorder) ensureRecording() { l.recOnce.Do(func() { - broadcaster := l.prov.getBroadcaster() - l.rec = broadcaster.NewRecorder(l.prov.scheme, corev1.EventSource{Component: l.name}) + _, broadcaster := l.prov.getBroadcaster() + l.rec = broadcaster.NewRecorder(l.prov.scheme, l.name) }) } -func (l *lazyRecorder) Event(object runtime.Object, eventtype, reason, message string) { +func (l *lazyRecorder) Eventf(regarding runtime.Object, related runtime.Object, eventtype, reason, action, note string, args ...any) { + l.ensureRecording() + + l.prov.lock.RLock() + if !l.prov.stopped { + l.rec.Eventf(regarding, related, eventtype, reason, action, note, args...) + } + l.prov.lock.RUnlock() +} + +// deprecatedRecorder implements the old events API during the tranisiton and will be removed in a future release. +// +// Deprecated: will be removed in a future release. +type deprecatedRecorder struct { + prov *Provider + name string + + recOnce sync.Once + rec record.EventRecorder +} + +// ensureRecording ensures that a concrete recorder is populated for this recorder. +func (l *deprecatedRecorder) ensureRecording() { + l.recOnce.Do(func() { + deprecatedBroadcaster, _ := l.prov.getBroadcaster() + l.rec = deprecatedBroadcaster.NewRecorder(l.prov.scheme, corev1.EventSource{Component: l.name}) + }) +} + +func (l *deprecatedRecorder) Event(object runtime.Object, eventtype, reason, message string) { l.ensureRecording() l.prov.lock.RLock() @@ -161,7 +229,8 @@ func (l *lazyRecorder) Event(object runtime.Object, eventtype, reason, message s } l.prov.lock.RUnlock() } -func (l *lazyRecorder) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...interface{}) { + +func (l *deprecatedRecorder) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...any) { l.ensureRecording() l.prov.lock.RLock() @@ -170,7 +239,8 @@ func (l *lazyRecorder) Eventf(object runtime.Object, eventtype, reason, messageF } l.prov.lock.RUnlock() } -func (l *lazyRecorder) AnnotatedEventf(object runtime.Object, annotations map[string]string, eventtype, reason, messageFmt string, args ...interface{}) { + +func (l *deprecatedRecorder) AnnotatedEventf(object runtime.Object, annotations map[string]string, eventtype, reason, messageFmt string, args ...any) { l.ensureRecording() l.prov.lock.RLock() diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/event_handler.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/event_handler.go index 7cc8c51555..9d614f34a5 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/event_handler.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/event_handler.go @@ -60,7 +60,7 @@ type EventHandler[object client.Object, request comparable] struct { } // OnAdd creates CreateEvent and calls Create on EventHandler. -func (e *EventHandler[object, request]) OnAdd(obj interface{}, isInInitialList bool) { +func (e *EventHandler[object, request]) OnAdd(obj any, isInInitialList bool) { c := event.TypedCreateEvent[object]{ IsInInitialList: isInInitialList, } @@ -87,7 +87,7 @@ func (e *EventHandler[object, request]) OnAdd(obj interface{}, isInInitialList b } // OnUpdate creates UpdateEvent and calls Update on EventHandler. -func (e *EventHandler[object, request]) OnUpdate(oldObj, newObj interface{}) { +func (e *EventHandler[object, request]) OnUpdate(oldObj, newObj any) { u := event.TypedUpdateEvent[object]{} if o, ok := oldObj.(object); ok { @@ -120,7 +120,7 @@ func (e *EventHandler[object, request]) OnUpdate(oldObj, newObj interface{}) { } // OnDelete creates DeleteEvent and calls Delete on EventHandler. -func (e *EventHandler[object, request]) OnDelete(obj interface{}) { +func (e *EventHandler[object, request]) OnDelete(obj any) { d := event.TypedDeleteEvent[object]{} // Deal with tombstone events by pulling the object out. Tombstone events wrap the object in a diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/kind.go b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/kind.go index 2854244523..a28aeb177e 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/kind.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/internal/source/kind.go @@ -91,16 +91,23 @@ func (ks *Kind[object, request]) Start(ctx context.Context, queue workqueue.Type return } - _, err := i.AddEventHandlerWithOptions(NewEventHandler(ctx, queue, ks.Handler, ks.Predicates), toolscache.HandlerOptions{ + handlerRegistration, err := i.AddEventHandlerWithOptions(NewEventHandler(ctx, queue, ks.Handler, ks.Predicates), toolscache.HandlerOptions{ Logger: &logKind, }) if err != nil { ks.startedErr <- err return } + // First, wait for the cache to sync. For real caches this waits for startup. + // For fakes with Synced=false, this returns immediately allowing fast failure. if !ks.Cache.WaitForCacheSync(ctx) { - // Would be great to return something more informative here ks.startedErr <- errors.New("cache did not sync") + close(ks.startedErr) + return + } + // Then wait for this specific handler to receive all initial events. + if !toolscache.WaitForCacheSync(ctx.Done(), handlerRegistration.HasSynced) { + ks.startedErr <- errors.New("handler did not sync") } close(ks.startedErr) }() diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/leader_election.go b/vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/leader_election.go index 6c013e7992..7f59d82897 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/leader_election.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/leaderelection/leader_election.go @@ -103,10 +103,7 @@ func NewResourceLock(config *rest.Config, recorderProvider recorder.Provider, op // RenewDeadline to keep a single hung request from forcing a leader loss. // Setting it to max(time.Second, RenewDeadline/2) as a reasonable heuristic. if options.RenewDeadline != 0 { - timeout := options.RenewDeadline / 2 - if timeout < time.Second { - timeout = time.Second - } + timeout := max(options.RenewDeadline/2, time.Second) config.Timeout = timeout } @@ -127,8 +124,10 @@ func NewResourceLock(config *rest.Config, recorderProvider recorder.Provider, op corev1Client, coordinationClient, resourcelock.ResourceLockConfig{ - Identity: id, - EventRecorder: recorderProvider.GetEventRecorderFor(id), + Identity: id, + // TODO(clebs): Replace with the new events API after leader election is updated upstream. + // REF: https://github.com/kubernetes/kubernetes/issues/82846 + EventRecorder: recorderProvider.GetEventRecorderFor(id), //nolint:staticcheck }, options.LeaderLabels, ) diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go b/vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go index 6eb551d3b6..948330b01d 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/log/deleg.go @@ -30,7 +30,7 @@ type loggerPromise struct { promisesLock sync.Mutex name *string - tags []interface{} + tags []any } func (p *loggerPromise) WithName(l *delegatingLogSink, name string) *loggerPromise { @@ -47,7 +47,7 @@ func (p *loggerPromise) WithName(l *delegatingLogSink, name string) *loggerPromi } // WithValues provides a new Logger with the tags appended. -func (p *loggerPromise) WithValues(l *delegatingLogSink, tags ...interface{}) *loggerPromise { +func (p *loggerPromise) WithValues(l *delegatingLogSink, tags ...any) *loggerPromise { res := &loggerPromise{ logger: l, tags: tags, @@ -120,7 +120,7 @@ func (l *delegatingLogSink) Enabled(level int) bool { // the log line. The key/value pairs can then be used to add additional // variable information. The key/value pairs should alternate string // keys and arbitrary values. -func (l *delegatingLogSink) Info(level int, msg string, keysAndValues ...interface{}) { +func (l *delegatingLogSink) Info(level int, msg string, keysAndValues ...any) { eventuallyFulfillRoot() l.lock.RLock() defer l.lock.RUnlock() @@ -135,7 +135,7 @@ func (l *delegatingLogSink) Info(level int, msg string, keysAndValues ...interfa // The msg field should be used to add context to any underlying error, // while the err field should be used to attach the actual error that // triggered this log line, if present. -func (l *delegatingLogSink) Error(err error, msg string, keysAndValues ...interface{}) { +func (l *delegatingLogSink) Error(err error, msg string, keysAndValues ...any) { eventuallyFulfillRoot() l.lock.RLock() defer l.lock.RUnlock() @@ -164,7 +164,7 @@ func (l *delegatingLogSink) WithName(name string) logr.LogSink { } // WithValues provides a new Logger with the tags appended. -func (l *delegatingLogSink) WithValues(tags ...interface{}) logr.LogSink { +func (l *delegatingLogSink) WithValues(tags ...any) logr.LogSink { eventuallyFulfillRoot() l.lock.RLock() defer l.lock.RUnlock() diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go b/vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go index ade21d6fb5..48a4d490d3 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/log/log.go @@ -88,7 +88,7 @@ var ( ) // FromContext returns a logger with predefined values from a context.Context. -func FromContext(ctx context.Context, keysAndValues ...interface{}) logr.Logger { +func FromContext(ctx context.Context, keysAndValues ...any) logr.Logger { log := Log if ctx != nil { if logger, err := logr.FromContext(ctx); err == nil { diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/log/null.go b/vendor/sigs.k8s.io/controller-runtime/pkg/log/null.go index f3e81074fe..f8dd84ca65 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/log/null.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/log/null.go @@ -34,7 +34,7 @@ func (log NullLogSink) Init(logr.RuntimeInfo) { } // Info implements logr.InfoLogger. -func (NullLogSink) Info(_ int, _ string, _ ...interface{}) { +func (NullLogSink) Info(_ int, _ string, _ ...any) { // Do nothing. } @@ -44,7 +44,7 @@ func (NullLogSink) Enabled(level int) bool { } // Error implements logr.Logger. -func (NullLogSink) Error(_ error, _ string, _ ...interface{}) { +func (NullLogSink) Error(_ error, _ string, _ ...any) { // Do nothing. } @@ -54,6 +54,6 @@ func (log NullLogSink) WithName(_ string) logr.LogSink { } // WithValues implements logr.Logger. -func (log NullLogSink) WithValues(_ ...interface{}) logr.LogSink { +func (log NullLogSink) WithValues(_ ...any) logr.LogSink { return log } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/log/zap/flags.go b/vendor/sigs.k8s.io/controller-runtime/pkg/log/zap/flags.go index 2c88ad42ab..4ebac57dcb 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/log/zap/flags.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/log/zap/flags.go @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package zap contains helpers for setting up a new logr.Logger instance -// using the Zap logging framework. package zap import ( diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go index a9f91cbdd5..187d4f56c2 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/internal.go @@ -32,9 +32,11 @@ import ( "k8s.io/apimachinery/pkg/runtime" kerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/events" "k8s.io/client-go/tools/leaderelection" "k8s.io/client-go/tools/leaderelection/resourcelock" "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/webhook/conversion" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" @@ -129,6 +131,9 @@ type controllerManager struct { // webhookServer if unset, and Add() it to controllerManager. webhookServerOnce sync.Once + // converterRegistry stores conversion.Converter for the conversion endpoint. + converterRegistry conversion.Registry + // leaderElectionID is the name of the resource that leader election // will use for holding the leader lock. leaderElectionID string @@ -256,7 +261,11 @@ func (cm *controllerManager) GetCache() cache.Cache { } func (cm *controllerManager) GetEventRecorderFor(name string) record.EventRecorder { - return cm.cluster.GetEventRecorderFor(name) + return cm.cluster.GetEventRecorderFor(name) //nolint:staticcheck +} + +func (cm *controllerManager) GetEventRecorder(name string) events.EventRecorder { + return cm.cluster.GetEventRecorder(name) } func (cm *controllerManager) GetRESTMapper() meta.RESTMapper { @@ -279,6 +288,10 @@ func (cm *controllerManager) GetWebhookServer() webhook.Server { return cm.webhookServer } +func (cm *controllerManager) GetConverterRegistry() conversion.Registry { + return cm.converterRegistry +} + func (cm *controllerManager) GetLogger() logr.Logger { return cm.logger } @@ -446,13 +459,16 @@ func (cm *controllerManager) Start(ctx context.Context) (err error) { // Start the leader election and all required runnables. { - ctx, cancel := context.WithCancel(context.Background()) + // Create a context that inherits all keys from the parent context + // but can be cancelled independently for leader election management + baseCtx := context.WithoutCancel(ctx) + leaderCtx, cancel := context.WithCancel(baseCtx) cm.leaderElectionCancel = cancel if leaderElector != nil { // Start the leader elector process go func() { - leaderElector.Run(ctx) - <-ctx.Done() + leaderElector.Run(leaderCtx) + <-leaderCtx.Done() close(cm.leaderElectionStopped) }() } else { diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go index e0e94245e7..af532ea741 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/manager.go @@ -29,11 +29,14 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" + eventsv1client "k8s.io/client-go/kubernetes/typed/events/v1" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/events" "k8s.io/client-go/tools/leaderelection/resourcelock" "k8s.io/client-go/tools/record" "k8s.io/utils/ptr" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook/conversion" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" @@ -95,6 +98,10 @@ type Manager interface { // GetControllerOptions returns controller global configuration options. GetControllerOptions() config.Controller + + // GetConverterRegistry returns the converter registry that is used to store conversion.Converter + // for the conversion endpoint. + GetConverterRegistry() conversion.Registry } // Options are the arguments for creating a new Manager. @@ -337,7 +344,10 @@ func New(config *rest.Config, options Options) (Manager, error) { return nil, errors.New("must specify Config") } // Set default values for options fields - options = setOptionsDefaults(options) + options, err := setOptionsDefaults(config, options) + if err != nil { + return nil, fmt.Errorf("failed setting manager default options: %w", err) + } cluster, err := cluster.New(config, func(clusterOptions *cluster.Options) { clusterOptions.Scheme = options.Scheme @@ -445,6 +455,7 @@ func New(config *rest.Config, options Options) (Manager, error) { logger: options.Logger, elected: make(chan struct{}), webhookServer: options.WebhookServer, + converterRegistry: conversion.NewRegistry(), leaderElectionID: options.LeaderElectionID, leaseDuration: *options.LeaseDuration, renewDeadline: *options.RenewDeadline, @@ -493,7 +504,7 @@ func defaultBaseContext() context.Context { } // setOptionsDefaults set default values for Options fields. -func setOptionsDefaults(options Options) Options { +func setOptionsDefaults(config *rest.Config, options Options) (Options, error) { // Allow newResourceLock to be mocked if options.newResourceLock == nil { options.newResourceLock = leaderelection.NewResourceLock @@ -507,14 +518,25 @@ func setOptionsDefaults(options Options) Options { // This is duplicated with pkg/cluster, we need it here // for the leader election and there to provide the user with // an EventBroadcaster + httpClient, err := rest.HTTPClientFor(config) + if err != nil { + return options, err + } + + evtCl, err := eventsv1client.NewForConfigAndClient(config, httpClient) + if err != nil { + return options, err + } + if options.EventBroadcaster == nil { // defer initialization to avoid leaking by default - options.makeBroadcaster = func() (record.EventBroadcaster, bool) { - return record.NewBroadcaster(), true + options.makeBroadcaster = func() (record.EventBroadcaster, events.EventBroadcaster, bool) { + return record.NewBroadcaster(), events.NewBroadcaster(&events.EventSinkImpl{Interface: evtCl}), true } } else { - options.makeBroadcaster = func() (record.EventBroadcaster, bool) { - return options.EventBroadcaster, false + // keep supporting the options.EventBroadcaster in the old API, but do not introduce it for the new one. + options.makeBroadcaster = func() (record.EventBroadcaster, events.EventBroadcaster, bool) { + return options.EventBroadcaster, events.NewBroadcaster(&events.EventSinkImpl{Interface: evtCl}), false } } @@ -571,5 +593,5 @@ func setOptionsDefaults(options Options) Options { options.WebhookServer = webhook.NewServer(webhook.Options{}) } - return options + return options, nil } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_posix.go b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_posix.go index a0f00a7321..2b24faa428 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_posix.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/manager/signals/signal_posix.go @@ -1,5 +1,4 @@ //go:build !windows -// +build !windows /* Copyright 2017 The Kubernetes Authors. diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go b/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go index c98b1864ef..88303ae781 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/reconcile/reconcile.go @@ -44,6 +44,11 @@ type Result struct { // RequeueAfter if greater than 0, tells the Controller to requeue the reconcile key after the Duration. // Implies that Requeue is true, there is no need to set Requeue to true at the same time as RequeueAfter. RequeueAfter time.Duration + + // Priority is the priority that will be used if the item gets re-enqueued (also if an error is returned). + // If Priority is not set the original Priority of the request is preserved. + // Note: Priority is only respected if the controller is using a priorityqueue.PriorityQueue. + Priority *int } // IsZero returns true if this result is empty. @@ -174,7 +179,7 @@ type terminalError struct { err error } -// This function will return nil if te.err is nil. +// Unwrap returns nil if te.err is nil. func (te *terminalError) Unwrap() error { return te.err } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/recorder/recorder.go b/vendor/sigs.k8s.io/controller-runtime/pkg/recorder/recorder.go index f093f0a726..b34fecb525 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/recorder/recorder.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/recorder/recorder.go @@ -21,11 +21,16 @@ limitations under the License. package recorder import ( + "k8s.io/client-go/tools/events" "k8s.io/client-go/tools/record" ) // Provider knows how to generate new event recorders with given name. type Provider interface { - // NewRecorder returns an EventRecorder with given name. + // GetEventRecorderFor returns an EventRecorder for the old events API. + // + // Deprecated: this uses the old events API and will be removed in a future release. Please use GetEventRecorder instead. GetEventRecorderFor(name string) record.EventRecorder + // GetEventRecorder returns a EventRecorder with given name. + GetEventRecorder(name string) events.EventRecorder } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go index 55f1cafb5e..576262cf70 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/decode.go @@ -79,7 +79,7 @@ func (d *decoder) DecodeRaw(rawObj runtime.RawExtension, into runtime.Object) er } if unstructuredInto, isUnstructured := into.(runtime.Unstructured); isUnstructured { // unmarshal into unstructured's underlying object to avoid calling the decoder - var object map[string]interface{} + var object map[string]any if err := json.Unmarshal(rawObj.Raw, &object); err != nil { return err } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go index a703cbd2c5..9fec8003f2 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/defaulter_custom.go @@ -21,6 +21,7 @@ import ( "encoding/json" "errors" "net/http" + "reflect" "slices" "gomodules.xyz/jsonpatch/v2" @@ -31,11 +32,16 @@ import ( "k8s.io/apimachinery/pkg/util/sets" ) -// CustomDefaulter defines functions for setting defaults on resources. -type CustomDefaulter interface { - Default(ctx context.Context, obj runtime.Object) error +// Defaulter defines functions for setting defaults on resources. +type Defaulter[T runtime.Object] interface { + Default(ctx context.Context, obj T) error } +// CustomDefaulter defines functions for setting defaults on resources. +// +// Deprecated: CustomDefaulter is deprecated, use Defaulter instead +type CustomDefaulter = Defaulter[runtime.Object] + type defaulterOptions struct { removeUnknownOrOmitableFields bool } @@ -50,6 +56,29 @@ func DefaulterRemoveUnknownOrOmitableFields(o *defaulterOptions) { o.removeUnknownOrOmitableFields = true } +// WithDefaulter creates a new Webhook for a Defaulter interface. +func WithDefaulter[T runtime.Object](scheme *runtime.Scheme, defaulter Defaulter[T], opts ...DefaulterOption) *Webhook { + options := &defaulterOptions{} + for _, o := range opts { + o(options) + } + return &Webhook{ + Handler: &defaulterForType[T]{ + defaulter: defaulter, + decoder: NewDecoder(scheme), + removeUnknownOrOmitableFields: options.removeUnknownOrOmitableFields, + new: func() T { + var zero T + typ := reflect.TypeOf(zero) + if typ.Kind() == reflect.Ptr { + return reflect.New(typ.Elem()).Interface().(T) + } + return zero + }, + }, + } +} + // WithCustomDefaulter creates a new Webhook for a CustomDefaulter interface. func WithCustomDefaulter(scheme *runtime.Scheme, obj runtime.Object, defaulter CustomDefaulter, opts ...DefaulterOption) *Webhook { options := &defaulterOptions{} @@ -57,33 +86,30 @@ func WithCustomDefaulter(scheme *runtime.Scheme, obj runtime.Object, defaulter C o(options) } return &Webhook{ - Handler: &defaulterForType{ - object: obj, + Handler: &defaulterForType[runtime.Object]{ defaulter: defaulter, decoder: NewDecoder(scheme), removeUnknownOrOmitableFields: options.removeUnknownOrOmitableFields, + new: func() runtime.Object { return obj.DeepCopyObject() }, }, } } -type defaulterForType struct { - defaulter CustomDefaulter - object runtime.Object +type defaulterForType[T runtime.Object] struct { + defaulter Defaulter[T] decoder Decoder removeUnknownOrOmitableFields bool + new func() T } // Handle handles admission requests. -func (h *defaulterForType) Handle(ctx context.Context, req Request) Response { +func (h *defaulterForType[T]) Handle(ctx context.Context, req Request) Response { if h.decoder == nil { panic("decoder should never be nil") } if h.defaulter == nil { panic("defaulter should never be nil") } - if h.object == nil { - panic("object should never be nil") - } // Always skip when a DELETE operation received in custom mutation handler. if req.Operation == admissionv1.Delete { @@ -98,16 +124,12 @@ func (h *defaulterForType) Handle(ctx context.Context, req Request) Response { ctx = NewContextWithRequest(ctx, req) // Get the object in the request - obj := h.object.DeepCopyObject() + obj := h.new() if err := h.decoder.Decode(req, obj); err != nil { return Errored(http.StatusBadRequest, err) } - // Keep a copy of the object if needed - var originalObj runtime.Object - if !h.removeUnknownOrOmitableFields { - originalObj = obj.DeepCopyObject() - } + originalObj := obj.DeepCopyObject().(T) // Default the object if err := h.defaulter.Default(ctx, obj); err != nil { @@ -118,6 +140,21 @@ func (h *defaulterForType) Handle(ctx context.Context, req Request) Response { return Denied(err.Error()) } + // If the object is not changed, there's no reason to go through the expensive patch calculation below. + // Note: While jsonpatch.CreatePatch short-circuits if both byte arrays are equal this is likely never the case. + // * json.Marshal that we use below sorts fields alphabetically + // * for builtin types the apiserver also sorts alphabetically (but it seems like it adds an empty line at the end) + // * for CRDs the apiserver uses the field order in the OpenAPI schema which very likely is not alphabetically sorted + // Note: If removeUnknownOrOmitableFields is set we have to compute a patch to remove unknown or omitable fields even + // if the objects are equal + if !h.removeUnknownOrOmitableFields && reflect.DeepEqual(originalObj, obj) { + return Response{ + AdmissionResponse: admissionv1.AdmissionResponse{ + Allowed: true, + }, + } + } + // Create the patch marshalled, err := json.Marshal(obj) if err != nil { @@ -131,7 +168,7 @@ func (h *defaulterForType) Handle(ctx context.Context, req Request) Response { return handlerResponse } -func (h *defaulterForType) dropSchemeRemovals(r Response, original runtime.Object, raw []byte) Response { +func (h *defaulterForType[T]) dropSchemeRemovals(r Response, original T, raw []byte) Response { const opRemove = "remove" if !r.Allowed || r.PatchType == nil { return r diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go index ef1be52a8f..f8401571d0 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "net/http" + "reflect" v1 "k8s.io/api/admission/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -30,54 +31,79 @@ import ( // Warnings represents warning messages. type Warnings []string -// CustomValidator defines functions for validating an operation. +// Validator defines functions for validating an operation. // The object to be validated is passed into methods as a parameter. -type CustomValidator interface { +type Validator[T runtime.Object] interface { // ValidateCreate validates the object on creation. // The optional warnings will be added to the response as warning messages. // Return an error if the object is invalid. - ValidateCreate(ctx context.Context, obj runtime.Object) (warnings Warnings, err error) + ValidateCreate(ctx context.Context, obj T) (warnings Warnings, err error) // ValidateUpdate validates the object on update. // The optional warnings will be added to the response as warning messages. // Return an error if the object is invalid. - ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (warnings Warnings, err error) + ValidateUpdate(ctx context.Context, oldObj, newObj T) (warnings Warnings, err error) // ValidateDelete validates the object on deletion. // The optional warnings will be added to the response as warning messages. // Return an error if the object is invalid. - ValidateDelete(ctx context.Context, obj runtime.Object) (warnings Warnings, err error) + ValidateDelete(ctx context.Context, obj T) (warnings Warnings, err error) +} + +// CustomValidator defines functions for validating an operation. +// +// Deprecated: CustomValidator is deprecated, use Validator instead +type CustomValidator = Validator[runtime.Object] + +// WithValidator creates a new Webhook for validating the provided type. +func WithValidator[T runtime.Object](scheme *runtime.Scheme, validator Validator[T]) *Webhook { + return &Webhook{ + Handler: &validatorForType[T]{ + validator: validator, + decoder: NewDecoder(scheme), + new: func() T { + var zero T + typ := reflect.TypeOf(zero) + if typ.Kind() == reflect.Ptr { + return reflect.New(typ.Elem()).Interface().(T) + } + return zero + }, + }, + } } -// WithCustomValidator creates a new Webhook for validating the provided type. +// WithCustomValidator creates a new Webhook for a CustomValidator. +// +// Deprecated: WithCustomValidator is deprecated, use WithValidator instead func WithCustomValidator(scheme *runtime.Scheme, obj runtime.Object, validator CustomValidator) *Webhook { return &Webhook{ - Handler: &validatorForType{object: obj, validator: validator, decoder: NewDecoder(scheme)}, + Handler: &validatorForType[runtime.Object]{ + validator: validator, + decoder: NewDecoder(scheme), + new: func() runtime.Object { return obj.DeepCopyObject() }, + }, } } -type validatorForType struct { - validator CustomValidator - object runtime.Object +type validatorForType[T runtime.Object] struct { + validator Validator[T] decoder Decoder + new func() T } // Handle handles admission requests. -func (h *validatorForType) Handle(ctx context.Context, req Request) Response { +func (h *validatorForType[T]) Handle(ctx context.Context, req Request) Response { if h.decoder == nil { panic("decoder should never be nil") } if h.validator == nil { panic("validator should never be nil") } - if h.object == nil { - panic("object should never be nil") - } ctx = NewContextWithRequest(ctx, req) - // Get the object in the request - obj := h.object.DeepCopyObject() + obj := h.new() var err error var warnings []string @@ -93,7 +119,7 @@ func (h *validatorForType) Handle(ctx context.Context, req Request) Response { warnings, err = h.validator.ValidateCreate(ctx, obj) case v1.Update: - oldObj := obj.DeepCopyObject() + oldObj := h.new() if err := h.decoder.DecodeRaw(req.Object, obj); err != nil { return Errored(http.StatusBadRequest, err) } diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go index 2882e7bab3..518d52f364 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/alias.go @@ -24,10 +24,14 @@ import ( // define some aliases for common bits of the webhook functionality // CustomDefaulter defines functions for setting defaults on resources. -type CustomDefaulter = admission.CustomDefaulter +// +// Deprecated: Use admission.Defaulter instead. +type CustomDefaulter = admission.CustomDefaulter //nolint:staticcheck // CustomValidator defines functions for validating an operation. -type CustomValidator = admission.CustomValidator +// +// Deprecated: Use admission.Validator instead. +type CustomValidator = admission.CustomValidator //nolint:staticcheck // AdmissionRequest defines the input for an admission handler. // It contains information to identify the object in diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go index a26fa348bb..3f98fb7ba7 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion.go @@ -43,14 +43,15 @@ var ( log = logf.Log.WithName("conversion-webhook") ) -func NewWebhookHandler(scheme *runtime.Scheme) http.Handler { - return &webhook{scheme: scheme, decoder: NewDecoder(scheme)} +func NewWebhookHandler(scheme *runtime.Scheme, registry Registry) http.Handler { + return &webhook{scheme: scheme, decoder: NewDecoder(scheme), registry: registry} } // webhook implements a CRD conversion webhook HTTP handler. type webhook struct { - scheme *runtime.Scheme - decoder *Decoder + scheme *runtime.Scheme + decoder *Decoder + registry Registry } // ensure Webhook implements http.Handler @@ -119,7 +120,7 @@ func (wh *webhook) handleConvertRequest(ctx context.Context, req *apix.Conversio if err != nil { return nil, err } - err = wh.convertObject(src, dst) + err = wh.convertObject(ctx, src, dst) if err != nil { return nil, err } @@ -137,7 +138,7 @@ func (wh *webhook) handleConvertRequest(ctx context.Context, req *apix.Conversio // convertObject will convert given a src object to dst object. // Note(droot): couldn't find a way to reduce the cyclomatic complexity under 10 // without compromising readability, so disabling gocyclo linter -func (wh *webhook) convertObject(src, dst runtime.Object) error { +func (wh *webhook) convertObject(ctx context.Context, src, dst runtime.Object) error { srcGVK := src.GetObjectKind().GroupVersionKind() dstGVK := dst.GetObjectKind().GroupVersionKind() @@ -149,6 +150,10 @@ func (wh *webhook) convertObject(src, dst runtime.Object) error { return fmt.Errorf("conversion is not allowed between same type %T", src) } + if converter, ok := wh.registry.GetConverter(srcGVK.GroupKind()); ok { + return converter.ConvertObject(ctx, src, dst) + } + srcIsHub, dstIsHub := isHub(src), isHub(dst) srcIsConvertible, dstIsConvertible := isConvertible(src), isConvertible(dst) diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion_hubspoke.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion_hubspoke.go new file mode 100644 index 0000000000..b33af92ff4 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion_hubspoke.go @@ -0,0 +1,173 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package conversion + +import ( + "context" + "fmt" + "slices" + "strings" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/sets" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/apiutil" +) + +func NewHubSpokeConverter[hubObject runtime.Object](hub hubObject, spokeConverter ...SpokeConverter[hubObject]) func(scheme *runtime.Scheme) (Converter, error) { + return func(scheme *runtime.Scheme) (Converter, error) { + hubGVK, err := apiutil.GVKForObject(hub, scheme) + if err != nil { + return nil, fmt.Errorf("failed to create hub spoke converter: failed to get GroupVersionKind for hub: %w", err) + } + allGVKs, err := objectGVKs(scheme, hub) + if err != nil { + return nil, fmt.Errorf("failed to create hub spoke converter for %s: %w", hubGVK.Kind, err) + } + spokeVersions := sets.New[string]() + for _, gvk := range allGVKs { + if gvk != hubGVK { + spokeVersions.Insert(gvk.Version) + } + } + + c := &hubSpokeConverter[hubObject]{ + scheme: scheme, + hubGVK: hubGVK, + spokeConverterByGVK: map[schema.GroupVersionKind]SpokeConverter[hubObject]{}, + } + + spokeConverterVersions := sets.New[string]() + for _, sc := range spokeConverter { + spokeGVK, err := apiutil.GVKForObject(sc.GetSpoke(), scheme) + if err != nil { + return nil, fmt.Errorf("failed to create hub spoke converter for %s: "+ + "failed to get GroupVersionKind for spoke converter: %w", + hubGVK.Kind, err) + } + if hubGVK.GroupKind() != spokeGVK.GroupKind() { + return nil, fmt.Errorf("failed to create hub spoke converter for %s: "+ + "spoke converter GroupKind %s does not match hub GroupKind %s", + hubGVK.Kind, spokeGVK.GroupKind(), hubGVK.GroupKind()) + } + + if _, ok := c.spokeConverterByGVK[spokeGVK]; ok { + return nil, fmt.Errorf("failed to create hub spoke converter for %s: "+ + "duplicate spoke converter for version %s", + hubGVK.Kind, spokeGVK.Version) + } + c.spokeConverterByGVK[spokeGVK] = sc + spokeConverterVersions.Insert(spokeGVK.Version) + } + + if !spokeConverterVersions.Equal(spokeVersions) { + return nil, fmt.Errorf("failed to create hub spoke converter for %s: "+ + "expected spoke converter for %s got spoke converter for %s", + hubGVK.Kind, sortAndJoin(spokeVersions), sortAndJoin(spokeConverterVersions)) + } + + return c, nil + } +} + +func sortAndJoin(set sets.Set[string]) string { + list := set.UnsortedList() + slices.Sort(list) + return strings.Join(list, ",") +} + +type hubSpokeConverter[hubObject runtime.Object] struct { + scheme *runtime.Scheme + hubGVK schema.GroupVersionKind + spokeConverterByGVK map[schema.GroupVersionKind]SpokeConverter[hubObject] +} + +func (c hubSpokeConverter[hubObject]) ConvertObject(ctx context.Context, src, dst runtime.Object) error { + srcGVK := src.GetObjectKind().GroupVersionKind() + dstGVK := dst.GetObjectKind().GroupVersionKind() + + if srcGVK.GroupKind() != dstGVK.GroupKind() { + return fmt.Errorf("src %T and dst %T does not belong to same API Group", src, dst) + } + + if srcGVK == dstGVK { + return fmt.Errorf("conversion is not allowed between same type %T", src) + } + + srcIsHub := c.hubGVK == srcGVK + dstIsHub := c.hubGVK == dstGVK + _, srcIsConvertible := c.spokeConverterByGVK[srcGVK] + _, dstIsConvertible := c.spokeConverterByGVK[dstGVK] + + switch { + case srcIsHub && dstIsConvertible: + return c.spokeConverterByGVK[dstGVK].ConvertHubToSpoke(ctx, src.(hubObject), dst) + case dstIsHub && srcIsConvertible: + return c.spokeConverterByGVK[srcGVK].ConvertSpokeToHub(ctx, src, dst.(hubObject)) + case srcIsConvertible && dstIsConvertible: + hub, err := c.scheme.New(c.hubGVK) + if err != nil { + return fmt.Errorf("failed to allocate an instance for GroupVersionKind %s: %w", c.hubGVK, err) + } + if err := c.spokeConverterByGVK[srcGVK].ConvertSpokeToHub(ctx, src, hub.(hubObject)); err != nil { + return fmt.Errorf("failed to convert spoke %s to hub %s : %w", srcGVK, c.hubGVK, err) + } + if err := c.spokeConverterByGVK[dstGVK].ConvertHubToSpoke(ctx, hub.(hubObject), dst); err != nil { + return fmt.Errorf("failed to convert hub %s to spoke %s : %w", c.hubGVK, dstGVK, err) + } + return nil + default: + return fmt.Errorf("failed to convert %s to %s: not convertible", srcGVK, dstGVK) + } +} + +type SpokeConverter[hubObject runtime.Object] interface { + GetSpoke() runtime.Object + ConvertHubToSpoke(ctx context.Context, hub hubObject, spoke runtime.Object) error + ConvertSpokeToHub(ctx context.Context, spoke runtime.Object, hub hubObject) error +} + +func NewSpokeConverter[hubObject, spokeObject client.Object]( + spoke spokeObject, + convertHubToSpokeFunc func(ctx context.Context, src hubObject, dst spokeObject) error, + convertSpokeToHubFunc func(ctx context.Context, src spokeObject, dst hubObject) error, +) SpokeConverter[hubObject] { + return &spokeConverter[hubObject, spokeObject]{ + spoke: spoke, + convertSpokeToHubFunc: convertSpokeToHubFunc, + convertHubToSpokeFunc: convertHubToSpokeFunc, + } +} + +type spokeConverter[hubObject, spokeObject runtime.Object] struct { + spoke spokeObject + convertHubToSpokeFunc func(ctx context.Context, src hubObject, dst spokeObject) error + convertSpokeToHubFunc func(ctx context.Context, src spokeObject, dst hubObject) error +} + +func (c spokeConverter[hubObject, spokeObject]) GetSpoke() runtime.Object { + return c.spoke +} + +func (c spokeConverter[hubObject, spokeObject]) ConvertHubToSpoke(ctx context.Context, hub hubObject, spoke runtime.Object) error { + return c.convertHubToSpokeFunc(ctx, hub, spoke.(spokeObject)) +} + +func (c spokeConverter[hubObject, spokeObject]) ConvertSpokeToHub(ctx context.Context, spoke runtime.Object, hub hubObject) error { + return c.convertSpokeToHubFunc(ctx, spoke.(spokeObject), hub) +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion_registry.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion_registry.go new file mode 100644 index 0000000000..6e68b5ffa6 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/conversion/conversion_registry.go @@ -0,0 +1,57 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package conversion + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +type Converter interface { + ConvertObject(ctx context.Context, src, dst runtime.Object) error +} + +type Registry interface { + RegisterConverter(gk schema.GroupKind, converter Converter) error + GetConverter(gk schema.GroupKind) (Converter, bool) +} + +type registry struct { + converterByGK map[schema.GroupKind]Converter +} + +func NewRegistry() Registry { + return registry{ + converterByGK: map[schema.GroupKind]Converter{}, + } +} +func (r registry) RegisterConverter(gk schema.GroupKind, converter Converter) error { + if _, ok := r.converterByGK[gk]; ok { + return fmt.Errorf("failed to register Converter for GroupKind %s: converter already registered", gk) + } + + r.converterByGK[gk] = converter + return nil +} + +func (r registry) GetConverter(gk schema.GroupKind) (Converter, bool) { + c, ok := r.converterByGK[gk] + return c, ok +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics/metrics.go b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics/metrics.go index f1e6ce68f5..a8ff400954 100644 --- a/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics/metrics.go +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/webhook/internal/metrics/metrics.go @@ -33,6 +33,7 @@ var ( prometheus.HistogramOpts{ Name: "controller_runtime_webhook_latency_seconds", Help: "Histogram of the latency of processing admission requests", + Buckets: prometheus.ExponentialBuckets(10e-9, 10, 12), NativeHistogramBucketFactor: 1.1, NativeHistogramMaxBucketNumber: 100, NativeHistogramMinResetDuration: 1 * time.Hour,