diff --git a/go.mod b/go.mod index 9c16b0c03..702f96031 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652 github.com/Microsoft/go-winio v0.6.0 github.com/Microsoft/hcsshim v0.10.0-rc.5 - github.com/container-orchestrated-devices/container-device-interface v0.5.1 + github.com/container-orchestrated-devices/container-device-interface v0.5.4 github.com/containerd/aufs v1.0.0 github.com/containerd/btrfs/v2 v2.0.0 github.com/containerd/cgroups/v3 v3.0.0 @@ -50,7 +50,7 @@ require ( github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb // ATM the runtime-tools commit we need are beyond the latest tag. // We use a replace to handle that until a new version is tagged. - github.com/opencontainers/runtime-tools v0.9.0 + github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 github.com/opencontainers/selinux v1.10.2 github.com/pelletier/go-toml v1.9.5 github.com/prometheus/client_golang v1.13.0 diff --git a/go.sum b/go.sum index 4ffb2f522..01f4519fa 100644 --- a/go.sum +++ b/go.sum @@ -176,8 +176,8 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/container-orchestrated-devices/container-device-interface v0.5.1 h1:nXIUTrlEgGcA/n2geY3J7yyaGGhkocSlMkKPS4Qp4c0= -github.com/container-orchestrated-devices/container-device-interface v0.5.1/go.mod h1:ZToWfSyUH5l9Rk7/bjkUUkNLz4b1mE+CVUVafuikDPY= +github.com/container-orchestrated-devices/container-device-interface v0.5.4 h1:PqQGqJqQttMP5oJ/qNGEg8JttlHqGY3xDbbcKb5T9E8= +github.com/container-orchestrated-devices/container-device-interface v0.5.4/go.mod h1:DjE95rfPiiSmG7uVXtg0z6MnPm/Lx4wxKCIts0ZE0vg= github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= diff --git a/integration/client/go.sum b/integration/client/go.sum index 349d26fb7..2301e318d 100644 --- a/integration/client/go.sum +++ b/integration/client/go.sum @@ -450,8 +450,6 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= @@ -493,7 +491,7 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/container-orchestrated-devices/container-device-interface v0.5.1/go.mod h1:ZToWfSyUH5l9Rk7/bjkUUkNLz4b1mE+CVUVafuikDPY= +github.com/container-orchestrated-devices/container-device-interface v0.5.4/go.mod h1:DjE95rfPiiSmG7uVXtg0z6MnPm/Lx4wxKCIts0ZE0vg= github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= github.com/containerd/btrfs/v2 v2.0.0/go.mod h1:swkD/7j9HApWpzl8OHfrHNxppPd9l44DFZdF94BUj9k= github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= @@ -886,7 +884,6 @@ github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmt github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -915,7 +912,6 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= @@ -1009,7 +1005,6 @@ github.com/opencontainers/selinux v1.10.2/go.mod h1:cARutUbaUrlRClyvxOICCgKixCs6 github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= @@ -1096,17 +1091,14 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -1114,7 +1106,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1176,11 +1167,8 @@ github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= go.etcd.io/etcd/pkg/v3 v3.5.4/go.mod h1:OI+TtO+Aa3nhQSppMbwE4ld3uF1/fqqwbpfndbbrEe0= @@ -1387,7 +1375,6 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -1673,7 +1660,6 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= @@ -1897,7 +1883,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/internal/multierror/multierror.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/internal/multierror/multierror.go new file mode 100644 index 000000000..07aca4a1d --- /dev/null +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/internal/multierror/multierror.go @@ -0,0 +1,82 @@ +/* + Copyright © 2022 The CDI 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 multierror + +import ( + "strings" +) + +// New combines several errors into a single error. Parameters that are nil are +// ignored. If no errors are passed in or all parameters are nil, then the +// result is also nil. +func New(errors ...error) error { + // Filter out nil entries. + numErrors := 0 + for _, err := range errors { + if err != nil { + errors[numErrors] = err + numErrors++ + } + } + if numErrors == 0 { + return nil + } + return multiError(errors[0:numErrors]) +} + +// multiError is the underlying implementation used by New. +// +// Beware that a null multiError is not the same as a nil error. +type multiError []error + +// multiError returns all individual error strings concatenated with "\n" +func (e multiError) Error() string { + var builder strings.Builder + for i, err := range e { + if i > 0 { + _, _ = builder.WriteString("\n") + } + _, _ = builder.WriteString(err.Error()) + } + return builder.String() +} + +// Append returns a new multi error all errors concatenated. Errors that are +// multi errors get flattened, nil is ignored. +func Append(err error, errors ...error) error { + var result multiError + if m, ok := err.(multiError); ok { + result = m + } else if err != nil { + result = append(result, err) + } + + for _, e := range errors { + if e == nil { + continue + } + if m, ok := e.(multiError); ok { + result = append(result, m...) + } else { + result = append(result, e) + } + } + if len(result) == 0 { + return nil + } + return result +} diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/annotations.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/annotations.go index 1055c7df8..c512ea09c 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/annotations.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/annotations.go @@ -17,9 +17,9 @@ package cdi import ( + "errors" + "fmt" "strings" - - "github.com/pkg/errors" ) const ( @@ -34,14 +34,14 @@ const ( func UpdateAnnotations(annotations map[string]string, plugin string, deviceID string, devices []string) (map[string]string, error) { key, err := AnnotationKey(plugin, deviceID) if err != nil { - return annotations, errors.Wrap(err, "CDI annotation failed") + return annotations, fmt.Errorf("CDI annotation failed: %w", err) } if _, ok := annotations[key]; ok { - return annotations, errors.Errorf("CDI annotation failed, key %q used", key) + return annotations, fmt.Errorf("CDI annotation failed, key %q used", key) } value, err := AnnotationValue(devices) if err != nil { - return annotations, errors.Wrap(err, "CDI annotation failed") + return annotations, fmt.Errorf("CDI annotation failed: %w", err) } if annotations == nil { @@ -70,7 +70,7 @@ func ParseAnnotations(annotations map[string]string) ([]string, []string, error) } for _, d := range strings.Split(value, ",") { if !IsQualifiedName(d) { - return nil, nil, errors.Errorf("invalid CDI device name %q", d) + return nil, nil, fmt.Errorf("invalid CDI device name %q", d) } devices = append(devices, d) } @@ -98,11 +98,11 @@ func AnnotationKey(pluginName, deviceID string) (string, error) { name := pluginName + "_" + strings.ReplaceAll(deviceID, "/", "_") if len(name) > maxNameLen { - return "", errors.Errorf("invalid plugin+deviceID %q, too long", name) + return "", fmt.Errorf("invalid plugin+deviceID %q, too long", name) } if c := rune(name[0]); !isAlphaNumeric(c) { - return "", errors.Errorf("invalid name %q, first '%c' should be alphanumeric", + return "", fmt.Errorf("invalid name %q, first '%c' should be alphanumeric", name, c) } if len(name) > 2 { @@ -111,13 +111,13 @@ func AnnotationKey(pluginName, deviceID string) (string, error) { case isAlphaNumeric(c): case c == '_' || c == '-' || c == '.': default: - return "", errors.Errorf("invalid name %q, invalid charcter '%c'", + return "", fmt.Errorf("invalid name %q, invalid charcter '%c'", name, c) } } } if c := rune(name[len(name)-1]); !isAlphaNumeric(c) { - return "", errors.Errorf("invalid name %q, last '%c' should be alphanumeric", + return "", fmt.Errorf("invalid name %q, last '%c' should be alphanumeric", name, c) } diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go index d653ac387..cb495ebb3 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go @@ -17,16 +17,19 @@ package cdi import ( + "errors" + "fmt" + "io/fs" + "os" "path/filepath" "sort" "strings" "sync" + "github.com/container-orchestrated-devices/container-device-interface/internal/multierror" cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go" "github.com/fsnotify/fsnotify" - "github.com/hashicorp/go-multierror" oci "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" ) // Option is an option to change some aspect of default CDI behavior. @@ -93,7 +96,7 @@ func (c *Cache) configure(options ...Option) error { for _, o := range options { if err = o(c); err != nil { - return errors.Wrapf(err, "failed to apply cache options") + return fmt.Errorf("failed to apply cache options: %w", err) } } @@ -123,8 +126,8 @@ func (c *Cache) Refresh() error { // collect and return cached errors, much like refresh() does it var result error - for _, err := range c.errors { - result = multierror.Append(result, err...) + for _, errors := range c.errors { + result = multierror.Append(result, errors...) } return result } @@ -155,7 +158,7 @@ func (c *Cache) refresh() error { return false case devPrio == oldPrio: devPath, oldPath := devSpec.GetPath(), oldSpec.GetPath() - collectError(errors.Errorf("conflicting device %q (specs %q, %q)", + collectError(fmt.Errorf("conflicting device %q (specs %q, %q)", name, devPath, oldPath), devPath, oldPath) conflicts[name] = struct{}{} } @@ -165,7 +168,7 @@ func (c *Cache) refresh() error { _ = scanSpecDirs(c.specDirs, func(path string, priority int, spec *Spec, err error) error { path = filepath.Clean(path) if err != nil { - collectError(errors.Wrapf(err, "failed to load CDI Spec"), path) + collectError(fmt.Errorf("failed to load CDI Spec %w", err), path) return nil } @@ -194,11 +197,7 @@ func (c *Cache) refresh() error { c.devices = devices c.errors = specErrors - if len(result) > 0 { - return multierror.Append(nil, result...) - } - - return nil + return multierror.New(result...) } // RefreshIfRequired triggers a refresh if necessary. @@ -219,7 +218,7 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e var unresolved []string if ociSpec == nil { - return devices, errors.Errorf("can't inject devices, nil OCI Spec") + return devices, fmt.Errorf("can't inject devices, nil OCI Spec") } c.Lock() @@ -244,22 +243,33 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e } if unresolved != nil { - return unresolved, errors.Errorf("unresolvable CDI devices %s", + return unresolved, fmt.Errorf("unresolvable CDI devices %s", strings.Join(devices, ", ")) } if err := edits.Apply(ociSpec); err != nil { - return nil, errors.Wrap(err, "failed to inject devices") + return nil, fmt.Errorf("failed to inject devices: %w", err) } return nil, nil } -// WriteSpec writes a Spec file with the given content. Priority is used -// as an index into the list of Spec directories to pick a directory for -// the file, adjusting for any under- or overflows. If name has a "json" -// or "yaml" extension it choses the encoding. Otherwise JSON encoding -// is used with a "json" extension. +// highestPrioritySpecDir returns the Spec directory with highest priority +// and its priority. +func (c *Cache) highestPrioritySpecDir() (string, int) { + if len(c.specDirs) == 0 { + return "", -1 + } + + prio := len(c.specDirs) - 1 + dir := c.specDirs[prio] + + return dir, prio +} + +// WriteSpec writes a Spec file with the given content into the highest +// priority Spec directory. If name has a "json" or "yaml" extension it +// choses the encoding. Otherwise the default YAML encoding is used. func (c *Cache) WriteSpec(raw *cdi.Spec, name string) error { var ( specDir string @@ -269,23 +279,51 @@ func (c *Cache) WriteSpec(raw *cdi.Spec, name string) error { err error ) - if len(c.specDirs) == 0 { + specDir, prio = c.highestPrioritySpecDir() + if specDir == "" { return errors.New("no Spec directories to write to") } - prio = len(c.specDirs) - 1 - specDir = c.specDirs[prio] path = filepath.Join(specDir, name) if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" { - path += ".json" + path += defaultSpecExt } - spec, err = NewSpec(raw, path, prio) + spec, err = newSpec(raw, path, prio) if err != nil { return err } - return spec.Write(true) + return spec.write(true) +} + +// RemoveSpec removes a Spec with the given name from the highest +// priority Spec directory. This function can be used to remove a +// Spec previously written by WriteSpec(). If the file exists and +// its removal fails RemoveSpec returns an error. +func (c *Cache) RemoveSpec(name string) error { + var ( + specDir string + path string + err error + ) + + specDir, _ = c.highestPrioritySpecDir() + if specDir == "" { + return errors.New("no Spec directories to remove from") + } + + path = filepath.Join(specDir, name) + if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" { + path += defaultSpecExt + } + + err = os.Remove(path) + if err != nil && errors.Is(err, fs.ErrNotExist) { + err = nil + } + + return err } // GetDevice returns the cached device for the given qualified name. @@ -370,7 +408,17 @@ func (c *Cache) GetVendorSpecs(vendor string) []*Spec { // GetSpecErrors returns all errors encountered for the spec during the // last cache refresh. func (c *Cache) GetSpecErrors(spec *Spec) []error { - return c.errors[spec.GetPath()] + var errors []error + + c.Lock() + defer c.Unlock() + + if errs, ok := c.errors[spec.GetPath()]; ok { + errors = make([]error, len(errs)) + copy(errors, errs) + } + + return errors } // GetErrors returns all errors encountered during the last @@ -436,7 +484,7 @@ func (w *watch) setup(dirs []string, dirErrors map[string]error) { w.watcher, err = fsnotify.NewWatcher() if err != nil { for _, dir := range dirs { - dirErrors[dir] = errors.Wrap(err, "failed to create watcher") + dirErrors[dir] = fmt.Errorf("failed to create watcher: %w", err) } return } @@ -519,7 +567,7 @@ func (w *watch) update(dirErrors map[string]error, removed ...string) bool { update = true } else { w.tracked[dir] = false - dirErrors[dir] = errors.Wrap(err, "failed to monitor for changes") + dirErrors[dir] = fmt.Errorf("failed to monitor for changes: %w", err) } } diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache_test_unix.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache_test_unix.go new file mode 100644 index 000000000..0ee5fb86f --- /dev/null +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache_test_unix.go @@ -0,0 +1,26 @@ +//go:build !windows +// +build !windows + +/* + Copyright © 2021 The CDI 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 cdi + +import "syscall" + +func osSync() { + syscall.Sync() +} diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache_test_windows.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache_test_windows.go new file mode 100644 index 000000000..c6dabf5fa --- /dev/null +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache_test_windows.go @@ -0,0 +1,22 @@ +//go:build windows +// +build windows + +/* + Copyright © 2021 The CDI 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 cdi + +func osSync() {} diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go index 1295f75e9..3e0d24ed8 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits.go @@ -17,18 +17,16 @@ package cdi import ( + "errors" + "fmt" "os" "path/filepath" "sort" "strings" - "github.com/pkg/errors" - "github.com/container-orchestrated-devices/container-device-interface/specs-go" oci "github.com/opencontainers/runtime-spec/specs-go" ocigen "github.com/opencontainers/runtime-tools/generate" - - runc "github.com/opencontainers/runc/libcontainer/devices" ) const ( @@ -142,7 +140,7 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error { ensureOCIHooks(spec) spec.Hooks.StartContainer = append(spec.Hooks.StartContainer, h.ToOCI()) default: - return errors.Errorf("unknown hook name %q", h.HookName) + return fmt.Errorf("unknown hook name %q", h.HookName) } } @@ -156,7 +154,7 @@ func (e *ContainerEdits) Validate() error { } if err := ValidateEnv(e.Env); err != nil { - return errors.Wrap(err, "invalid container edits") + return fmt.Errorf("invalid container edits: %w", err) } for _, d := range e.DeviceNodes { if err := (&DeviceNode{d}).Validate(); err != nil { @@ -211,7 +209,7 @@ func (e *ContainerEdits) isEmpty() bool { func ValidateEnv(env []string) error { for _, v := range env { if strings.IndexByte(v, byte('=')) <= 0 { - return errors.Errorf("invalid environment variable %q", v) + return fmt.Errorf("invalid environment variable %q", v) } } return nil @@ -236,11 +234,11 @@ func (d *DeviceNode) Validate() error { return errors.New("invalid (empty) device path") } if _, ok := validTypes[d.Type]; !ok { - return errors.Errorf("device %q: invalid type %q", d.Path, d.Type) + return fmt.Errorf("device %q: invalid type %q", d.Path, d.Type) } for _, bit := range d.Permissions { if bit != 'r' && bit != 'w' && bit != 'm' { - return errors.Errorf("device %q: invalid persmissions %q", + return fmt.Errorf("device %q: invalid persmissions %q", d.Path, d.Permissions) } } @@ -255,13 +253,13 @@ type Hook struct { // Validate a hook. func (h *Hook) Validate() error { if _, ok := validHookNames[h.HookName]; !ok { - return errors.Errorf("invalid hook name %q", h.HookName) + return fmt.Errorf("invalid hook name %q", h.HookName) } if h.Path == "" { - return errors.Errorf("invalid hook %q with empty path", h.HookName) + return fmt.Errorf("invalid hook %q with empty path", h.HookName) } if err := ValidateEnv(h.Env); err != nil { - return errors.Wrapf(err, "invalid hook %q", h.HookName) + return fmt.Errorf("invalid hook %q: %w", h.HookName, err) } return nil } @@ -289,37 +287,6 @@ func ensureOCIHooks(spec *oci.Spec) { } } -// fillMissingInfo fills in missing mandatory attributes from the host device. -func (d *DeviceNode) fillMissingInfo() error { - if d.HostPath == "" { - d.HostPath = d.Path - } - - if d.Type != "" && (d.Major != 0 || d.Type == "p") { - return nil - } - - hostDev, err := runc.DeviceFromPath(d.HostPath, "rwm") - if err != nil { - return errors.Wrapf(err, "failed to stat CDI host device %q", d.HostPath) - } - - if d.Type == "" { - d.Type = string(hostDev.Type) - } else { - if d.Type != string(hostDev.Type) { - return errors.Errorf("CDI device (%q, %q), host type mismatch (%s, %s)", - d.Path, d.HostPath, d.Type, string(hostDev.Type)) - } - } - if d.Major == 0 && d.Type != "p" { - d.Major = hostDev.Major - d.Minor = hostDev.Minor - } - - return nil -} - // sortMounts sorts the mounts in the given OCI Spec. func sortMounts(specgen *ocigen.Generator) { mounts := specgen.Mounts() @@ -331,7 +298,8 @@ func sortMounts(specgen *ocigen.Generator) { // orderedMounts defines how to sort an OCI Spec Mount slice. // This is the almost the same implementation sa used by CRI-O and Docker, // with a minor tweak for stable sorting order (easier to test): -// https://github.com/moby/moby/blob/17.05.x/daemon/volumes.go#L26 +// +// https://github.com/moby/moby/blob/17.05.x/daemon/volumes.go#L26 type orderedMounts []oci.Mount // Len returns the number of mounts. Used in sorting. diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits_unix.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits_unix.go new file mode 100644 index 000000000..11a4cfe8c --- /dev/null +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits_unix.go @@ -0,0 +1,57 @@ +//go:build !windows +// +build !windows + +/* + Copyright © 2021 The CDI 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 cdi + +import ( + "fmt" + + runc "github.com/opencontainers/runc/libcontainer/devices" +) + +// fillMissingInfo fills in missing mandatory attributes from the host device. +func (d *DeviceNode) fillMissingInfo() error { + if d.HostPath == "" { + d.HostPath = d.Path + } + + if d.Type != "" && (d.Major != 0 || d.Type == "p") { + return nil + } + + hostDev, err := runc.DeviceFromPath(d.HostPath, "rwm") + if err != nil { + return fmt.Errorf("failed to stat CDI host device %q: %w", d.HostPath, err) + } + + if d.Type == "" { + d.Type = string(hostDev.Type) + } else { + if d.Type != string(hostDev.Type) { + return fmt.Errorf("CDI device (%q, %q), host type mismatch (%s, %s)", + d.Path, d.HostPath, d.Type, string(hostDev.Type)) + } + } + if d.Major == 0 && d.Type != "p" { + d.Major = hostDev.Major + d.Minor = hostDev.Minor + } + + return nil +} diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits_windows.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits_windows.go new file mode 100644 index 000000000..fd91afa92 --- /dev/null +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/container-edits_windows.go @@ -0,0 +1,27 @@ +//go:build windows +// +build windows + +/* + Copyright © 2021 The CDI 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 cdi + +import "fmt" + +// fillMissingInfo fills in missing mandatory attributes from the host device. +func (d *DeviceNode) fillMissingInfo() error { + return fmt.Errorf("unimplemented") +} diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/device.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/device.go index 0bb1f531b..7b16a3134 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/device.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/device.go @@ -17,9 +17,10 @@ package cdi import ( + "fmt" + cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go" oci "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" ) // Device represents a CDI device of a Spec. @@ -69,10 +70,10 @@ func (d *Device) validate() error { } edits := d.edits() if edits.isEmpty() { - return errors.Errorf("invalid device, empty device edits") + return fmt.Errorf("invalid device, empty device edits") } if err := edits.Validate(); err != nil { - return errors.Wrapf(err, "invalid device %q", d.Name) + return fmt.Errorf("invalid device %q: %w", d.Name, err) } return nil } diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go index 847e51254..ab063a138 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/doc.go @@ -46,7 +46,6 @@ // "fmt" // "strings" // -// "github.com/pkg/errors" // log "github.com/sirupsen/logrus" // // "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" @@ -58,7 +57,7 @@ // // unresolved, err := cdi.GetRegistry().InjectDevices(spec, devices) // if err != nil { -// return errors.Wrap(err, "CDI device injection failed") +// return fmt.Errorf("CDI device injection failed: %w", err) // } // // log.Debug("CDI-updated OCI Spec: %s", dumpSpec(spec)) @@ -90,7 +89,6 @@ // "fmt" // "strings" // -// "github.com/pkg/errors" // log "github.com/sirupsen/logrus" // // "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" @@ -115,7 +113,7 @@ // // unresolved, err := registry.InjectDevices(spec, devices) // if err != nil { -// return errors.Wrap(err, "CDI device injection failed") +// return fmt.Errorf("CDI device injection failed: %w", err) // } // // log.Debug("CDI-updated OCI Spec: %s", dumpSpec(spec)) @@ -124,10 +122,15 @@ // // Generated Spec Files, Multiple Directories, Device Precedence // -// There are systems where the set of available or usable CDI devices -// changes dynamically and this needs to be reflected in the CDI Specs. -// This is done by dynamically regenerating CDI Spec files which are -// affected by these changes. +// It is often necessary to generate Spec files dynamically. On some +// systems the available or usable set of CDI devices might change +// dynamically which then needs to be reflected in CDI Specs. For +// some device classes it makes sense to enumerate the available +// devices at every boot and generate Spec file entries for each +// device found. Some CDI devices might need special client- or +// request-specific configuration which can only be fulfilled by +// dynamically generated client-specific entries in transient Spec +// files. // // CDI can collect Spec files from multiple directories. Spec files are // automatically assigned priorities according to which directory they @@ -141,7 +144,111 @@ // separating dynamically generated CDI Spec files from static ones. // The default directories are '/etc/cdi' and '/var/run/cdi'. By putting // dynamically generated Spec files under '/var/run/cdi', those take -// precedence over static ones in '/etc/cdi'. +// precedence over static ones in '/etc/cdi'. With this scheme, static +// Spec files, typically installed by distro-specific packages, go into +// '/etc/cdi' while all the dynamically generated Spec files, transient +// or other, go into '/var/run/cdi'. +// +// Spec File Generation +// +// CDI offers two functions for writing and removing dynamically generated +// Specs from CDI Spec directories. These functions, WriteSpec() and +// RemoveSpec() implicitly follow the principle of separating dynamic Specs +// from the rest and therefore always write to and remove Specs from the +// last configured directory. +// +// Corresponding functions are also provided for generating names for Spec +// files. These functions follow a simple naming convention to ensure that +// multiple entities generating Spec files simultaneously on the same host +// do not end up using conflicting Spec file names. GenerateSpecName(), +// GenerateNameForSpec(), GenerateTransientSpecName(), and +// GenerateTransientNameForSpec() all generate names which can be passed +// as such to WriteSpec() and subsequently to RemoveSpec(). +// +// Generating a Spec file for a vendor/device class can be done with a +// code snippet similar to the following: +// +// import ( +// "fmt" +// ... +// "github.com/container-orchestrated-devices/container-device-interface/specs-go" +// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" +// ) +// +// func generateDeviceSpecs() error { +// registry := cdi.GetRegistry() +// spec := &specs.Spec{ +// Version: specs.CurrentVersion, +// Kind: vendor+"/"+class, +// } +// +// for _, dev := range enumerateDevices() { +// spec.Devices = append(spec.Devices, specs.Device{ +// Name: dev.Name, +// ContainerEdits: getContainerEditsForDevice(dev), +// }) +// } +// +// specName, err := cdi.GenerateNameForSpec(spec) +// if err != nil { +// return fmt.Errorf("failed to generate Spec name: %w", err) +// } +// +// return registry.SpecDB().WriteSpec(spec, specName) +// } +// +// Similary, generating and later cleaning up transient Spec files can be +// done with code fragments similar to the following. These transient Spec +// files are temporary Spec files with container-specific parametrization. +// They are typically created before the associated container is created +// and removed once that container is removed. +// +// import ( +// "fmt" +// ... +// "github.com/container-orchestrated-devices/container-device-interface/specs-go" +// "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi" +// ) +// +// func generateTransientSpec(ctr Container) error { +// registry := cdi.GetRegistry() +// devices := getContainerDevs(ctr, vendor, class) +// spec := &specs.Spec{ +// Version: specs.CurrentVersion, +// Kind: vendor+"/"+class, +// } +// +// for _, dev := range devices { +// spec.Devices = append(spec.Devices, specs.Device{ +// // the generated name needs to be unique within the +// // vendor/class domain on the host/node. +// Name: generateUniqueDevName(dev, ctr), +// ContainerEdits: getEditsForContainer(dev), +// }) +// } +// +// // transientID is expected to guarantee that the Spec file name +// // generated using is unique within +// // the host/node. If more than one device is allocated with the +// // same vendor/class domain, either all generated Spec entries +// // should go to a single Spec file (like in this sample snippet), +// // or transientID should be unique for each generated Spec file. +// transientID := getSomeSufficientlyUniqueIDForContainer(ctr) +// specName, err := cdi.GenerateNameForTransientSpec(vendor, class, transientID) +// if err != nil { +// return fmt.Errorf("failed to generate Spec name: %w", err) +// } +// +// return registry.SpecDB().WriteSpec(spec, specName) +// } +// +// func removeTransientSpec(ctr Container) error { +// registry := cdi.GetRegistry() +// transientID := getSomeSufficientlyUniqueIDForContainer(ctr) +// specName := cdi.GenerateNameForTransientSpec(vendor, class, transientID) +// +// return registry.SpecDB().RemoveSpec(specName) +// } // // CDI Spec Validation // diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/qualified-device.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/qualified-device.go index ccfab7094..4fe3cfe47 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/qualified-device.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/qualified-device.go @@ -17,9 +17,8 @@ package cdi import ( + "fmt" "strings" - - "github.com/pkg/errors" ) // QualifiedName returns the qualified name for a device. @@ -50,23 +49,23 @@ func ParseQualifiedName(device string) (string, string, string, error) { vendor, class, name := ParseDevice(device) if vendor == "" { - return "", "", device, errors.Errorf("unqualified device %q, missing vendor", device) + return "", "", device, fmt.Errorf("unqualified device %q, missing vendor", device) } if class == "" { - return "", "", device, errors.Errorf("unqualified device %q, missing class", device) + return "", "", device, fmt.Errorf("unqualified device %q, missing class", device) } if name == "" { - return "", "", device, errors.Errorf("unqualified device %q, missing device name", device) + return "", "", device, fmt.Errorf("unqualified device %q, missing device name", device) } if err := ValidateVendorName(vendor); err != nil { - return "", "", device, errors.Wrapf(err, "invalid device %q", device) + return "", "", device, fmt.Errorf("invalid device %q: %w", device, err) } if err := ValidateClassName(class); err != nil { - return "", "", device, errors.Wrapf(err, "invalid device %q", device) + return "", "", device, fmt.Errorf("invalid device %q: %w", device, err) } if err := ValidateDeviceName(name); err != nil { - return "", "", device, errors.Wrapf(err, "invalid device %q", device) + return "", "", device, fmt.Errorf("invalid device %q: %w", device, err) } return vendor, class, name, nil @@ -115,22 +114,22 @@ func ParseQualifier(kind string) (string, string) { // - underscore, dash, and dot ('_', '-', and '.') func ValidateVendorName(vendor string) error { if vendor == "" { - return errors.Errorf("invalid (empty) vendor name") + return fmt.Errorf("invalid (empty) vendor name") } if !isLetter(rune(vendor[0])) { - return errors.Errorf("invalid vendor %q, should start with letter", vendor) + return fmt.Errorf("invalid vendor %q, should start with letter", vendor) } for _, c := range string(vendor[1 : len(vendor)-1]) { switch { case isAlphaNumeric(c): case c == '_' || c == '-' || c == '.': default: - return errors.Errorf("invalid character '%c' in vendor name %q", + return fmt.Errorf("invalid character '%c' in vendor name %q", c, vendor) } } if !isAlphaNumeric(rune(vendor[len(vendor)-1])) { - return errors.Errorf("invalid vendor %q, should end with a letter or digit", vendor) + return fmt.Errorf("invalid vendor %q, should end with a letter or digit", vendor) } return nil @@ -143,22 +142,22 @@ func ValidateVendorName(vendor string) error { // - underscore and dash ('_', '-') func ValidateClassName(class string) error { if class == "" { - return errors.Errorf("invalid (empty) device class") + return fmt.Errorf("invalid (empty) device class") } if !isLetter(rune(class[0])) { - return errors.Errorf("invalid class %q, should start with letter", class) + return fmt.Errorf("invalid class %q, should start with letter", class) } for _, c := range string(class[1 : len(class)-1]) { switch { case isAlphaNumeric(c): case c == '_' || c == '-': default: - return errors.Errorf("invalid character '%c' in device class %q", + return fmt.Errorf("invalid character '%c' in device class %q", c, class) } } if !isAlphaNumeric(rune(class[len(class)-1])) { - return errors.Errorf("invalid class %q, should end with a letter or digit", class) + return fmt.Errorf("invalid class %q, should end with a letter or digit", class) } return nil } @@ -170,10 +169,10 @@ func ValidateClassName(class string) error { // - underscore, dash, dot, colon ('_', '-', '.', ':') func ValidateDeviceName(name string) error { if name == "" { - return errors.Errorf("invalid (empty) device name") + return fmt.Errorf("invalid (empty) device name") } if !isAlphaNumeric(rune(name[0])) { - return errors.Errorf("invalid class %q, should start with a letter or digit", name) + return fmt.Errorf("invalid class %q, should start with a letter or digit", name) } if len(name) == 1 { return nil @@ -183,12 +182,12 @@ func ValidateDeviceName(name string) error { case isAlphaNumeric(c): case c == '_' || c == '-' || c == '.' || c == ':': default: - return errors.Errorf("invalid character '%c' in device name %q", + return fmt.Errorf("invalid character '%c' in device name %q", c, name) } } if !isAlphaNumeric(rune(name[len(name)-1])) { - return errors.Errorf("invalid name %q, should end with a letter or digit", name) + return fmt.Errorf("invalid name %q, should end with a letter or digit", name) } return nil } diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go index d5bd54b0b..10fab8997 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/registry.go @@ -107,6 +107,7 @@ type RegistrySpecDB interface { GetVendorSpecs(vendor string) []*Spec GetSpecErrors(*Spec) []error WriteSpec(raw *cdi.Spec, name string) error + RemoveSpec(name string) error } type registry struct { diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go index 9a5d451c9..fe20e6dd1 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec.go @@ -18,29 +18,28 @@ package cdi import ( "encoding/json" + "fmt" "io/ioutil" "os" "path/filepath" + "strings" + "sync" oci "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" "sigs.k8s.io/yaml" cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go" ) -var ( - // Valid CDI Spec versions. - validSpecVersions = map[string]struct{}{ - "0.1.0": {}, - "0.2.0": {}, - "0.3.0": {}, - "0.4.0": {}, - "0.5.0": {}, - } +const ( + // defaultSpecExt is the file extension for the default encoding. + defaultSpecExt = ".yaml" +) +var ( // Externally set CDI Spec validation function. specValidator func(*cdi.Spec) error + validatorLock sync.RWMutex ) // Spec represents a single CDI Spec. It is usually loaded from a @@ -67,18 +66,18 @@ func ReadSpec(path string, priority int) (*Spec, error) { case os.IsNotExist(err): return nil, err case err != nil: - return nil, errors.Wrapf(err, "failed to read CDI Spec %q", path) + return nil, fmt.Errorf("failed to read CDI Spec %q: %w", path, err) } raw, err := ParseSpec(data) if err != nil { - return nil, errors.Wrapf(err, "failed to parse CDI Spec %q", path) + return nil, fmt.Errorf("failed to parse CDI Spec %q: %w", path, err) } if raw == nil { - return nil, errors.Errorf("failed to parse CDI Spec %q, no Spec data", path) + return nil, fmt.Errorf("failed to parse CDI Spec %q, no Spec data", path) } - spec, err := NewSpec(raw, path, priority) + spec, err := newSpec(raw, path, priority) if err != nil { return nil, err } @@ -86,11 +85,11 @@ func ReadSpec(path string, priority int) (*Spec, error) { return spec, nil } -// NewSpec creates a new Spec from the given CDI Spec data. The +// newSpec creates a new Spec from the given CDI Spec data. The // Spec is marked as loaded from the given path with the given -// priority. If Spec data validation fails NewSpec returns a nil +// priority. If Spec data validation fails newSpec returns a nil // Spec and an error. -func NewSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) { +func newSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) { err := validateSpec(raw) if err != nil { return nil, err @@ -102,18 +101,22 @@ func NewSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) { priority: priority, } + if ext := filepath.Ext(spec.path); ext != ".yaml" && ext != ".json" { + spec.path += defaultSpecExt + } + spec.vendor, spec.class = ParseQualifier(spec.Kind) if spec.devices, err = spec.validate(); err != nil { - return nil, errors.Wrap(err, "invalid CDI Spec") + return nil, fmt.Errorf("invalid CDI Spec: %w", err) } return spec, nil } // Write the CDI Spec to the file associated with it during instantiation -// by NewSpec() or ReadSpec(). -func (s *Spec) Write(overwrite bool) error { +// by newSpec() or ReadSpec(). +func (s *Spec) write(overwrite bool) error { var ( data []byte dir string @@ -132,30 +135,30 @@ func (s *Spec) Write(overwrite bool) error { data, err = json.Marshal(s.Spec) } if err != nil { - return errors.Wrap(err, "failed to marshal Spec file") + return fmt.Errorf("failed to marshal Spec file: %w", err) } dir = filepath.Dir(s.path) err = os.MkdirAll(dir, 0o755) if err != nil { - return errors.Wrap(err, "failed to create Spec dir") + return fmt.Errorf("failed to create Spec dir: %w", err) } tmp, err = os.CreateTemp(dir, "spec.*.tmp") if err != nil { - return errors.Wrap(err, "failed to create Spec file") + return fmt.Errorf("failed to create Spec file: %w", err) } _, err = tmp.Write(data) tmp.Close() if err != nil { - return errors.Wrap(err, "failed to write Spec file") + return fmt.Errorf("failed to write Spec file: %w", err) } err = renameIn(dir, filepath.Base(tmp.Name()), filepath.Base(s.path), overwrite) if err != nil { os.Remove(tmp.Name()) - err = errors.Wrap(err, "failed to write Spec file") + err = fmt.Errorf("failed to write Spec file: %w", err) } return err @@ -201,6 +204,15 @@ func (s *Spec) validate() (map[string]*Device, error) { if err := validateVersion(s.Version); err != nil { return nil, err } + + minVersion, err := MinimumRequiredVersion(s.Spec) + if err != nil { + return nil, fmt.Errorf("could not determine minumum required version: %v", err) + } + if newVersion(minVersion).IsGreaterThan(newVersion(s.Version)) { + return nil, fmt.Errorf("the spec version must be at least v%v", minVersion) + } + if err := ValidateVendorName(s.vendor); err != nil { return nil, err } @@ -215,10 +227,10 @@ func (s *Spec) validate() (map[string]*Device, error) { for _, d := range s.Devices { dev, err := newDevice(s, d) if err != nil { - return nil, errors.Wrapf(err, "failed add device %q", d.Name) + return nil, fmt.Errorf("failed add device %q: %w", d.Name, err) } if _, conflict := devices[d.Name]; conflict { - return nil, errors.Errorf("invalid spec, multiple device %q", d.Name) + return nil, fmt.Errorf("invalid spec, multiple device %q", d.Name) } devices[d.Name] = dev } @@ -228,8 +240,8 @@ func (s *Spec) validate() (map[string]*Device, error) { // validateVersion checks whether the specified spec version is supported. func validateVersion(version string) error { - if _, ok := validSpecVersions[version]; !ok { - return errors.Errorf("invalid version %q", version) + if !validSpecVersions.isValidVersion(version) { + return fmt.Errorf("invalid version %q", version) } return nil @@ -240,26 +252,96 @@ func ParseSpec(data []byte) (*cdi.Spec, error) { var raw *cdi.Spec err := yaml.UnmarshalStrict(data, &raw) if err != nil { - return nil, errors.Wrap(err, "failed to unmarshal CDI Spec") + return nil, fmt.Errorf("failed to unmarshal CDI Spec: %w", err) } return raw, nil } // SetSpecValidator sets a CDI Spec validator function. This function // is used for extra CDI Spec content validation whenever a Spec file -// loaded (using ReadSpec() or NewSpec()) or written (Spec.Write()). +// loaded (using ReadSpec() or written (using WriteSpec()). func SetSpecValidator(fn func(*cdi.Spec) error) { + validatorLock.Lock() + defer validatorLock.Unlock() specValidator = fn } // validateSpec validates the Spec using the extneral validator. func validateSpec(raw *cdi.Spec) error { + validatorLock.RLock() + defer validatorLock.RUnlock() + if specValidator == nil { return nil } err := specValidator(raw) if err != nil { - return errors.Wrap(err, "Spec validation failed") + return fmt.Errorf("Spec validation failed: %w", err) } return nil } + +// GenerateSpecName generates a vendor+class scoped Spec file name. The +// name can be passed to WriteSpec() to write a Spec file to the file +// system. +// +// vendor and class should match the vendor and class of the CDI Spec. +// The file name is generated without a ".json" or ".yaml" extension. +// The caller can append the desired extension to choose a particular +// encoding. Otherwise WriteSpec() will use its default encoding. +// +// This function always returns the same name for the same vendor/class +// combination. Therefore it cannot be used as such to generate multiple +// Spec file names for a single vendor and class. +func GenerateSpecName(vendor, class string) string { + return vendor + "-" + class +} + +// GenerateTransientSpecName generates a vendor+class scoped transient +// Spec file name. The name can be passed to WriteSpec() to write a Spec +// file to the file system. +// +// Transient Specs are those whose lifecycle is tied to that of some +// external entity, for instance a container. vendor and class should +// match the vendor and class of the CDI Spec. transientID should be +// unique among all CDI users on the same host that might generate +// transient Spec files using the same vendor/class combination. If +// the external entity to which the lifecycle of the tranient Spec +// is tied to has a unique ID of its own, then this is usually a +// good choice for transientID. +// +// The file name is generated without a ".json" or ".yaml" extension. +// The caller can append the desired extension to choose a particular +// encoding. Otherwise WriteSpec() will use its default encoding. +func GenerateTransientSpecName(vendor, class, transientID string) string { + transientID = strings.ReplaceAll(transientID, "/", "_") + return GenerateSpecName(vendor, class) + "_" + transientID +} + +// GenerateNameForSpec generates a name for the given Spec using +// GenerateSpecName with the vendor and class taken from the Spec. +// On success it returns the generated name and a nil error. If +// the Spec does not contain a valid vendor or class, it returns +// an empty name and a non-nil error. +func GenerateNameForSpec(raw *cdi.Spec) (string, error) { + vendor, class := ParseQualifier(raw.Kind) + if vendor == "" { + return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind) + } + + return GenerateSpecName(vendor, class), nil +} + +// GenerateNameForTransientSpec generates a name for the given transient +// Spec using GenerateTransientSpecName with the vendor and class taken +// from the Spec. On success it returns the generated name and a nil error. +// If the Spec does not contain a valid vendor or class, it returns an +// an empty name and a non-nil error. +func GenerateNameForTransientSpec(raw *cdi.Spec, transientID string) (string, error) { + vendor, class := ParseQualifier(raw.Kind) + if vendor == "" { + return "", fmt.Errorf("invalid vendor/class %q in Spec", raw.Kind) + } + + return GenerateTransientSpecName(vendor, class, transientID), nil +} diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_linux.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_linux.go index cca825c60..9ad273925 100644 --- a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_linux.go +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_linux.go @@ -17,9 +17,9 @@ package cdi import ( + "fmt" "os" - "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -30,7 +30,7 @@ func renameIn(dir, src, dst string, overwrite bool) error { dirf, err := os.Open(dir) if err != nil { - return errors.Wrap(err, "rename failed") + return fmt.Errorf("rename failed: %w", err) } defer dirf.Close() @@ -41,7 +41,7 @@ func renameIn(dir, src, dst string, overwrite bool) error { dirFd := int(dirf.Fd()) err = unix.Renameat2(dirFd, src, dirFd, dst, flags) if err != nil { - return errors.Wrap(err, "rename failed") + return fmt.Errorf("rename failed: %w", err) } return nil diff --git a/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/version.go b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/version.go new file mode 100644 index 000000000..08b36a320 --- /dev/null +++ b/vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/version.go @@ -0,0 +1,160 @@ +/* + Copyright © The CDI 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 cdi + +import ( + "strings" + + "golang.org/x/mod/semver" + + cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go" +) + +const ( + // CurrentVersion is the current version of the CDI Spec. + CurrentVersion = cdi.CurrentVersion + + // vCurrent is the current version as a semver-comparable type + vCurrent version = "v" + CurrentVersion + + // These represent the released versions of the CDI specification + v010 version = "v0.1.0" + v020 version = "v0.2.0" + v030 version = "v0.3.0" + v040 version = "v0.4.0" + v050 version = "v0.5.0" + + // vEarliest is the earliest supported version of the CDI specification + vEarliest version = v030 +) + +// validSpecVersions stores a map of spec versions to functions to check the required versions. +// Adding new fields / spec versions requires that a `requiredFunc` be implemented and +// this map be updated. +var validSpecVersions = requiredVersionMap{ + v010: nil, + v020: nil, + v030: nil, + v040: requiresV040, + v050: requiresV050, +} + +// MinimumRequiredVersion determines the minumum spec version for the input spec. +func MinimumRequiredVersion(spec *cdi.Spec) (string, error) { + minVersion := validSpecVersions.requiredVersion(spec) + return minVersion.String(), nil +} + +// version represents a semantic version string +type version string + +// newVersion creates a version that can be used for semantic version comparisons. +func newVersion(v string) version { + return version("v" + strings.TrimPrefix(v, "v")) +} + +// String returns the string representation of the version. +// This trims a leading v if present. +func (v version) String() string { + return strings.TrimPrefix(string(v), "v") +} + +// IsGreaterThan checks with a version is greater than the specified version. +func (v version) IsGreaterThan(o version) bool { + return semver.Compare(string(v), string(o)) > 0 +} + +// IsLatest checks whether the version is the latest supported version +func (v version) IsLatest() bool { + return v == vCurrent +} + +type requiredFunc func(*cdi.Spec) bool + +type requiredVersionMap map[version]requiredFunc + +// isValidVersion checks whether the specified version is valid. +// A version is valid if it is contained in the required version map. +func (r requiredVersionMap) isValidVersion(specVersion string) bool { + _, ok := validSpecVersions[newVersion(specVersion)] + + return ok +} + +// requiredVersion returns the minimum version required for the given spec +func (r requiredVersionMap) requiredVersion(spec *cdi.Spec) version { + minVersion := vEarliest + + for v, isRequired := range validSpecVersions { + if isRequired == nil { + continue + } + if isRequired(spec) && v.IsGreaterThan(minVersion) { + minVersion = v + } + // If we have already detected the latest version then no later version could be detected + if minVersion.IsLatest() { + break + } + } + + return minVersion +} + +// requiresV050 returns true if the spec uses v0.5.0 features +func requiresV050(spec *cdi.Spec) bool { + var edits []*cdi.ContainerEdits + + for _, d := range spec.Devices { + // The v0.5.0 spec allowed device names to start with a digit instead of requiring a letter + if len(d.Name) > 0 && !isLetter(rune(d.Name[0])) { + return true + } + edits = append(edits, &d.ContainerEdits) + } + + edits = append(edits, &spec.ContainerEdits) + for _, e := range edits { + for _, dn := range e.DeviceNodes { + // The HostPath field was added in v0.5.0 + if dn.HostPath != "" { + return true + } + } + } + return false +} + +// requiresV040 returns true if the spec uses v0.4.0 features +func requiresV040(spec *cdi.Spec) bool { + var edits []*cdi.ContainerEdits + + for _, d := range spec.Devices { + edits = append(edits, &d.ContainerEdits) + } + + edits = append(edits, &spec.ContainerEdits) + for _, e := range edits { + for _, m := range e.Mounts { + // The Type field was added in v0.4.0 + if m.Type != "" { + return true + } + } + } + return false +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 470a7769c..d626f8c16 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -76,8 +76,9 @@ github.com/cilium/ebpf/internal github.com/cilium/ebpf/internal/sys github.com/cilium/ebpf/internal/unix github.com/cilium/ebpf/link -# github.com/container-orchestrated-devices/container-device-interface v0.5.1 +# github.com/container-orchestrated-devices/container-device-interface v0.5.4 ## explicit; go 1.17 +github.com/container-orchestrated-devices/container-device-interface/internal/multierror github.com/container-orchestrated-devices/container-device-interface/pkg/cdi github.com/container-orchestrated-devices/container-device-interface/specs-go # github.com/containerd/aufs v1.0.0 @@ -348,7 +349,7 @@ github.com/opencontainers/runc/libcontainer/user # github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb ## explicit github.com/opencontainers/runtime-spec/specs-go -# github.com/opencontainers/runtime-tools v0.9.0 => github.com/opencontainers/runtime-tools v0.0.0-20221026201742-946c877fa809 +# github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 => github.com/opencontainers/runtime-tools v0.0.0-20221026201742-946c877fa809 ## explicit; go 1.16 github.com/opencontainers/runtime-tools/generate github.com/opencontainers/runtime-tools/generate/seccomp