Merge pull request #28552 from Luap99/registries-conf

vendor registries.conf rework
This commit is contained in:
Lokesh Mandvekar 2026-04-24 13:44:17 -04:00 • committed by GitHub
commit fd57a62ffe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 1418 additions and 927 deletions

View file

@ -20,7 +20,6 @@ import (
"go.podman.io/common/pkg/config"
"go.podman.io/common/pkg/ssh"
"go.podman.io/image/v5/pkg/sysregistriesv2"
imageTypes "go.podman.io/image/v5/types"
"go.podman.io/podman/v6/cmd/podman/registry"
"go.podman.io/podman/v6/libpod/define"
"go.podman.io/podman/v6/libpod/events"
@ -317,9 +316,7 @@ func getSecrets(cmd *cobra.Command, toComplete string, cType completeType) ([]st
}
func getRegistries() ([]string, cobra.ShellCompDirective) {
sysCtx := &imageTypes.SystemContext{}
SetRegistriesConfPath(sysCtx)
regs, err := sysregistriesv2.UnqualifiedSearchRegistries(sysCtx)
regs, err := sysregistriesv2.UnqualifiedSearchRegistries(nil)
if err != nil {
cobra.CompErrorln(err.Error())
return nil, cobra.ShellCompDirectiveNoFileComp

View file

@ -1,24 +0,0 @@
package common
import (
"os"
"go.podman.io/image/v5/types"
)
// SetRegistriesConfPath sets the registries.conf path for the specified context.
// NOTE: this is a verbatim copy from c/common/libimage which we're not using
// to prevent leaking c/storage into this file. Maybe this should go into c/image?
func SetRegistriesConfPath(systemContext *types.SystemContext) {
if systemContext.SystemRegistriesConfPath != "" {
return
}
if envOverride, ok := os.LookupEnv("CONTAINERS_REGISTRIES_CONF"); ok {
systemContext.SystemRegistriesConfPath = envOverride
return
}
if envOverride, ok := os.LookupEnv("REGISTRIES_CONFIG_PATH"); ok {
systemContext.SystemRegistriesConfPath = envOverride
return
}
}

View file

@ -104,7 +104,6 @@ func login(cmd *cobra.Command, args []string) error {
DockerInsecureSkipTLSVerify: skipTLS,
BaseTLSConfig: baseTLSConfig.TLSConfig(),
}
common.SetRegistriesConfPath(sysCtx)
loginOptions.GetLoginSet = cmd.Flag("get-login").Changed
return auth.Login(context.Background(), sysCtx, &loginOptions.LoginOptions, args)
}

View file

@ -57,6 +57,5 @@ func logout(_ *cobra.Command, args []string) error {
sysCtx := &types.SystemContext{
BaseTLSConfig: baseTLSConfig.TLSConfig(),
}
common.SetRegistriesConfPath(sysCtx)
return auth.Logout(sysCtx, &logoutOptions, args)
}

24
go.mod
View file

@ -64,9 +64,9 @@ require (
github.com/vbauerster/mpb/v8 v8.12.0
github.com/vishvananda/netlink v1.3.1
go.podman.io/buildah v1.42.1-0.20260421143840-0acb6b8cca85
go.podman.io/common v0.67.2-0.20260420103546-618304d6f83d
go.podman.io/image/v5 v5.39.3-0.20260420103546-618304d6f83d
go.podman.io/storage v1.62.1-0.20260420103546-618304d6f83d
go.podman.io/common v0.67.2-0.20260423135811-cbaa5f41e643
go.podman.io/image/v5 v5.39.3-0.20260423135811-cbaa5f41e643
go.podman.io/storage v1.62.1-0.20260423135811-cbaa5f41e643
golang.org/x/crypto v0.50.0
golang.org/x/net v0.53.0
golang.org/x/sync v0.20.0
@ -109,7 +109,7 @@ require (
github.com/digitalocean/go-libvirt v0.0.0-20220804181439-8648fbde413e // indirect
github.com/disiqueira/gotree/v3 v3.0.2 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker-credential-helpers v0.9.5 // indirect
github.com/docker/docker-credential-helpers v0.9.6 // indirect
github.com/ebitengine/purego v0.10.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
@ -133,7 +133,7 @@ require (
github.com/kr/fs v0.1.0 // indirect
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
github.com/manifoldco/promptui v0.9.0 // indirect
github.com/mattn/go-runewidth v0.0.22 // indirect
github.com/mattn/go-runewidth v0.0.23 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mistifyio/go-zfs/v4 v4.0.0 // indirect
@ -154,7 +154,7 @@ require (
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/proglottis/gpgme v0.1.6 // indirect
github.com/seccomp/libseccomp-golang v0.11.1 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.10.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.11.0 // indirect
github.com/sigstore/fulcio v1.8.5 // indirect
github.com/sigstore/protobuf-specs v0.5.0 // indirect
github.com/sigstore/sigstore v1.10.5 // indirect
@ -172,18 +172,18 @@ require (
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.etcd.io/bbolt v1.4.3 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
go.opentelemetry.io/otel v1.42.0 // indirect
go.opentelemetry.io/otel/metric v1.42.0 // indirect
go.opentelemetry.io/otel/trace v1.42.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
go.opentelemetry.io/otel v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/mod v0.34.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/tools v0.43.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
tags.cncf.io/container-device-interface/specs-go v1.1.0 // indirect
)

60
go.sum
View file

@ -98,12 +98,12 @@ github.com/disiqueira/gotree/v3 v3.0.2 h1:ik5iuLQQoufZBNPY518dXhiO5056hyNBIK9lWh
github.com/disiqueira/gotree/v3 v3.0.2/go.mod h1:ZuyjE4+mUQZlbpkI24AmruZKhg3VHEgPLDY8Qk+uUu8=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v29.4.0+incompatible h1:+IjXULMetlvWJiuSI0Nbor36lcJ5BTcVpUmB21KBoVM=
github.com/docker/cli v29.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v29.4.1+incompatible h1:02RT8QqqwtGRn+6SYypv8IUEbD/ltY6sfKCJIoUcGzk=
github.com/docker/cli v29.4.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/docker-credential-helpers v0.9.6 h1:cT2PbRPSlnMmNTfT2TDMXRyQ1KMWHG7xoTLBcn1ZNv0=
github.com/docker/docker-credential-helpers v0.9.6/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
github.com/docker/go-plugins-helpers v0.0.0-20240701071450-45e2431495c8 h1:IMfrF5LCzP2Vhw7j4IIH3HxPsCLuZYjDqFAM/C88ulg=
@ -229,8 +229,8 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.22 h1:76lXsPn6FyHtTY+jt2fTTvsMUCZq1k0qwRsAMuxzKAk=
github.com/mattn/go-runewidth v0.0.22/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-shellwords v1.0.13 h1:DC0OMEpGjm6LfNFU4ckYcvbQKyp2vE8atyFGXNtDcf4=
github.com/mattn/go-shellwords v1.0.13/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-sqlite3 v1.14.42 h1:MigqEP4ZmHw3aIdIT7T+9TLa90Z6smwcthx+Azv4Cgo=
@ -327,8 +327,8 @@ github.com/sebdah/goldie/v2 v2.8.0 h1:dZb9wR8q5++oplmEiJT+U/5KyotVD+HNGCAc5gNr8r
github.com/sebdah/goldie/v2 v2.8.0/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
github.com/seccomp/libseccomp-golang v0.11.1 h1:wuk4ZjSx6kyQII4rj6G6fvVzRHQaSiPvccJazDagu4g=
github.com/seccomp/libseccomp-golang v0.11.1/go.mod h1:5m1Lk8E9OwgZTTVz4bBOer7JuazaBa+xTkM895tDiWc=
github.com/secure-systems-lab/go-securesystemslib v0.10.0 h1:l+H5ErcW0PAehBNrBxoGv1jjNpGYdZ9RcheFkB2WI14=
github.com/secure-systems-lab/go-securesystemslib v0.10.0/go.mod h1:MRKONWmRoFzPNQ9USRF9i1mc7MvAVvF1LlW8X5VWDvk=
github.com/secure-systems-lab/go-securesystemslib v0.11.0 h1:iuCR9kcMFD4QurdKrGvPLoKZLv9YvwPYVr0473BdtFs=
github.com/secure-systems-lab/go-securesystemslib v0.11.0/go.mod h1:+PMOTjUGwHj2vcZ+TFKlb1tXRbrdWE1LYDT5i9JC80Q=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc=
@ -415,26 +415,26 @@ go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
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.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.podman.io/buildah v1.42.1-0.20260421143840-0acb6b8cca85 h1:rVKRagobPO6kGHOg2NhGNs6xWVgZctiOn4tmxa3cytA=
go.podman.io/buildah v1.42.1-0.20260421143840-0acb6b8cca85/go.mod h1:JjFirF1zlILz55ZkVYYhLRnted7mPlmoS2w2ihYw8iw=
go.podman.io/common v0.67.2-0.20260420103546-618304d6f83d h1:zryioAYWqUu1BUPraJJ6q5mLiclNZ3kTuQyyBO/5vYs=
go.podman.io/common v0.67.2-0.20260420103546-618304d6f83d/go.mod h1:Pom4Io+8C+CCZzr3CRUkr6A5inTMY8nVaFS3X1Y5NbI=
go.podman.io/image/v5 v5.39.3-0.20260420103546-618304d6f83d h1:NeMM89Fwaw19guPj/hXEjBtoCeH/mGgbAWoBnpnRiIo=
go.podman.io/image/v5 v5.39.3-0.20260420103546-618304d6f83d/go.mod h1:Yl9kZ8QslAOPc2rMkCrjPENCpSox6frxScCEzpmsQX4=
go.podman.io/storage v1.62.1-0.20260420103546-618304d6f83d h1:aMAdj9gLrVUz+Zb+nOxcoeT6+CC+8Rk2lHs1MC3kcQM=
go.podman.io/storage v1.62.1-0.20260420103546-618304d6f83d/go.mod h1:13aOBf6782/fbAzH7QNEqlVzFu+X4sS4MxDM/VdJGZU=
go.podman.io/common v0.67.2-0.20260423135811-cbaa5f41e643 h1:WwUcVKPEUoSDiQ22osdYGqM3q9IDB4viqSASPCdw1K0=
go.podman.io/common v0.67.2-0.20260423135811-cbaa5f41e643/go.mod h1:kBztCGxEhou0OO0O4yJXAKC3JEQTmOxCJPni90+sQKY=
go.podman.io/image/v5 v5.39.3-0.20260423135811-cbaa5f41e643 h1:1LuhuiiXHHteVXULDV/x9qxJtFozQPLEYKHCbYdOuP0=
go.podman.io/image/v5 v5.39.3-0.20260423135811-cbaa5f41e643/go.mod h1:sAszAH18v4KPpzPZdav9ovjEBcIDkmqdhbJV9SmnzfE=
go.podman.io/storage v1.62.1-0.20260423135811-cbaa5f41e643 h1:/zmZ36KkCZumHK0EDH5QpSjyM3fY09YuyIfqoXxEI48=
go.podman.io/storage v1.62.1-0.20260423135811-cbaa5f41e643/go.mod h1:13aOBf6782/fbAzH7QNEqlVzFu+X4sS4MxDM/VdJGZU=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
@ -554,10 +554,10 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=

View file

@ -24,10 +24,48 @@ var (
}
)
var (
zerowidth table // combining + nonprint merged for faster zero-width lookup
widewidth table // ambiguous + doublewidth merged for EA path
)
func init() {
zerowidth = mergeIntervals(combining, nonprint)
widewidth = mergeIntervals(ambiguous, doublewidth)
handleEnv()
}
func mergeIntervals(t1, t2 table) table {
merged := make(table, 0, len(t1)+len(t2))
i, j := 0, 0
for i < len(t1) && j < len(t2) {
if t1[i].first <= t2[j].first {
merged = append(merged, t1[i])
i++
} else {
merged = append(merged, t2[j])
j++
}
}
merged = append(merged, t1[i:]...)
merged = append(merged, t2[j:]...)
if len(merged) == 0 {
return merged
}
result := merged[:1]
for _, iv := range merged[1:] {
last := &result[len(result)-1]
if iv.first <= last.last+1 {
if iv.last > last.last {
last.last = iv.last
}
} else {
result = append(result, iv)
}
}
return result
}
func handleEnv() {
env := os.Getenv("RUNEWIDTH_EASTASIAN")
if env == "" {
@ -52,15 +90,6 @@ type interval struct {
type table []interval
func inTables(r rune, ts ...table) bool {
for _, t := range ts {
if inTable(r, t) {
return true
}
}
return false
}
func inTable(r rune, t table) bool {
if r < t[0].first {
return false
@ -131,9 +160,7 @@ func (c *Condition) RuneWidth(r rune) int {
return 0
case r < 0x300:
return 1
case inTable(r, narrow):
return 1
case inTables(r, nonprint, combining):
case inTable(r, zerowidth):
return 0
case inTable(r, doublewidth):
return 2
@ -142,13 +169,13 @@ func (c *Condition) RuneWidth(r rune) int {
}
} else {
switch {
case inTables(r, nonprint, combining):
case inTable(r, zerowidth):
return 0
case inTable(r, narrow):
return 1
case inTables(r, ambiguous, doublewidth):
case inTable(r, widewidth):
return 2
case !c.StrictEmojiNeutral && inTables(r, ambiguous, emoji, narrow):
case !c.StrictEmojiNeutral && inTable(r, emoji):
return 2
default:
return 1
@ -185,6 +212,16 @@ func (c *Condition) StringWidth(s string) (width int) {
return c.RuneWidth(r)
}
}
// ASCII fast path: no grapheme clustering needed for pure ASCII
if isAllASCII(s) {
for i := 0; i < len(s); i++ {
b := s[i]
if b >= 0x20 && b != 0x7F {
width++
}
}
return
}
g := graphemes.FromString(s)
for g.Next() {
var chWidth int
@ -199,6 +236,15 @@ func (c *Condition) StringWidth(s string) (width int) {
return
}
func isAllASCII(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] >= 0x80 {
return false
}
}
return true
}
// Truncate return string truncated with w cells
func (c *Condition) Truncate(s string, w int, tail string) string {
if c.StringWidth(s) <= w {
@ -264,24 +310,25 @@ func (c *Condition) TruncateLeft(s string, w int, prefix string) string {
// Wrap return string wrapped with w cells
func (c *Condition) Wrap(s string, w int) string {
width := 0
out := ""
var out strings.Builder
out.Grow(len(s) + len(s)/w + 1)
for _, r := range s {
cw := c.RuneWidth(r)
if r == '\n' {
out += string(r)
out.WriteRune(r)
width = 0
continue
} else if width+cw > w {
out += "\n"
out.WriteByte('\n')
width = 0
out += string(r)
out.WriteRune(r)
width += cw
continue
}
out += string(r)
out.WriteRune(r)
width += cw
}
return out
return out.String()
}
// FillLeft return string filled in left by spaces in w cells
@ -320,7 +367,7 @@ func RuneWidth(r rune) int {
// IsAmbiguousWidth returns whether is ambiguous width or not.
func IsAmbiguousWidth(r rune) bool {
return inTables(r, private, ambiguous)
return inTable(r, private) || inTable(r, ambiguous)
}
// IsCombiningWidth returns whether is combining width or not.

View file

@ -364,7 +364,9 @@ func (n HTTPServer) MetricAttributes(server string, req *http.Request, statusCod
if statusCode > 0 {
num++
}
if route == "" && req.Pattern != "" {
route = httpRoute(req.Pattern)
}
if route != "" {
num++
}

View file

@ -4,4 +4,4 @@
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
// Version is the current release version of the otelhttp instrumentation.
const Version = "0.67.0"
const Version = "0.68.0"

View file

@ -17,6 +17,7 @@ linters:
- ineffassign
- misspell
- modernize
- noctx
- perfsprint
- revive
- staticcheck
@ -88,6 +89,16 @@ linters:
deny:
- pkg: go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal
desc: Do not use cross-module internal packages.
semconv:
list-mode: lax
files:
- "!**/semconv/**"
- "!**/exporters/zipkin/**"
deny:
- pkg: go.opentelemetry.io/otel/semconv
desc: "Use go.opentelemetry.io/otel/semconv/v1.40.0 instead. If a newer semconv version has been released, update the depguard rule."
allow:
- go.opentelemetry.io/otel/semconv/v1.40.0
gocritic:
disabled-checks:
- appendAssign

View file

@ -11,6 +11,49 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
<!-- Released section -->
<!-- Don't change this section unless doing release -->
## [1.43.0/0.65.0/0.19.0] 2026-04-02
### Added
- Add `IsRandom` and `WithRandom` on `TraceFlags`, and `IsRandom` on `SpanContext` in `go.opentelemetry.io/otel/trace` for [W3C Trace Context Level 2 Random Trace ID Flag](https://www.w3.org/TR/trace-context-2/#random-trace-id-flag) support. (#8012)
- Add service detection with `WithService` in `go.opentelemetry.io/otel/sdk/resource`. (#7642)
- Add `DefaultWithContext` and `EnvironmentWithContext` in `go.opentelemetry.io/otel/sdk/resource` to support plumbing `context.Context` through default and environment detectors. (#8051)
- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#8038)
- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. (#8038)
- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#8038)
- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#8038)
- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#8038)
- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8038)
- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest`. (#8038)
- Add support for per-series start time tracking for cumulative metrics in `go.opentelemetry.io/otel/sdk/metric`.
Set `OTEL_GO_X_PER_SERIES_START_TIMESTAMPS=true` to enable. (#8060)
- Add `WithCardinalityLimitSelector` for metric reader for configuring cardinality limits specific to the instrument kind. (#7855)
### Changed
- Introduce the `EMPTY` Type in `go.opentelemetry.io/otel/attribute` to reflect that an empty value is now a valid value, with `INVALID` remaining as a deprecated alias of `EMPTY`. (#8038)
- Improve slice handling in `go.opentelemetry.io/otel/attribute` to optimize short slice values with fixed-size fast paths. (#8039)
- Improve performance of span metric recording in `go.opentelemetry.io/otel/sdk/trace` by returning early if self-observability is not enabled. (#8067)
- Improve formatting of metric data diffs in `go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest`. (#8073)
### Deprecated
- Deprecate `INVALID` in `go.opentelemetry.io/otel/attribute`. Use `EMPTY` instead. (#8038)
### Fixed
- Return spec-compliant `TraceIdRatioBased` description. This is a breaking behavioral change, but it is necessary to
make the implementation [spec-compliant](https://opentelemetry.io/docs/specs/otel/trace/sdk/#traceidratiobased). (#8027)
- Fix a race condition in `go.opentelemetry.io/otel/sdk/metric` where the lastvalue aggregation could collect the value 0 even when no zero-value measurements were recorded. (#8056)
- Limit HTTP response body to 4 MiB in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` to mitigate excessive memory usage caused by a misconfigured or malicious server.
Responses exceeding the limit are treated as non-retryable errors. (#8108)
- Limit HTTP response body to 4 MiB in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` to mitigate excessive memory usage caused by a misconfigured or malicious server.
Responses exceeding the limit are treated as non-retryable errors. (#8108)
- Limit HTTP response body to 4 MiB in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp` to mitigate excessive memory usage caused by a misconfigured or malicious server.
Responses exceeding the limit are treated as non-retryable errors. (#8108)
- `WithHostID` detector in `go.opentelemetry.io/otel/sdk/resource` to use full path for `kenv` command on BSD. (#8113)
- Fix missing `request.GetBody` in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp` to correctly handle HTTP2 GOAWAY frame. (#8096)
## [1.42.0/0.64.0/0.18.0/0.0.16] 2026-03-06
### Added
@ -3576,7 +3619,8 @@ It contains api and sdk for trace and meter.
- CircleCI build CI manifest files.
- CODEOWNERS file to track owners of this project.
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.42.0...HEAD
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...HEAD
[1.43.0/0.65.0/0.19.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.43.0
[1.42.0/0.64.0/0.18.0/0.0.16]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.42.0
[1.41.0/0.63.0/0.17.0/0.0.15]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.41.0
[1.40.0/0.62.0/0.16.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.40.0

View file

@ -38,10 +38,14 @@ CROSSLINK = $(TOOLS)/crosslink
$(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/build-tools/crosslink
SEMCONVKIT = $(TOOLS)/semconvkit
SEMCONVKIT_FILES := $(sort $(shell find $(TOOLS_MOD_DIR)/semconvkit -type f))
$(TOOLS)/semconvkit: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/semconvkit
$(TOOLS)/semconvkit: $(SEMCONVKIT_FILES)
VERIFYREADMES = $(TOOLS)/verifyreadmes
VERIFYREADMES_FILES := $(sort $(shell find $(TOOLS_MOD_DIR)/verifyreadmes -type f))
$(TOOLS)/verifyreadmes: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/verifyreadmes
$(TOOLS)/verifyreadmes: $(VERIFYREADMES_FILES)
GOLANGCI_LINT = $(TOOLS)/golangci-lint
$(TOOLS)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/v2/cmd/golangci-lint

View file

@ -4,7 +4,9 @@
Create a `Version Release` issue to track the release process.
## Semantic Convention Generation
## Semantic Convention Upgrade
### Semantic Convention Generation
New versions of the [OpenTelemetry Semantic Conventions] mean new versions of the `semconv` package need to be generated.
The `semconv-generate` make target is used for this.
@ -22,6 +24,43 @@ make semconv-generate # Uses the exported TAG.
This should create a new sub-package of [`semconv`](./semconv).
Ensure things look correct before submitting a pull request to include the addition.
The `CHANGELOG.md` should also be updated to reflect the new changes:
```md
- The `go.opentelemetry.io/otel/semconv/<NEW VERSION>` package. The package contains semantic conventions from the `<NEW VERSION>` version of the OpenTelemetry Semantic Conventions. See the [migration documentation](./semconv/<NEW VERSION>/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/<PREVIOUS VERSION>`. (#PR_NUMBER)
```
> **Tip:** Change to the release and prior version to match the changes
### Update semconv imports
Once the new semconv module has been generated, update all semconv imports throughout the codebase to reference the new version:
```go
// Before
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
// After
semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
"go.opentelemetry.io/otel/semconv/v1.39.0/otelconv"
```
Once complete, run `make` to check for any compilation or test failures.
#### Handling attribute changes
Some semconv releases might add new attributes or impact attributes that are currently being used. Changes could stem from a simple renaming, to more complex changes like merging attributes and property values being changed.
One should update the code to the new attributes that supersede the impacted ones, hence sticking to the semantic conventions. However, legacy attributes might still be emitted in accordance to the `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable.
For an example on how such migration might have to be tracked and performed, see issue [#7806](https://github.com/open-telemetry/opentelemetry-go/issues/7806).
### Go contrib linter update
Update [.golangci.yml](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/.golangci.yml) in [opentelemetry-go-contrib](https://github.com/open-telemetry/opentelemetry-go-contrib/) to mandate the new semconv version.
## Breaking changes validation
You can run `make gorelease` which runs [gorelease](https://pkg.go.dev/golang.org/x/exp/cmd/gorelease) to ensure that there are no unwanted changes made in the public API.

View file

@ -53,7 +53,7 @@ var (
_ Encoder = &defaultAttrEncoder{}
// encoderIDCounter is for generating IDs for other attribute encoders.
encoderIDCounter uint64
encoderIDCounter atomic.Uint64
defaultEncoderOnce sync.Once
defaultEncoderID = NewEncoderID()
@ -64,7 +64,7 @@ var (
// once per each type of attribute encoder. Preferably in init() or in var
// definition.
func NewEncoderID() EncoderID {
return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)}
return EncoderID{value: encoderIDCounter.Add(1)}
}
// DefaultEncoder returns an attribute encoder that encodes attributes in such

View file

@ -27,6 +27,7 @@ const (
int64SliceID uint64 = 3762322556277578591 // "_[]int64" (little endian)
float64SliceID uint64 = 7308324551835016539 // "[]double" (little endian)
stringSliceID uint64 = 7453010373645655387 // "[]string" (little endian)
emptyID uint64 = 7305809155345288421 // "__empty_" (little endian)
)
// hashKVs returns a new xxHash64 hash of kvs.
@ -80,7 +81,8 @@ func hashKV(h xxhash.Hash, kv KeyValue) xxhash.Hash {
for i := 0; i < rv.Len(); i++ {
h = h.String(rv.Index(i).String())
}
case INVALID:
case EMPTY:
h = h.Uint64(emptyID)
default:
// Logging is an alternative, but using the internal logger here
// causes an import cycle so it is not done.

View file

@ -11,80 +11,63 @@ import (
"reflect"
)
// BoolSliceValue converts a bool slice into an array with same elements as slice.
func BoolSliceValue(v []bool) any {
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[bool]())).Elem()
// sliceElem is the exact set of element types stored in attribute slice values.
// Using a closed set prevents accidental instantiations for unsupported types.
type sliceElem interface {
bool | int64 | float64 | string
}
// SliceValue converts a slice into an array with the same elements.
func SliceValue[T sliceElem](v []T) any {
// Keep only the common tiny-slice cases out of reflection. Extending this
// much further increases code size for diminishing benefit while larger
// slices still need the generic reflective path to preserve comparability.
// This matches the short lengths that show up most often in local
// benchmarks and semantic convention examples while leaving larger, less
// predictable slices on the generic reflective path.
switch len(v) {
case 0:
return [0]T{}
case 1:
return [1]T{v[0]}
case 2:
return [2]T{v[0], v[1]}
case 3:
return [3]T{v[0], v[1], v[2]}
}
return sliceValueReflect(v)
}
// AsSlice converts an array into a slice with the same elements.
func AsSlice[T sliceElem](v any) []T {
// Mirror the small fixed-array fast path used by SliceValue.
switch a := v.(type) {
case [0]T:
return []T{}
case [1]T:
return []T{a[0]}
case [2]T:
return []T{a[0], a[1]}
case [3]T:
return []T{a[0], a[1], a[2]}
}
return asSliceReflect[T](v)
}
func sliceValueReflect[T sliceElem](v []T) any {
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[T]())).Elem()
reflect.Copy(cp, reflect.ValueOf(v))
return cp.Interface()
}
// Int64SliceValue converts an int64 slice into an array with same elements as slice.
func Int64SliceValue(v []int64) any {
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[int64]())).Elem()
reflect.Copy(cp, reflect.ValueOf(v))
return cp.Interface()
}
// Float64SliceValue converts a float64 slice into an array with same elements as slice.
func Float64SliceValue(v []float64) any {
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[float64]())).Elem()
reflect.Copy(cp, reflect.ValueOf(v))
return cp.Interface()
}
// StringSliceValue converts a string slice into an array with same elements as slice.
func StringSliceValue(v []string) any {
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[string]())).Elem()
reflect.Copy(cp, reflect.ValueOf(v))
return cp.Interface()
}
// AsBoolSlice converts a bool array into a slice into with same elements as array.
func AsBoolSlice(v any) []bool {
func asSliceReflect[T sliceElem](v any) []T {
rv := reflect.ValueOf(v)
if rv.Type().Kind() != reflect.Array {
if !rv.IsValid() || rv.Kind() != reflect.Array || rv.Type().Elem() != reflect.TypeFor[T]() {
return nil
}
cpy := make([]bool, rv.Len())
if len(cpy) > 0 {
_ = reflect.Copy(reflect.ValueOf(cpy), rv)
}
return cpy
}
// AsInt64Slice converts an int64 array into a slice into with same elements as array.
func AsInt64Slice(v any) []int64 {
rv := reflect.ValueOf(v)
if rv.Type().Kind() != reflect.Array {
return nil
}
cpy := make([]int64, rv.Len())
if len(cpy) > 0 {
_ = reflect.Copy(reflect.ValueOf(cpy), rv)
}
return cpy
}
// AsFloat64Slice converts a float64 array into a slice into with same elements as array.
func AsFloat64Slice(v any) []float64 {
rv := reflect.ValueOf(v)
if rv.Type().Kind() != reflect.Array {
return nil
}
cpy := make([]float64, rv.Len())
if len(cpy) > 0 {
_ = reflect.Copy(reflect.ValueOf(cpy), rv)
}
return cpy
}
// AsStringSlice converts a string array into a slice into with same elements as array.
func AsStringSlice(v any) []string {
rv := reflect.ValueOf(v)
if rv.Type().Kind() != reflect.Array {
return nil
}
cpy := make([]string, rv.Len())
cpy := make([]T, rv.Len())
if len(cpy) > 0 {
_ = reflect.Copy(reflect.ValueOf(cpy), rv)
}

View file

@ -15,7 +15,7 @@ type KeyValue struct {
// Valid reports whether kv is a valid OpenTelemetry attribute.
func (kv KeyValue) Valid() bool {
return kv.Key.Defined() && kv.Value.Type() != INVALID
return kv.Key.Defined()
}
// Bool creates a KeyValue with a BOOL Value type.

View file

@ -8,7 +8,7 @@ func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[INVALID-0]
_ = x[EMPTY-0]
_ = x[BOOL-1]
_ = x[INT64-2]
_ = x[FLOAT64-3]
@ -19,9 +19,9 @@ func _() {
_ = x[STRINGSLICE-8]
}
const _Type_name = "INVALIDBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICE"
const _Type_name = "EMPTYBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICE"
var _Type_index = [...]uint8{0, 7, 11, 16, 23, 29, 38, 48, 60, 71}
var _Type_index = [...]uint8{0, 5, 9, 14, 21, 27, 36, 46, 58, 69}
func (i Type) String() string {
idx := int(i) - 0

View file

@ -6,7 +6,6 @@ package attribute // import "go.opentelemetry.io/otel/attribute"
import (
"encoding/json"
"fmt"
"reflect"
"strconv"
attribute "go.opentelemetry.io/otel/attribute/internal"
@ -18,6 +17,8 @@ import (
type Type int // nolint: revive // redefines builtin Type.
// Value represents the value part in key-value pairs.
//
// Note that the zero value is a valid empty value.
type Value struct {
vtype Type
numeric uint64
@ -26,8 +27,8 @@ type Value struct {
}
const (
// INVALID is used for a Value with no value set.
INVALID Type = iota
// EMPTY is used for a Value with no value set.
EMPTY Type = iota
// BOOL is a boolean Type Value.
BOOL
// INT64 is a 64-bit signed integral Type Value.
@ -44,6 +45,10 @@ const (
FLOAT64SLICE
// STRINGSLICE is a slice of strings Type Value.
STRINGSLICE
// INVALID is used for a Value with no value set.
//
// Deprecated: Use EMPTY instead as an empty value is a valid value.
INVALID = EMPTY
)
// BoolValue creates a BOOL Value.
@ -56,7 +61,7 @@ func BoolValue(v bool) Value {
// BoolSliceValue creates a BOOLSLICE Value.
func BoolSliceValue(v []bool) Value {
return Value{vtype: BOOLSLICE, slice: attribute.BoolSliceValue(v)}
return Value{vtype: BOOLSLICE, slice: attribute.SliceValue(v)}
}
// IntValue creates an INT64 Value.
@ -64,16 +69,30 @@ func IntValue(v int) Value {
return Int64Value(int64(v))
}
// IntSliceValue creates an INTSLICE Value.
// IntSliceValue creates an INT64SLICE Value.
func IntSliceValue(v []int) Value {
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[int64]()))
for i, val := range v {
cp.Elem().Index(i).SetInt(int64(val))
}
return Value{
vtype: INT64SLICE,
slice: cp.Elem().Interface(),
val := Value{vtype: INT64SLICE}
// Avoid the common tiny-slice cases from allocating a new slice.
switch len(v) {
case 0:
val.slice = [0]int64{}
case 1:
val.slice = [1]int64{int64(v[0])}
case 2:
val.slice = [2]int64{int64(v[0]), int64(v[1])}
case 3:
val.slice = [3]int64{int64(v[0]), int64(v[1]), int64(v[2])}
default:
// Fallback to a new slice for larger slices.
cp := make([]int64, len(v))
for i, val := range v {
cp[i] = int64(val)
}
val.slice = attribute.SliceValue(cp)
}
return val
}
// Int64Value creates an INT64 Value.
@ -86,7 +105,7 @@ func Int64Value(v int64) Value {
// Int64SliceValue creates an INT64SLICE Value.
func Int64SliceValue(v []int64) Value {
return Value{vtype: INT64SLICE, slice: attribute.Int64SliceValue(v)}
return Value{vtype: INT64SLICE, slice: attribute.SliceValue(v)}
}
// Float64Value creates a FLOAT64 Value.
@ -99,7 +118,7 @@ func Float64Value(v float64) Value {
// Float64SliceValue creates a FLOAT64SLICE Value.
func Float64SliceValue(v []float64) Value {
return Value{vtype: FLOAT64SLICE, slice: attribute.Float64SliceValue(v)}
return Value{vtype: FLOAT64SLICE, slice: attribute.SliceValue(v)}
}
// StringValue creates a STRING Value.
@ -112,7 +131,7 @@ func StringValue(v string) Value {
// StringSliceValue creates a STRINGSLICE Value.
func StringSliceValue(v []string) Value {
return Value{vtype: STRINGSLICE, slice: attribute.StringSliceValue(v)}
return Value{vtype: STRINGSLICE, slice: attribute.SliceValue(v)}
}
// Type returns a type of the Value.
@ -136,7 +155,7 @@ func (v Value) AsBoolSlice() []bool {
}
func (v Value) asBoolSlice() []bool {
return attribute.AsBoolSlice(v.slice)
return attribute.AsSlice[bool](v.slice)
}
// AsInt64 returns the int64 value. Make sure that the Value's type is
@ -155,7 +174,7 @@ func (v Value) AsInt64Slice() []int64 {
}
func (v Value) asInt64Slice() []int64 {
return attribute.AsInt64Slice(v.slice)
return attribute.AsSlice[int64](v.slice)
}
// AsFloat64 returns the float64 value. Make sure that the Value's
@ -174,7 +193,7 @@ func (v Value) AsFloat64Slice() []float64 {
}
func (v Value) asFloat64Slice() []float64 {
return attribute.AsFloat64Slice(v.slice)
return attribute.AsSlice[float64](v.slice)
}
// AsString returns the string value. Make sure that the Value's type
@ -193,7 +212,7 @@ func (v Value) AsStringSlice() []string {
}
func (v Value) asStringSlice() []string {
return attribute.AsStringSlice(v.slice)
return attribute.AsSlice[string](v.slice)
}
type unknownValueType struct{}
@ -217,6 +236,8 @@ func (v Value) AsInterface() any {
return v.stringly
case STRINGSLICE:
return v.asStringSlice()
case EMPTY:
return nil
}
return unknownValueType{}
}
@ -252,6 +273,8 @@ func (v Value) Emit() string {
return string(j)
case STRING:
return v.stringly
case EMPTY:
return ""
default:
return "unknown"
}

View file

@ -1,4 +1,4 @@
# This is a renovate-friendly source of Docker images.
FROM python:3.13.6-slim-bullseye@sha256:e98b521460ee75bca92175c16247bdf7275637a8faaeb2bcfa19d879ae5c4b9a AS python
FROM otel/weaver:v0.21.2@sha256:2401de985c38bdb98b43918e2f43aa36b2afed4aa5669ac1c1de0a17301cd36d AS weaver
FROM otel/weaver:v0.22.1@sha256:33ae522ae4b71c1c562563c1d81f46aa0f79f088a0873199143a1f11ac30e5c9 AS weaver
FROM avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee AS markdown

View file

@ -196,6 +196,20 @@ func (tf TraceFlags) WithSampled(sampled bool) TraceFlags { // nolint:revive //
return tf &^ FlagsSampled
}
// IsRandom reports whether the random bit is set in the TraceFlags.
func (tf TraceFlags) IsRandom() bool {
return tf&FlagsRandom == FlagsRandom
}
// WithRandom sets the random bit in a new copy of the TraceFlags.
func (tf TraceFlags) WithRandom(random bool) TraceFlags { // nolint:revive // random is not a control flag.
if random {
return tf | FlagsRandom
}
return tf &^ FlagsRandom
}
// MarshalJSON implements a custom marshal function to encode TraceFlags
// as a hex string.
func (tf TraceFlags) MarshalJSON() ([]byte, error) {
@ -322,6 +336,11 @@ func (sc SpanContext) IsSampled() bool {
return sc.traceFlags.IsSampled()
}
// IsRandom reports whether the random bit is set in the SpanContext's TraceFlags.
func (sc SpanContext) IsRandom() bool {
return sc.traceFlags.IsRandom()
}
// WithTraceFlags returns a new SpanContext with the TraceFlags replaced.
func (sc SpanContext) WithTraceFlags(flags TraceFlags) SpanContext {
return SpanContext{

View file

@ -5,5 +5,5 @@ package otel // import "go.opentelemetry.io/otel"
// Version is the current release version of OpenTelemetry in use.
func Version() string {
return "1.42.0"
return "1.43.0"
}

View file

@ -3,7 +3,7 @@
module-sets:
stable-v1:
version: v1.42.0
version: v1.43.0
modules:
- go.opentelemetry.io/otel
- go.opentelemetry.io/otel/bridge/opencensus
@ -22,11 +22,11 @@ module-sets:
- go.opentelemetry.io/otel/sdk/metric
- go.opentelemetry.io/otel/trace
experimental-metrics:
version: v0.64.0
version: v0.65.0
modules:
- go.opentelemetry.io/otel/exporters/prometheus
experimental-logs:
version: v0.18.0
version: v0.19.0
modules:
- go.opentelemetry.io/otel/log
- go.opentelemetry.io/otel/log/logtest
@ -64,3 +64,6 @@ modules:
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp:
version-refs:
- ./internal/version.go
go.opentelemetry.io/otel/exporters/stdout/stdoutlog:
version-refs:
- ./internal/version.go

View file

@ -48,21 +48,6 @@ type RuntimeOptions struct {
SystemContext *types.SystemContext
}
// setRegistriesConfPath sets the registries.conf path for the specified context.
func setRegistriesConfPath(systemContext *types.SystemContext) {
if systemContext.SystemRegistriesConfPath != "" {
return
}
if envOverride, ok := os.LookupEnv("CONTAINERS_REGISTRIES_CONF"); ok {
systemContext.SystemRegistriesConfPath = envOverride
return
}
if envOverride, ok := os.LookupEnv("REGISTRIES_CONFIG_PATH"); ok {
systemContext.SystemRegistriesConfPath = envOverride
return
}
}
// Runtime is responsible for image management and storing them in a containers
// storage.
type Runtime struct {
@ -119,8 +104,6 @@ func RuntimeFromStore(store storage.Store, options *RuntimeOptions) (*Runtime, e
systemContext.BigFilesTemporaryDir = tmpdir
}
setRegistriesConfPath(&systemContext)
return &Runtime{
store: store,
systemContext: systemContext,

View file

@ -393,7 +393,7 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri
func defaultRegistryWhenUnspecified(systemContext *types.SystemContext) (string, error) {
registriesFromFile, err := sysregistriesv2.UnqualifiedSearchRegistries(systemContext)
if err != nil {
return "", fmt.Errorf("getting registry from registry.conf, please specify a registry: %w", err)
return "", fmt.Errorf("getting registry from registries.conf, please specify a registry: %w", err)
}
if len(registriesFromFile) == 0 {
return "", errors.New("no registries found in registries.conf, a registry must be provided")

View file

@ -1,11 +0,0 @@
//go:build !freebsd
package sysregistriesv2
// builtinRegistriesConfPath is the path to the registry configuration file.
// DO NOT change this, instead see systemRegistriesConfPath above.
const builtinRegistriesConfPath = "/etc/containers/registries.conf"
// builtinRegistriesConfDirPath is the path to the registry configuration directory.
// DO NOT change this, instead see systemRegistriesConfDirectoryPath above.
const builtinRegistriesConfDirPath = "/etc/containers/registries.conf.d"

View file

@ -1,11 +0,0 @@
//go:build freebsd
package sysregistriesv2
// builtinRegistriesConfPath is the path to the registry configuration file.
// DO NOT change this, instead see systemRegistriesConfPath above.
const builtinRegistriesConfPath = "/usr/local/etc/containers/registries.conf"
// builtinRegistriesConfDirPath is the path to the registry configuration directory.
// DO NOT change this, instead see systemRegistriesConfDirectoryPath above.
const builtinRegistriesConfDirPath = "/usr/local/etc/containers/registries.conf.d"

View file

@ -1,12 +1,8 @@
package sysregistriesv2
import (
"errors"
"fmt"
"io/fs"
"maps"
"os"
"path/filepath"
"reflect"
"slices"
"sort"
@ -17,23 +13,12 @@ import (
"github.com/sirupsen/logrus"
"go.podman.io/image/v5/docker/reference"
"go.podman.io/image/v5/types"
"go.podman.io/storage/pkg/configfile"
"go.podman.io/storage/pkg/fileutils"
"go.podman.io/storage/pkg/homedir"
"go.podman.io/storage/pkg/regexp"
"go.podman.io/storage/pkg/unshare"
)
// systemRegistriesConfPath is the path to the system-wide registry
// configuration file and is used to add/subtract potential registries for
// obtaining images. You can override this at build time with
// -ldflags '-X go.podman.io/image/v5/sysregistries.systemRegistriesConfPath=$your_path'
var systemRegistriesConfPath = builtinRegistriesConfPath
// systemRegistriesConfDirPath is the path to the system-wide registry
// configuration directory and is used to add/subtract potential registries for
// obtaining images. You can override this at build time with
// -ldflags '-X go.podman.io/image/v5/sysregistries.systemRegistriesConfDirectoryPath=$your_path'
var systemRegistriesConfDirPath = builtinRegistriesConfDirPath
// AuthenticationFileHelper is a special key for credential helpers indicating
// the usage of consulting containers-auth.json files instead of a credential
// helper.
@ -73,12 +58,6 @@ type Endpoint struct {
PullFromMirror string `toml:"pull-from-mirror,omitempty"`
}
// userRegistriesFile is the path to the per user registry configuration file.
var userRegistriesFile = filepath.FromSlash(".config/containers/registries.conf")
// userRegistriesDir is the path to the per user registry configuration file.
var userRegistriesDir = filepath.FromSlash(".config/containers/registries.conf.d")
// rewriteReference will substitute the provided reference `prefix` to the
// endpoints `location` from the `ref` and creates a new named reference from it.
// The function errors if the newly created reference is not parsable.
@ -185,11 +164,15 @@ func (r *Registry) PullSourcesFromReference(ref reference.Named) ([]PullSource,
}
// V1TOMLregistries is for backwards compatibility to sysregistries v1
//
// Deprecated: This format is no longer accepted, use [V2RegistriesConf] instead.
type V1TOMLregistries struct {
Registries []string `toml:"registries"`
}
// V1TOMLConfig is for backwards compatibility to sysregistries v1
//
// Deprecated: This format is no longer accepted, use [V2RegistriesConf] instead.
type V1TOMLConfig struct {
Search V1TOMLregistries `toml:"search"`
Insecure V1TOMLregistries `toml:"insecure"`
@ -197,6 +180,9 @@ type V1TOMLConfig struct {
}
// V1RegistriesConf is the sysregistries v1 configuration format.
//
// Deprecated: This format is no longer accepted, use [V2RegistriesConf] instead.
// You can use [V1RegistriesConf.ConvertToV2] to convert the type.
type V1RegistriesConf struct {
V1TOMLConfig `toml:"registries"`
}
@ -529,95 +515,90 @@ func (config *V2RegistriesConf) postProcessRegistries() error {
}
// ConfigPath returns the path to the system-wide registry configuration file.
// This may return an empty string if it fails to resolve a home directory.
//
// Deprecated: This API implies configuration is read from files, and that there is only one.
// Please use ConfigurationSourceDescription to obtain a string usable for error messages.
func ConfigPath(ctx *types.SystemContext) string {
return newConfigWrapper(ctx).configPath
configWrapper := newConfigWrapper(ctx)
paths, err := configfile.GetSearchPaths(configWrapper.toConfigFileOptions())
if err == nil && len(paths.MainFiles) > 0 {
for _, file := range paths.MainFiles {
if fileutils.Exists(file) == nil {
return file
}
}
// nothing exists return first file
return paths.MainFiles[0]
}
return ""
}
// ConfigDirPath returns the path to the directory for drop-in
// registry configuration files.
// registry configuration files. This may return an empty string if it fails to resolve a home directory.
//
// Deprecated: This API implies configuration is read from directories, and that there is only one.
// Please use ConfigurationSourceDescription to obtain a string usable for error messages.
func ConfigDirPath(ctx *types.SystemContext) string {
configWrapper := newConfigWrapper(ctx)
if configWrapper.userConfigDirPath != "" {
return configWrapper.userConfigDirPath
paths, err := configfile.GetSearchPaths(configWrapper.toConfigFileOptions())
if err == nil && len(paths.DropInDirectories) > 0 {
return paths.DropInDirectories[0]
}
return configWrapper.configDirPath
return ""
}
// configWrapper is used to store the paths from ConfigPath and ConfigDirPath
// and acts as a key to the internal cache.
//
// Note some env vars like CONTAINERS_REGISTRIES_CONF or $HOME will effect the
// parsing behavior but are not used for the cache key here, if these env change
// at runtime then the cache will result in correct results. This is a known trade
// off as regular callers are not expected to modify the envs at runtime.
// If they do so they should call [InvalidateCache].
type configWrapper struct {
// path to the registries.conf file
configPath string
// path to system-wide registries.conf.d directory, or "" if not used
configDirPath string
// path to user specified registries.conf.d directory, or "" if not used
userConfigDirPath string
// system context override to specific path
systemRegistriesConfPath string
// system context override to specific drop in directory
systemRegistriesConfDirPath string
// system context override to root directory
rootForImplicitAbsolutePaths string
}
func (c *configWrapper) toConfigFileOptions() *configfile.File {
return &configfile.File{
Name: "registries",
Extension: "conf",
EnvironmentName: "CONTAINERS_REGISTRIES_CONF",
RootForImplicitAbsolutePaths: c.rootForImplicitAbsolutePaths,
CustomConfigFilePath: c.systemRegistriesConfPath,
CustomConfigFileDropInDirectory: c.systemRegistriesConfDirPath,
UserId: unshare.GetRootlessUID(),
}
}
// newConfigWrapper returns a configWrapper for the specified SystemContext.
func newConfigWrapper(ctx *types.SystemContext) configWrapper {
return newConfigWrapperWithHomeDir(ctx, homedir.Get())
}
// newConfigWrapperWithHomeDir is an internal implementation detail of newConfigWrapper,
// it exists only to allow testing it with an artificial home directory.
func newConfigWrapperWithHomeDir(ctx *types.SystemContext, homeDir string) configWrapper {
var wrapper configWrapper
userRegistriesFilePath := filepath.Join(homeDir, userRegistriesFile)
userRegistriesDirPath := filepath.Join(homeDir, userRegistriesDir)
// decide configPath using per-user path or system file
if ctx != nil && ctx.SystemRegistriesConfPath != "" {
wrapper.configPath = ctx.SystemRegistriesConfPath
} else if err := fileutils.Exists(userRegistriesFilePath); err == nil {
// per-user registries.conf exists, not reading system dir
// return config dirs from ctx or per-user one
wrapper.configPath = userRegistriesFilePath
if ctx != nil && ctx.SystemRegistriesConfDirPath != "" {
wrapper.configDirPath = ctx.SystemRegistriesConfDirPath
} else {
wrapper.userConfigDirPath = userRegistriesDirPath
}
return wrapper
} else if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" {
wrapper.configPath = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemRegistriesConfPath)
} else {
wrapper.configPath = systemRegistriesConfPath
}
// potentially use both system and per-user dirs if not using per-user config file
if ctx != nil && ctx.SystemRegistriesConfDirPath != "" {
// dir explicitly chosen: use only that one
wrapper.configDirPath = ctx.SystemRegistriesConfDirPath
} else if ctx != nil && ctx.RootForImplicitAbsolutePaths != "" {
wrapper.configDirPath = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemRegistriesConfDirPath)
wrapper.userConfigDirPath = userRegistriesDirPath
} else {
wrapper.configDirPath = systemRegistriesConfDirPath
wrapper.userConfigDirPath = userRegistriesDirPath
if ctx != nil {
wrapper.systemRegistriesConfPath = ctx.SystemRegistriesConfPath
wrapper.systemRegistriesConfDirPath = ctx.SystemRegistriesConfDirPath
wrapper.rootForImplicitAbsolutePaths = ctx.RootForImplicitAbsolutePaths
}
return wrapper
}
// ConfigurationSourceDescription returns a string containers paths of registries.conf and registries.conf.d
// ConfigurationSourceDescription returns a string of one or more paths to registries.conf and registries.conf.d.
// This may return an empty string in case it fails to resolve all paths.
func ConfigurationSourceDescription(ctx *types.SystemContext) string {
wrapper := newConfigWrapper(ctx)
configSources := []string{wrapper.configPath}
if wrapper.configDirPath != "" {
configSources = append(configSources, wrapper.configDirPath)
configWrapper := newConfigWrapper(ctx)
paths, err := configfile.GetSearchPaths(configWrapper.toConfigFileOptions())
if err != nil {
return ""
}
if wrapper.userConfigDirPath != "" {
configSources = append(configSources, wrapper.userConfigDirPath)
}
return strings.Join(configSources, ", ")
return fmt.Sprintf("%q", slices.Concat(paths.MainFiles, paths.DropInDirectories))
}
// configMutex is used to synchronize concurrent accesses to configCache.
@ -647,58 +628,7 @@ func getConfig(ctx *types.SystemContext) (*parsedConfig, error) {
}
configMutex.Unlock()
return tryUpdatingCache(ctx, wrapper)
}
// dropInConfigs returns a slice of drop-in-configs from the registries.conf.d
// directory.
func dropInConfigs(wrapper configWrapper) ([]string, error) {
var (
configs []string
dirPaths []string
)
if wrapper.configDirPath != "" {
dirPaths = append(dirPaths, wrapper.configDirPath)
}
if wrapper.userConfigDirPath != "" {
dirPaths = append(dirPaths, wrapper.userConfigDirPath)
}
for _, dirPath := range dirPaths {
err := filepath.WalkDir(dirPath,
// WalkFunc to read additional configs
func(path string, d fs.DirEntry, err error) error {
switch {
case err != nil:
// return error (could be a permission problem)
return err
case d == nil:
// this should only happen when err != nil but let's be sure
return nil
case d.IsDir():
if path != dirPath {
// make sure to not recurse into sub-directories
return filepath.SkipDir
}
// ignore directories
return nil
default:
// only add *.conf files
if strings.HasSuffix(path, ".conf") {
configs = append(configs, path)
}
return nil
}
},
)
if err != nil && !os.IsNotExist(err) {
// Ignore IsNotExist errors: most systems won't have a registries.conf.d
// directory.
return nil, fmt.Errorf("reading registries.conf.d: %w", err)
}
}
return configs, nil
return tryUpdatingCache(wrapper)
}
// TryUpdatingCache loads the configuration from the provided `SystemContext`
@ -707,7 +637,7 @@ func dropInConfigs(wrapper configWrapper) ([]string, error) {
// It returns the resulting configuration; this is DEPRECATED and may not correctly
// reflect any future data handled by this package.
func TryUpdatingCache(ctx *types.SystemContext) (*V2RegistriesConf, error) {
config, err := tryUpdatingCache(ctx, newConfigWrapper(ctx))
config, err := tryUpdatingCache(newConfigWrapper(ctx))
if err != nil {
return nil, err
}
@ -716,47 +646,31 @@ func TryUpdatingCache(ctx *types.SystemContext) (*V2RegistriesConf, error) {
// tryUpdatingCache implements TryUpdatingCache with an additional configWrapper
// argument to avoid redundantly calculating the config paths.
func tryUpdatingCache(ctx *types.SystemContext, wrapper configWrapper) (*parsedConfig, error) {
func tryUpdatingCache(wrapper configWrapper) (*parsedConfig, error) {
configMutex.Lock()
defer configMutex.Unlock()
// load the config
config, err := loadConfigFile(wrapper.configPath, false)
if err != nil {
// Continue with an empty []Registry if we use the default config, which
// implies that the config path of the SystemContext isn't set.
//
// Note: if ctx.SystemRegistriesConfPath points to the default config,
// we will still return an error.
if os.IsNotExist(err) && (ctx == nil || ctx.SystemRegistriesConfPath == "") {
config = &parsedConfig{}
config.partialV2 = V2RegistriesConf{Registries: []Registry{}}
config.aliasCache, err = newShortNameAliasCache("", &shortNameAliasConf{})
if err != nil {
return nil, err // Should never happen
}
} else {
return nil, fmt.Errorf("loading registries configuration %q: %w", wrapper.configPath, err)
}
config := &parsedConfig{
partialV2: V2RegistriesConf{Registries: []Registry{}},
}
// Load the configs from the conf directory path.
dinConfigs, err := dropInConfigs(wrapper)
var err error
config.aliasCache, err = newShortNameAliasCache("", &shortNameAliasConf{})
if err != nil {
return nil, err
return nil, err // Should never happen
}
for _, path := range dinConfigs {
// Enforce v2 format for drop-in-configs.
dropIn, err := loadConfigFile(path, true)
for item, err := range configfile.Read(wrapper.toConfigFileOptions()) {
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
// file must have been removed between the directory listing
// and the open call, ignore that as it is a expected race
continue
}
return nil, fmt.Errorf("loading drop-in registries configuration %q: %w", path, err)
return nil, err
}
config.updateWithConfigurationFrom(dropIn)
logrus.Debugf("Loading registries configuration %q", item.Name)
parsed, err := loadConfigFile(item)
if err != nil {
return nil, fmt.Errorf("loading registries configuration %q: %w", item.Name, err)
}
config.updateWithConfigurationFrom(parsed)
}
if config.shortNameMode == types.ShortNameModeInvalid {
@ -947,42 +861,26 @@ func findRegistryWithParsedConfig(config *parsedConfig, ref string) (*Registry,
}
// loadConfigFile loads and unmarshals a single config file.
// Use forceV2 if the config must in the v2 format.
func loadConfigFile(path string, forceV2 bool) (*parsedConfig, error) {
logrus.Debugf("Loading registries configuration %q", path)
func loadConfigFile(item *configfile.Item) (*parsedConfig, error) {
// tomlConfig allows us to unmarshal either V1 or V2 simultaneously.
type tomlConfig struct {
V2RegistriesConf
V1RegistriesConf // for backwards compatibility with sysregistries v1
V1RegistriesConf // to detect no-longer-supported use of the v1 format
}
// Load the tomlConfig. Note that `DecodeFile` will overwrite set fields.
var combinedTOML tomlConfig
meta, err := toml.DecodeFile(path, &combinedTOML)
meta, err := toml.NewDecoder(item.Reader).Decode(&combinedTOML)
if err != nil {
return nil, err
}
if keys := meta.Undecoded(); len(keys) > 0 {
logrus.Debugf("Failed to decode keys %q from %q", keys, path)
logrus.Debugf("Failed to decode keys %q from %q", keys, item.Name)
}
if combinedTOML.V1RegistriesConf.hasSetField() {
// Enforce the v2 format if requested.
if forceV2 {
return nil, &InvalidRegistries{s: "registry must be in v2 format but is in v1"}
}
// Convert a v1 config into a v2 config.
if combinedTOML.V2RegistriesConf.hasSetField() {
return nil, &InvalidRegistries{s: fmt.Sprintf("mixing sysregistry v1/v2 is not supported: %#v", combinedTOML)}
}
converted, err := combinedTOML.V1RegistriesConf.ConvertToV2()
if err != nil {
return nil, err
}
combinedTOML.V1RegistriesConf = V1RegistriesConf{}
combinedTOML.V2RegistriesConf = *converted
// V1 format is no longer supported, produce hard error so callers know they must update the config.
return nil, &InvalidRegistries{s: "registries.conf must be in v2 format but is in v1"}
}
res := parsedConfig{partialV2: combinedTOML.V2RegistriesConf}
@ -992,7 +890,7 @@ func loadConfigFile(path string, forceV2 bool) (*parsedConfig, error) {
return nil, err
}
res.unqualifiedSearchRegistriesOrigin = path
res.unqualifiedSearchRegistriesOrigin = item.Name
if len(res.partialV2.ShortNameMode) > 0 {
mode, err := parseShortNameMode(res.partialV2.ShortNameMode)
@ -1016,7 +914,7 @@ func loadConfigFile(path string, forceV2 bool) (*parsedConfig, error) {
}
// Parse and validate short-name aliases.
cache, err := newShortNameAliasCache(path, &res.partialV2.shortNameAliasConf)
cache, err := newShortNameAliasCache(item.Name, &res.partialV2.shortNameAliasConf)
if err != nil {
return nil, fmt.Errorf("validating short-name aliases: %w", err)
}

View file

@ -84,7 +84,13 @@ const (
stagingDir = "staging"
tempDirName = "tempdirs"
lowerFile = "lower"
maxDepth = 500
// lowerLayersFile references lower layers directly by layer ID
// instead of going through the l/ symlinks. The code appends
// "/diff" itself when consuming entries. It is preferred over
// lowerFile when present. The old lowerFile is still written
// for backward compatibility with older tools.
lowerLayersFile = "lower-layers"
maxDepth = 500
stagingLockFile = "staging.lock"
@ -327,7 +333,16 @@ func Init(home string, options graphdriver.Options) (graphdriver.Driver, error)
runhome := filepath.Join(options.RunRoot, filepath.Base(home))
// Create the driver home dir
// Create the driver home dir.
// NOTE: the l/ subdirectory currently also serves as an anchor that
// prevents the home directory from being removed when all layers are
// deleted. If l/ is dropped entirely in the future, an alternative
// mechanism (e.g. a sentinel file) must be put in place to keep the
// home directory around. Without it, supportsOverlay() on the error
// path and checkAndRecordOverlaySupport() would successfully rmdir
// the home, breaking XFS project quotas set on the directory and
// causing ScanPriorDrivers() to no longer detect the overlay driver
// as in use.
if err := os.MkdirAll(path.Join(home, linkDir), 0o755); err != nil {
return nil, err
}
@ -1178,6 +1193,35 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnl
}
}
// Write a lower-layers file referencing layers by ID instead of
// l/ symlink references. The reading side appends "/diff" itself.
parentDir := d.dir(parent)
layerLower := parent
parentLower, err := os.ReadFile(path.Join(parentDir, lowerLayersFile))
if err == nil {
layerLower += ":" + string(parentLower)
} else if !errors.Is(err, unix.ENOENT) {
return err
} else {
// Parent has no lower-layers file. Convert old-format lower
// entries (l/ symlinks) to layer IDs.
oldLower, err := os.ReadFile(path.Join(parentDir, lowerFile))
if err == nil {
for _, s := range strings.Split(string(oldLower), ":") {
target, err := os.Readlink(d.dir(s))
if err != nil {
return fmt.Errorf("reading symlink for lower %q: %w", s, err)
}
layerLower += ":" + filepath.Base(filepath.Dir(target))
}
} else if !errors.Is(err, unix.ENOENT) {
return err
}
}
if err := os.WriteFile(path.Join(dir, lowerLayersFile), []byte(layerLower), 0o666); err != nil {
return err
}
return nil
}
@ -1215,20 +1259,9 @@ func (d *Driver) getLower(parent string) (string, error) {
return "", err
}
// Read Parent link fileA
parentLink, err := os.ReadFile(path.Join(parentDir, "link"))
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return "", err
}
logrus.Warnf("Can't read parent link %q because it does not exist. Going through storage to recreate the missing links.", path.Join(parentDir, "link"))
if err := d.recreateSymlinks(); err != nil {
return "", fmt.Errorf("recreating the links: %w", err)
}
parentLink, err = os.ReadFile(path.Join(parentDir, "link"))
if err != nil {
return "", err
}
return "", err
}
lowers := []string{path.Join(linkDir, string(parentLink))}
@ -1283,27 +1316,25 @@ func (d *Driver) dir2(id string, useImageStore bool) (string, string, bool) {
func (d *Driver) getLowerDirs(id string) ([]string, error) {
var lowersArray []string
lowers, err := os.ReadFile(path.Join(d.dir(id), lowerFile))
dir := d.dir(id)
lowers, err := os.ReadFile(path.Join(dir, lowerLayersFile))
if err != nil {
if !errors.Is(err, unix.ENOENT) {
return nil, err
}
lowers, err = os.ReadFile(path.Join(dir, lowerFile))
}
if err == nil {
for s := range strings.SplitSeq(string(lowers), ":") {
lower := d.dir(s)
lp, err := os.Readlink(lower)
// if the link does not exist, we lost the symlinks during a sudden reboot.
// Let's go ahead and recreate those symlinks.
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
logrus.Warnf("Can't read link %q because it does not exist. A storage corruption might have occurred, attempting to recreate the missing symlinks. It might be best wipe the storage to avoid further errors due to storage corruption.", lower)
if err := d.recreateSymlinks(); err != nil {
return nil, fmt.Errorf("recreating the missing symlinks: %w", err)
}
// let's call Readlink on lower again now that we have recreated the missing symlinks
lp, err = os.Readlink(lower)
if err != nil {
return nil, err
}
} else {
return nil, err
if errors.Is(err, syscall.EINVAL) {
// Not a symlink: layer ID, append /diff.
lowersArray = append(lowersArray, path.Join(lower, "diff"))
continue
}
return nil, err
}
lowersArray = append(lowersArray, path.Clean(d.dir(path.Join("link", lp))))
}
@ -1415,112 +1446,6 @@ func (d *Driver) DeferredRemove(id string) (tempdir.CleanupTempDirFunc, error) {
return t.Cleanup, nil
}
// recreateSymlinks goes through the driver's home directory and checks if the diff directory
// under each layer has a symlink created for it under the linkDir. If the symlink does not
// exist, it creates them
func (d *Driver) recreateSymlinks() error {
// We have at most 3 corrective actions per layer, so 10 iterations is plenty.
const maxIterations = 10
// List all the directories under the home directory
dirs, err := os.ReadDir(d.home)
if err != nil {
return fmt.Errorf("reading driver home directory %q: %w", d.home, err)
}
// This makes the link directory if it doesn't exist
if err := idtools.MkdirAllAndChown(path.Join(d.home, linkDir), 0o755, idtools.IDPair{UID: 0, GID: 0}); err != nil {
return err
}
// Keep looping as long as we take some corrective action in each iteration
var errs error
madeProgress := true
iterations := 0
for madeProgress {
errs = nil
madeProgress = false
// Check that for each layer, there's a link in "l" with the name in
// the layer's "link" file that points to the layer's "diff" directory.
for _, dir := range dirs {
// Skip over the linkDir, stagingDir, tempDirName and anything that is not a directory
if dir.Name() == linkDir || dir.Name() == stagingDir || dir.Name() == tempDirName || !dir.IsDir() {
continue
}
// Read the "link" file under each layer to get the name of the symlink
data, err := os.ReadFile(path.Join(d.dir(dir.Name()), "link"))
if err != nil {
errs = errors.Join(errs, fmt.Errorf("reading name of symlink for %q: %w", dir.Name(), err))
continue
}
linkPath := path.Join(d.home, linkDir, strings.Trim(string(data), "\n"))
// Check if the symlink exists, and if it doesn't, create it again with the
// name we got from the "link" file
err = fileutils.Lexists(linkPath)
if err != nil && errors.Is(err, fs.ErrNotExist) {
if err := os.Symlink(path.Join("..", dir.Name(), "diff"), linkPath); err != nil {
errs = errors.Join(errs, err)
continue
}
madeProgress = true
} else if err != nil {
errs = errors.Join(errs, err)
continue
}
}
// linkDirFullPath is the full path to the linkDir
linkDirFullPath := filepath.Join(d.home, "l")
// Now check if we somehow lost a "link" file, by making sure
// that each symlink we have corresponds to one.
links, err := os.ReadDir(linkDirFullPath)
if err != nil {
errs = errors.Join(errs, err)
continue
}
// Go through all of the symlinks in the "l" directory
for _, link := range links {
// Read the symlink's target, which should be "../$layer/diff"
target, err := os.Readlink(filepath.Join(linkDirFullPath, link.Name()))
if err != nil {
errs = errors.Join(errs, err)
continue
}
targetComponents := strings.Split(target, string(os.PathSeparator))
if len(targetComponents) != 3 || targetComponents[0] != ".." || targetComponents[2] != "diff" {
errs = errors.Join(errs, fmt.Errorf("link target of %q looks weird: %q", link, target))
// force the link to be recreated on the next pass
if err := os.Remove(filepath.Join(linkDirFullPath, link.Name())); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
errs = errors.Join(errs, fmt.Errorf("removing link %q: %w", link, err))
} // else don’t report any error, but also don’t set madeProgress.
continue
}
madeProgress = true
continue
}
// Reconstruct the name of the target's link file and check that
// it has the basename of our symlink in it.
targetID := targetComponents[1]
linkFile := filepath.Join(d.dir(targetID), "link")
data, err := os.ReadFile(linkFile)
if err != nil || string(data) != link.Name() {
// NOTE: If two or more links point to the same target, we will update linkFile
// with every value of link.Name(), and set madeProgress = true every time.
if err := os.WriteFile(linkFile, []byte(link.Name()), 0o644); err != nil {
errs = errors.Join(errs, fmt.Errorf("correcting link for layer %s: %w", targetID, err))
continue
}
madeProgress = true
}
}
iterations++
if iterations >= maxIterations {
errs = errors.Join(errs, fmt.Errorf("reached %d iterations in overlay graph driver’s recreateSymlink, giving up", iterations))
break
}
}
return errs
}
// Get creates and mounts the required file system for the given id and returns the mount path.
func (d *Driver) Get(id string, options graphdriver.MountOpts) (string, error) {
return d.get(id, false, options)
@ -1615,9 +1540,15 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
readWrite = false
}
lowers, err := os.ReadFile(path.Join(dir, lowerFile))
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return "", err
lowers, err := os.ReadFile(path.Join(dir, lowerLayersFile))
if err != nil {
if !errors.Is(err, unix.ENOENT) {
return "", err
}
lowers, err = os.ReadFile(path.Join(dir, lowerFile))
if err != nil && !os.IsNotExist(err) {
return "", err
}
}
splitLowers := strings.Split(string(lowers), ":")
if len(splitLowers) > maxDepth {
@ -1729,16 +1660,7 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
}
lower = ""
}
// if it is a "not found" error, that means the symlinks were lost in a sudden reboot
// so call the recreateSymlinks function to go through all the layer dirs and recreate
// the symlinks with the name from their respective "link" files
if lower == "" && errors.Is(err, fs.ErrNotExist) {
logrus.Warnf("Can't stat lower layer %q because it does not exist. Going through storage to recreate the missing symlinks.", newpath)
if err := d.recreateSymlinks(); err != nil {
return "", fmt.Errorf("recreating the missing symlinks: %w", err)
}
lower = newpath
} else if lower == "" {
if lower == "" {
return "", fmt.Errorf("can't stat lower layer %q: %w", newpath, err)
}
} else {
@ -1751,7 +1673,12 @@ func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountO
linkContent, err := os.Readlink(lower)
if err != nil {
return "", err
if !errors.Is(err, syscall.EINVAL) {
return "", err
}
// Not a symlink: layer ID from lower-layers, append /diff.
lower = path.Join(lower, "diff")
linkContent = lower
}
lowerID := filepath.Base(filepath.Dir(linkContent))
composefsMount, err := maybeAddComposefsMount(lowerID, i+1, readWrite)

View file

@ -57,6 +57,25 @@ type File struct {
// NOTE: This does NOT affect paths starting by $HOME or environment variables paths.
RootForImplicitAbsolutePaths string
// CustomConfigFilePath is the path to a specific file that will be parsed as main file instead
// of the default location files. Unlike the regular parsing logic if set this file must exists
// or ErrNotExist will be returned. Note when just using this option without also
// CustomConfigFileDropInDirectory it means the regular drop in directories are still searched
// assuming DoNotLoadDropInFiles is not set.
// This has higher priority over the EnvironmentName variable, so if set the env is ignored.
// RootForImplicitAbsolutePaths will not be used for this path.
// Optional.
CustomConfigFilePath string
// CustomConfigFileDropInDirectory is the path to a specific drop in directory that will be searched
// instead of the default location. Note when just using this option without also
// CustomConfigFilePath it means the regular main file location is still being read assuming
// DoNotLoadMainFiles is not set.
// This has higher priority over the EnvironmentName + "_OVERRIDE" variable, so if set the env is ignored.
// RootForImplicitAbsolutePaths will not be used for this path.
// Optional.
CustomConfigFileDropInDirectory string
// DoNotLoadMainFiles should be set if only the Drop In files should be loaded.
DoNotLoadMainFiles bool
@ -90,19 +109,40 @@ type Item struct {
Name string
}
func getConfName(name, extension string, noExtension bool) string {
if noExtension {
return name
}
return name + "." + extension
type SearchPaths struct {
// MainFiles are the main config file paths, ordered from highest priority to lower ones.
// For example: $HOME/..., then /etc/..., then /usr/...
// Can be empty if there are no main files for the given config.
MainFiles []string
// DropInDirectories is the list of drop in directories read by this config file, again
// ordered from highest priority to lower ones.
// Can be empty if there are no drop in directories for the given config.
DropInDirectories []string
// ModuleDirectories is the list of module directories checked by this config file, again
// ordered from highest priority to lower ones.
// Will be empty if no modules were request for the given conf.
ModuleDirectories []string
// The file path from conf.EnvironmentName + "_OVERRIDE" env if it must be parsed for the given config.
// Can be empty.
ExtraOverrideFile string
}
// Read parses all config files with the specified options and returns an iterator which returns all files as Item in the right order.
// If an error is returned by the iterator then this must be treated as fatal error and must fail the config file parsing.
// Expected ENOENT errors are already ignored in this function and must not be handled again by callers.
// The given File options must not be nil and populated with valid options.
func Read(conf *File) iter.Seq2[*Item, error] {
configFileName := getConfName(conf.Name, conf.Extension, conf.DoNotUseExtensionForConfigName)
func (f *File) getConfName() string {
if f.DoNotUseExtensionForConfigName {
return f.Name
}
return f.Name + "." + f.Extension
}
// GetSearchPaths returns the list of files which will be tried to be parsed.
// See the doc of [SearchPaths] for more information.
func GetSearchPaths(conf *File) (SearchPaths, error) {
paths, _, err := getSearchPaths(conf)
return paths, err
}
func getSearchPaths(conf *File) (SearchPaths, bool, error) {
configFileName := conf.getConfName()
// Note this can be empty which is a valid case and should be simply ignored then.
defaultConfig := systemConfigPath
@ -122,13 +162,95 @@ func Read(conf *File) iter.Seq2[*Item, error] {
}
}
// userConfig can be empty as well
userConfig, err := UserConfigPath()
if err != nil {
return SearchPaths{}, false, err
}
if userConfig != "" {
userConfig = filepath.Join(userConfig, configFileName)
}
// main files
ignoreENOENT := true
shouldLoadDropIns := true
var mainFiles []string
if !conf.DoNotLoadMainFiles {
if conf.CustomConfigFilePath != "" {
mainFiles = append(mainFiles, conf.CustomConfigFilePath)
ignoreENOENT = false
// Only consider the env if no custom path was explicitly set.
// As this path often comes from cli options it is important it wins over the env value.
} else if path := os.Getenv(conf.EnvironmentName); path != "" && conf.EnvironmentName != "" {
mainFiles = append(mainFiles, path)
ignoreENOENT = false
// Also when the env is set skip the loading of drop in files, modules and _OVERRIDE env are still read though.
shouldLoadDropIns = false
} else {
// default search paths
if userConfig != "" {
mainFiles = append(mainFiles, userConfig)
}
if overrideConfig != "" {
mainFiles = append(mainFiles, overrideConfig)
}
if defaultConfig != "" {
mainFiles = append(mainFiles, defaultConfig)
}
}
}
// drop in dirs
var dropInDirs []string
var extraOverrideFilePath string
if !conf.DoNotLoadDropInFiles {
if shouldLoadDropIns {
if conf.CustomConfigFileDropInDirectory != "" {
dropInDirs = append(dropInDirs, conf.CustomConfigFileDropInDirectory)
} else {
// default search paths
dropInDirs = getDropInPaths(defaultConfig, overrideConfig, userConfig, "."+conf.Extension, conf.UserId)
}
}
if conf.EnvironmentName != "" && conf.CustomConfigFileDropInDirectory == "" {
if path := os.Getenv(conf.EnvironmentName + "_OVERRIDE"); path != "" {
extraOverrideFilePath = path
}
}
}
// modules
var modDirs []string
if len(conf.Modules) > 0 {
modDirs = moduleDirectories(defaultConfig, overrideConfig, userConfig)
}
return SearchPaths{
MainFiles: mainFiles,
DropInDirectories: dropInDirs,
ModuleDirectories: modDirs,
ExtraOverrideFile: extraOverrideFilePath,
},
ignoreENOENT,
nil
}
// Read parses all config files with the specified options and returns an iterator which returns all files as Item in the right order.
// If an error is returned by the iterator then this must be treated as fatal error and must fail the config file parsing.
// Expected ENOENT errors are already ignored in this function and must not be handled again by callers.
// The given File options must not be nil and populated with valid options.
func Read(conf *File) iter.Seq2[*Item, error] {
return func(yield func(*Item, error) bool) {
paths, ignoreMainENOENT, err := getSearchPaths(conf)
if err != nil {
yield(nil, err)
return
}
usedPaths := make([]string, 0, 8)
foundAny := false
shouldLoadMainFile := !conf.DoNotLoadMainFiles
shouldLoadDropIns := !conf.DoNotLoadDropInFiles
yieldAndClose := func(f *os.File) bool {
foundAny = true
ok := yield(&Item{
@ -146,61 +268,31 @@ func Read(conf *File) iter.Seq2[*Item, error] {
return ok
}
if conf.EnvironmentName != "" {
if path := os.Getenv(conf.EnvironmentName); path != "" {
usedPaths = append(usedPaths, path)
f, err := os.Open(path)
// Do not ignore ErrNotExist here, we want to hard error if users set a wrong path here.
if err != nil {
yield(nil, err)
return
}
if !yieldAndClose(f) {
return
}
// Also when the env is set skip the loading of the main and drop in files, modules and _OVERRIDE env are still read though.
shouldLoadMainFile = false
shouldLoadDropIns = false
for _, path := range paths.MainFiles {
if path == "" {
continue
}
}
// userConfig can be empty as well
userConfig, err := UserConfigPath()
if err != nil {
// return error via iterator
yield(nil, err)
return
}
if userConfig != "" {
userConfig = filepath.Join(userConfig, configFileName)
}
if shouldLoadMainFile {
for _, path := range []string{userConfig, overrideConfig, defaultConfig} {
if path == "" {
usedPaths = append(usedPaths, path)
f, err := os.Open(path)
if err != nil {
// only ignore ErrNotExist when needed, all other errors get return to the caller via yield
if ignoreMainENOENT && errors.Is(err, fs.ErrNotExist) {
continue
}
usedPaths = append(usedPaths, path)
f, err := os.Open(path)
// only ignore ErrNotExist, all other errors get return to the caller via yield
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
continue
}
yield(nil, err)
return
}
if !yieldAndClose(f) {
return
}
// we only read the first file
break
yield(nil, err)
return
}
if !yieldAndClose(f) {
return
}
// we only read the first found file
break
}
if shouldLoadDropIns {
files, err := readDropIns(defaultConfig, overrideConfig, userConfig, conf.Extension, conf.UserId)
if len(paths.DropInDirectories) > 0 {
suffix := "." + conf.Extension
files, err := readDropInsFromPaths(paths.DropInDirectories, suffix)
if err != nil {
// return error via iterator
yield(nil, err)
@ -209,7 +301,7 @@ func Read(conf *File) iter.Seq2[*Item, error] {
for _, file := range files {
usedPaths = append(usedPaths, file)
f, err := os.Open(file)
// only ignore ErrNotExist, all other errors get return to the caller via yield
// always ignore ErrNotExist, all other errors get return to the caller via yield
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
continue
@ -225,10 +317,9 @@ func Read(conf *File) iter.Seq2[*Item, error] {
}
if len(conf.Modules) > 0 {
dirs := moduleDirectories(defaultConfig, overrideConfig, userConfig)
resolvedModules := make([]string, 0, len(conf.Modules))
for _, module := range conf.Modules {
f, err := resolveModule(module, dirs, &usedPaths)
f, err := resolveModule(module, paths.ModuleDirectories, &usedPaths)
if err != nil {
yield(nil, fmt.Errorf("could not resolve module: %w", err))
return
@ -241,24 +332,22 @@ func Read(conf *File) iter.Seq2[*Item, error] {
conf.Modules = resolvedModules
}
if conf.EnvironmentName != "" && !conf.DoNotLoadDropInFiles {
if paths.ExtraOverrideFile != "" {
// The _OVERRIDE env must be appended after loading all files, even modules.
if path := os.Getenv(conf.EnvironmentName + "_OVERRIDE"); path != "" {
usedPaths = append(usedPaths, path)
f, err := os.Open(path)
// Do not ignore ErrNotExist here, we want to hard error if users set a wrong path here.
if err != nil {
yield(nil, err)
return
}
if !yieldAndClose(f) {
return
}
usedPaths = append(usedPaths, paths.ExtraOverrideFile)
f, err := os.Open(paths.ExtraOverrideFile)
// Do not ignore ErrNotExist here, we want to hard error if users set a wrong path here.
if err != nil {
yield(nil, err)
return
}
if !yieldAndClose(f) {
return
}
}
if conf.ErrorIfNotFound && !foundAny {
yield(nil, fmt.Errorf("%w: no %s file found; searched paths: %q", ErrConfigFileNotFound, configFileName, usedPaths))
yield(nil, fmt.Errorf("%w: no %s file found; searched paths: %q", ErrConfigFileNotFound, conf.getConfName(), usedPaths))
return
}
}
@ -266,24 +355,27 @@ func Read(conf *File) iter.Seq2[*Item, error] {
const dropInSuffix = ".d"
func readDropIns(defaultConfig, overrideConfig, userConfig, extension string, uid int) ([]string, error) {
dropInMap := make(map[string]string)
func getDropInPaths(defaultConfig, overrideConfig, userConfig, suffix string, uid int) []string {
paths := make([]string, 0, 7)
suffix := "." + extension
if defaultConfig != "" {
paths = append(paths, getDropInPaths(defaultConfig, suffix, uid)...)
}
if overrideConfig != "" {
paths = append(paths, getDropInPaths(overrideConfig, suffix, uid)...)
}
if userConfig != "" {
// the $HOME config only has one .d path not the rootful/rootless ones.
paths = append(paths, userConfig+dropInSuffix)
}
if overrideConfig != "" {
paths = append(paths, getDropInPathsUnderMain(overrideConfig, suffix, uid)...)
}
if defaultConfig != "" {
paths = append(paths, getDropInPathsUnderMain(defaultConfig, suffix, uid)...)
}
for _, path := range paths {
return paths
}
func readDropInsFromPaths(paths []string, suffix string) ([]string, error) {
dropInMap := make(map[string]string)
for _, path := range slices.Backward(paths) {
entries, err := os.ReadDir(path)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
@ -306,7 +398,7 @@ func readDropIns(defaultConfig, overrideConfig, userConfig, extension string, ui
return files, nil
}
func getDropInPaths(mainPath, suffix string, uid int) []string {
func getDropInPathsUnderMain(mainPath, suffix string, uid int) []string {
paths := make([]string, 0, 3)
paths = append(paths, mainPath+dropInSuffix)

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -66,9 +66,13 @@ const (
// app_profile_id: profiles/prof_qux
// }
//
// The routing header consists of one or multiple key-value pairs. Every key
// and value must be percent-encoded, and joined together in the format of
// `key1=value1&key2=value2`.
// The routing header consists of one or multiple key-value pairs. The order of
// the key-value pairs is undefined, the order of the `routing_parameters` in
// the `RoutingRule` only matters for the evaluation order of the path
// templates when `field` is the same. See the examples below for more details.
//
// Every key and value in the routing header must be percent-encoded,
// and joined together in the following format: `key1=value1&key2=value2`.
// The examples below skip the percent-encoding for readability.
//
// # Example 1

View file

@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

View file

@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -127,14 +127,13 @@ var file_google_rpc_status_proto_rawDesc = []byte{
0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61,
0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52,
0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x61, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e,
0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x5e, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x42, 0x0b, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73,
0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3b, 0x73, 0x74, 0x61, 0x74,
0x75, 0x73, 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x03, 0x52, 0x50, 0x43, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
0x75, 0x73, 0xa2, 0x02, 0x03, 0x52, 0x50, 0x43, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

30
vendor/modules.txt vendored
View file

@ -186,7 +186,7 @@ github.com/distribution/reference
## explicit
github.com/docker/distribution/registry/api/errcode
github.com/docker/distribution/registry/api/v2
# github.com/docker/docker-credential-helpers v0.9.5
# github.com/docker/docker-credential-helpers v0.9.6
## explicit; go 1.21
github.com/docker/docker-credential-helpers/client
github.com/docker/docker-credential-helpers/credentials
@ -341,7 +341,7 @@ github.com/lufia/plan9stats
github.com/manifoldco/promptui
github.com/manifoldco/promptui/list
github.com/manifoldco/promptui/screenbuf
# github.com/mattn/go-runewidth v0.0.22
# github.com/mattn/go-runewidth v0.0.23
## explicit; go 1.20
github.com/mattn/go-runewidth
# github.com/mattn/go-shellwords v1.0.13
@ -562,8 +562,8 @@ github.com/rootless-containers/rootlesskit/v2/pkg/port/portutil
# github.com/seccomp/libseccomp-golang v0.11.1
## explicit; go 1.19
github.com/seccomp/libseccomp-golang
# github.com/secure-systems-lab/go-securesystemslib v0.10.0
## explicit; go 1.24.0
# github.com/secure-systems-lab/go-securesystemslib v0.11.0
## explicit; go 1.25.0
github.com/secure-systems-lab/go-securesystemslib/encrypted
# github.com/shirou/gopsutil/v4 v4.26.3
## explicit; go 1.24.0
@ -665,12 +665,12 @@ go.etcd.io/bbolt/internal/freelist
## 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.67.0
# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0
## explicit; go 1.25.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
# go.opentelemetry.io/otel v1.42.0
# go.opentelemetry.io/otel v1.43.0
## explicit; go 1.25.0
go.opentelemetry.io/otel
go.opentelemetry.io/otel/attribute
@ -685,12 +685,12 @@ go.opentelemetry.io/otel/propagation
go.opentelemetry.io/otel/semconv/v1.37.0
go.opentelemetry.io/otel/semconv/v1.40.0
go.opentelemetry.io/otel/semconv/v1.40.0/httpconv
# go.opentelemetry.io/otel/metric v1.42.0
# go.opentelemetry.io/otel/metric v1.43.0
## explicit; go 1.25.0
go.opentelemetry.io/otel/metric
go.opentelemetry.io/otel/metric/embedded
go.opentelemetry.io/otel/metric/noop
# go.opentelemetry.io/otel/trace v1.42.0
# go.opentelemetry.io/otel/trace v1.43.0
## explicit; go 1.25.0
go.opentelemetry.io/otel/trace
go.opentelemetry.io/otel/trace/embedded
@ -733,7 +733,7 @@ go.podman.io/buildah/pkg/sshagent
go.podman.io/buildah/pkg/util
go.podman.io/buildah/pkg/volumes
go.podman.io/buildah/util
# go.podman.io/common v0.67.2-0.20260420103546-618304d6f83d
# go.podman.io/common v0.67.2-0.20260423135811-cbaa5f41e643
## explicit; go 1.25.6
go.podman.io/common/internal
go.podman.io/common/libimage
@ -799,7 +799,7 @@ go.podman.io/common/pkg/umask
go.podman.io/common/pkg/util
go.podman.io/common/pkg/version
go.podman.io/common/version
# go.podman.io/image/v5 v5.39.3-0.20260420103546-618304d6f83d
# go.podman.io/image/v5 v5.39.3-0.20260423135811-cbaa5f41e643
## explicit; go 1.25.6
go.podman.io/image/v5/copy
go.podman.io/image/v5/directory
@ -876,7 +876,7 @@ go.podman.io/image/v5/transports
go.podman.io/image/v5/transports/alltransports
go.podman.io/image/v5/types
go.podman.io/image/v5/version
# go.podman.io/storage v1.62.1-0.20260420103546-618304d6f83d
# go.podman.io/storage v1.62.1-0.20260423135811-cbaa5f41e643
## explicit; go 1.25.0
go.podman.io/storage
go.podman.io/storage/drivers
@ -1044,12 +1044,12 @@ golang.org/x/tools/internal/stdlib
golang.org/x/tools/internal/typeparams
golang.org/x/tools/internal/typesinternal
golang.org/x/tools/internal/versions
# google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57
## explicit; go 1.24.0
# google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9
## explicit; go 1.25.0
google.golang.org/genproto/googleapis/api
google.golang.org/genproto/googleapis/api/annotations
# google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57
## explicit; go 1.24.0
# google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9
## explicit; go 1.25.0
google.golang.org/genproto/googleapis/rpc/status
# google.golang.org/grpc v1.80.0
## explicit; go 1.24.0