Merge pull request #7374 from soulseen/update-cdi-version
This commit is contained in:
commit
99ee82d0b6
2
go.mod
2
go.mod
@ -7,7 +7,7 @@ require (
|
|||||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20220723121359-e03a0069662f
|
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20220723121359-e03a0069662f
|
||||||
github.com/Microsoft/go-winio v0.5.2
|
github.com/Microsoft/go-winio v0.5.2
|
||||||
github.com/Microsoft/hcsshim v0.10.0-rc.1
|
github.com/Microsoft/hcsshim v0.10.0-rc.1
|
||||||
github.com/container-orchestrated-devices/container-device-interface v0.3.1
|
github.com/container-orchestrated-devices/container-device-interface v0.5.1
|
||||||
github.com/containerd/aufs v1.0.0
|
github.com/containerd/aufs v1.0.0
|
||||||
github.com/containerd/btrfs v1.0.0
|
github.com/containerd/btrfs v1.0.0
|
||||||
github.com/containerd/cgroups v1.0.5-0.20220816231112-7083cd60b721
|
github.com/containerd/cgroups v1.0.5-0.20220816231112-7083cd60b721
|
||||||
|
4
go.sum
4
go.sum
@ -178,8 +178,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/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/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/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
||||||
github.com/container-orchestrated-devices/container-device-interface v0.3.1 h1:AvASKHHm6w3qMU49iPYyp8GhwbacvqjfGHUZEgvA/mQ=
|
github.com/container-orchestrated-devices/container-device-interface v0.5.1 h1:nXIUTrlEgGcA/n2geY3J7yyaGGhkocSlMkKPS4Qp4c0=
|
||||||
github.com/container-orchestrated-devices/container-device-interface v0.3.1/go.mod h1:E1zcucIkq9P3eyNmY+68dBQsTcsXJh9cgRo2IVNScKQ=
|
github.com/container-orchestrated-devices/container-device-interface v0.5.1/go.mod h1:ZToWfSyUH5l9Rk7/bjkUUkNLz4b1mE+CVUVafuikDPY=
|
||||||
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
|
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-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
|
||||||
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
|
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
|
||||||
|
@ -26,7 +26,7 @@ require (
|
|||||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||||
github.com/cilium/ebpf v0.9.1 // indirect
|
github.com/cilium/ebpf v0.9.1 // indirect
|
||||||
github.com/container-orchestrated-devices/container-device-interface v0.3.1 // indirect
|
github.com/container-orchestrated-devices/container-device-interface v0.5.1 // indirect
|
||||||
github.com/containerd/console v1.0.3 // indirect
|
github.com/containerd/console v1.0.3 // indirect
|
||||||
github.com/containerd/continuity v0.3.0 // indirect
|
github.com/containerd/continuity v0.3.0 // indirect
|
||||||
github.com/containerd/fifo v1.0.0 // indirect
|
github.com/containerd/fifo v1.0.0 // indirect
|
||||||
@ -35,6 +35,7 @@ require (
|
|||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.0.6 // indirect
|
github.com/godbus/dbus/v5 v5.0.6 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
|
@ -182,8 +182,8 @@ github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoC
|
|||||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
||||||
github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
|
github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA=
|
||||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
|
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
|
||||||
github.com/container-orchestrated-devices/container-device-interface v0.3.1 h1:AvASKHHm6w3qMU49iPYyp8GhwbacvqjfGHUZEgvA/mQ=
|
github.com/container-orchestrated-devices/container-device-interface v0.5.1 h1:nXIUTrlEgGcA/n2geY3J7yyaGGhkocSlMkKPS4Qp4c0=
|
||||||
github.com/container-orchestrated-devices/container-device-interface v0.3.1/go.mod h1:E1zcucIkq9P3eyNmY+68dBQsTcsXJh9cgRo2IVNScKQ=
|
github.com/container-orchestrated-devices/container-device-interface v0.5.1/go.mod h1:ZToWfSyUH5l9Rk7/bjkUUkNLz4b1mE+CVUVafuikDPY=
|
||||||
github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
|
github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
|
||||||
github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
|
github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
|
||||||
github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
|
github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
|
||||||
@ -309,6 +309,7 @@ github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzP
|
|||||||
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
|
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||||
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
|
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
|
||||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||||
@ -722,6 +723,7 @@ github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1 h1:9iF
|
|||||||
github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1/go.mod h1:K/JAU0m27RFhDRX4PcFdIKntROP6y5Ed6O91aZYDQfs=
|
github.com/opencontainers/image-spec v1.0.3-0.20220303224323-02efb9a75ee1/go.mod h1:K/JAU0m27RFhDRX4PcFdIKntROP6y5Ed6O91aZYDQfs=
|
||||||
github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
|
github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
|
||||||
github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
|
github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
|
||||||
|
github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
|
||||||
github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg=
|
github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg=
|
||||||
github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
|
github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
|
||||||
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
@ -733,7 +735,6 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo
|
|||||||
github.com/opencontainers/runtime-tools v0.0.0-20190417131837-cd1349b7c47e h1:2Tg49TNXSTIsX8AAtmo1aQ1IbfnoUFzkOp7p2iWygtc=
|
github.com/opencontainers/runtime-tools v0.0.0-20190417131837-cd1349b7c47e h1:2Tg49TNXSTIsX8AAtmo1aQ1IbfnoUFzkOp7p2iWygtc=
|
||||||
github.com/opencontainers/runtime-tools v0.0.0-20190417131837-cd1349b7c47e/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
github.com/opencontainers/runtime-tools v0.0.0-20190417131837-cd1349b7c47e/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||||
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
|
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
|
||||||
github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
|
||||||
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||||
github.com/opencontainers/selinux v1.10.1 h1:09LIPVRP3uuZGQvgR+SgMSNBd1Eb3vlRbGqQpoHsF8w=
|
github.com/opencontainers/selinux v1.10.1 h1:09LIPVRP3uuZGQvgR+SgMSNBd1Eb3vlRbGqQpoHsF8w=
|
||||||
github.com/opencontainers/selinux v1.10.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
github.com/opencontainers/selinux v1.10.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||||
@ -815,6 +816,7 @@ github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZ
|
|||||||
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
|
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||||
|
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
284
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
generated
vendored
284
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/cache.go
generated
vendored
@ -22,6 +22,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
oci "github.com/opencontainers/runtime-spec/specs-go"
|
oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -37,26 +39,42 @@ type Cache struct {
|
|||||||
specs map[string][]*Spec
|
specs map[string][]*Spec
|
||||||
devices map[string]*Device
|
devices map[string]*Device
|
||||||
errors map[string][]error
|
errors map[string][]error
|
||||||
|
dirErrors map[string]error
|
||||||
|
|
||||||
|
autoRefresh bool
|
||||||
|
watch *watch
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAutoRefresh returns an option to control automatic Cache refresh.
|
||||||
|
// By default auto-refresh is enabled, the list of Spec directories are
|
||||||
|
// monitored and the Cache is automatically refreshed whenever a change
|
||||||
|
// is detected. This option can be used to disable this behavior when a
|
||||||
|
// manually refreshed mode is preferable.
|
||||||
|
func WithAutoRefresh(autoRefresh bool) Option {
|
||||||
|
return func(c *Cache) error {
|
||||||
|
c.autoRefresh = autoRefresh
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCache creates a new CDI Cache. The cache is populated from a set
|
// NewCache creates a new CDI Cache. The cache is populated from a set
|
||||||
// of CDI Spec directories. These can be specified using a WithSpecDirs
|
// of CDI Spec directories. These can be specified using a WithSpecDirs
|
||||||
// option. The default set of directories is exposed in DefaultSpecDirs.
|
// option. The default set of directories is exposed in DefaultSpecDirs.
|
||||||
func NewCache(options ...Option) (*Cache, error) {
|
func NewCache(options ...Option) (*Cache, error) {
|
||||||
c := &Cache{}
|
c := &Cache{
|
||||||
|
autoRefresh: true,
|
||||||
if err := c.Configure(options...); err != nil {
|
watch: &watch{},
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(c.specDirs) == 0 {
|
|
||||||
c.Configure(WithSpecDirs(DefaultSpecDirs...))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, c.Refresh()
|
WithSpecDirs(DefaultSpecDirs...)(c)
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
|
return c, c.configure(options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure applies options to the cache. Updates the cache if options have
|
// Configure applies options to the Cache. Updates and refreshes the
|
||||||
// changed.
|
// Cache if options have changed.
|
||||||
func (c *Cache) Configure(options ...Option) error {
|
func (c *Cache) Configure(options ...Option) error {
|
||||||
if len(options) == 0 {
|
if len(options) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@ -65,17 +83,54 @@ func (c *Cache) Configure(options ...Option) error {
|
|||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
return c.configure(options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the Cache. Start/stop CDI Spec directory watch, refresh
|
||||||
|
// the Cache if necessary.
|
||||||
|
func (c *Cache) configure(options ...Option) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
for _, o := range options {
|
for _, o := range options {
|
||||||
if err := o(c); err != nil {
|
if err = o(c); err != nil {
|
||||||
return errors.Wrapf(err, "failed to apply cache options")
|
return errors.Wrapf(err, "failed to apply cache options")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.dirErrors = make(map[string]error)
|
||||||
|
|
||||||
|
c.watch.stop()
|
||||||
|
if c.autoRefresh {
|
||||||
|
c.watch.setup(c.specDirs, c.dirErrors)
|
||||||
|
c.watch.start(&c.Mutex, c.refresh, c.dirErrors)
|
||||||
|
}
|
||||||
|
c.refresh()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh rescans the CDI Spec directories and refreshes the Cache.
|
// Refresh rescans the CDI Spec directories and refreshes the Cache.
|
||||||
|
// In manual refresh mode the cache is always refreshed. In auto-
|
||||||
|
// refresh mode the cache is only refreshed if it is out of date.
|
||||||
func (c *Cache) Refresh() error {
|
func (c *Cache) Refresh() error {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
|
// force a refresh in manual mode
|
||||||
|
if refreshed, err := c.refreshIfRequired(!c.autoRefresh); refreshed {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect and return cached errors, much like refresh() does it
|
||||||
|
var result error
|
||||||
|
for _, err := range c.errors {
|
||||||
|
result = multierror.Append(result, err...)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh the Cache by rescanning CDI Spec directories and files.
|
||||||
|
func (c *Cache) refresh() error {
|
||||||
var (
|
var (
|
||||||
specs = map[string][]*Spec{}
|
specs = map[string][]*Spec{}
|
||||||
devices = map[string]*Device{}
|
devices = map[string]*Device{}
|
||||||
@ -135,9 +190,6 @@ func (c *Cache) Refresh() error {
|
|||||||
delete(devices, conflict)
|
delete(devices, conflict)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Lock()
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
c.specs = specs
|
c.specs = specs
|
||||||
c.devices = devices
|
c.devices = devices
|
||||||
c.errors = specErrors
|
c.errors = specErrors
|
||||||
@ -149,6 +201,17 @@ func (c *Cache) Refresh() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefreshIfRequired triggers a refresh if necessary.
|
||||||
|
func (c *Cache) refreshIfRequired(force bool) (bool, error) {
|
||||||
|
// We need to refresh if
|
||||||
|
// - it's forced by an explicitly call to Refresh() in manual mode
|
||||||
|
// - a missing Spec dir appears (added to watch) in auto-refresh mode
|
||||||
|
if force || (c.autoRefresh && c.watch.update(c.dirErrors)) {
|
||||||
|
return true, c.refresh()
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// InjectDevices injects the given qualified devices to an OCI Spec. It
|
// InjectDevices injects the given qualified devices to an OCI Spec. It
|
||||||
// returns any unresolvable devices and an error if injection fails for
|
// returns any unresolvable devices and an error if injection fails for
|
||||||
// any of the devices.
|
// any of the devices.
|
||||||
@ -162,6 +225,8 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e
|
|||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
c.refreshIfRequired(false)
|
||||||
|
|
||||||
edits := &ContainerEdits{}
|
edits := &ContainerEdits{}
|
||||||
specs := map[*Spec]struct{}{}
|
specs := map[*Spec]struct{}{}
|
||||||
|
|
||||||
@ -190,11 +255,46 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e
|
|||||||
return nil, nil
|
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.
|
||||||
|
func (c *Cache) WriteSpec(raw *cdi.Spec, name string) error {
|
||||||
|
var (
|
||||||
|
specDir string
|
||||||
|
path string
|
||||||
|
prio int
|
||||||
|
spec *Spec
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(c.specDirs) == 0 {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
spec, err = NewSpec(raw, path, prio)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return spec.Write(true)
|
||||||
|
}
|
||||||
|
|
||||||
// GetDevice returns the cached device for the given qualified name.
|
// GetDevice returns the cached device for the given qualified name.
|
||||||
func (c *Cache) GetDevice(device string) *Device {
|
func (c *Cache) GetDevice(device string) *Device {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
c.refreshIfRequired(false)
|
||||||
|
|
||||||
return c.devices[device]
|
return c.devices[device]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,6 +305,8 @@ func (c *Cache) ListDevices() []string {
|
|||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
c.refreshIfRequired(false)
|
||||||
|
|
||||||
for name := range c.devices {
|
for name := range c.devices {
|
||||||
devices = append(devices, name)
|
devices = append(devices, name)
|
||||||
}
|
}
|
||||||
@ -220,6 +322,8 @@ func (c *Cache) ListVendors() []string {
|
|||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
c.refreshIfRequired(false)
|
||||||
|
|
||||||
for vendor := range c.specs {
|
for vendor := range c.specs {
|
||||||
vendors = append(vendors, vendor)
|
vendors = append(vendors, vendor)
|
||||||
}
|
}
|
||||||
@ -238,6 +342,8 @@ func (c *Cache) ListClasses() []string {
|
|||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
c.refreshIfRequired(false)
|
||||||
|
|
||||||
for _, specs := range c.specs {
|
for _, specs := range c.specs {
|
||||||
for _, spec := range specs {
|
for _, spec := range specs {
|
||||||
cmap[spec.GetClass()] = struct{}{}
|
cmap[spec.GetClass()] = struct{}{}
|
||||||
@ -256,6 +362,8 @@ func (c *Cache) GetVendorSpecs(vendor string) []*Spec {
|
|||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
|
||||||
|
c.refreshIfRequired(false)
|
||||||
|
|
||||||
return c.specs[vendor]
|
return c.specs[vendor]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,12 +376,158 @@ func (c *Cache) GetSpecErrors(spec *Spec) []error {
|
|||||||
// GetErrors returns all errors encountered during the last
|
// GetErrors returns all errors encountered during the last
|
||||||
// cache refresh.
|
// cache refresh.
|
||||||
func (c *Cache) GetErrors() map[string][]error {
|
func (c *Cache) GetErrors() map[string][]error {
|
||||||
return c.errors
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
|
errors := map[string][]error{}
|
||||||
|
for path, errs := range c.errors {
|
||||||
|
errors[path] = errs
|
||||||
|
}
|
||||||
|
for path, err := range c.dirErrors {
|
||||||
|
errors[path] = []error{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSpecDirectories returns the CDI Spec directories currently in use.
|
// GetSpecDirectories returns the CDI Spec directories currently in use.
|
||||||
func (c *Cache) GetSpecDirectories() []string {
|
func (c *Cache) GetSpecDirectories() []string {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
dirs := make([]string, len(c.specDirs))
|
dirs := make([]string, len(c.specDirs))
|
||||||
copy(dirs, c.specDirs)
|
copy(dirs, c.specDirs)
|
||||||
return dirs
|
return dirs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSpecDirErrors returns any errors related to configured Spec directories.
|
||||||
|
func (c *Cache) GetSpecDirErrors() map[string]error {
|
||||||
|
if c.dirErrors == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
|
errors := make(map[string]error)
|
||||||
|
for dir, err := range c.dirErrors {
|
||||||
|
errors[dir] = err
|
||||||
|
}
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our fsnotify helper wrapper.
|
||||||
|
type watch struct {
|
||||||
|
watcher *fsnotify.Watcher
|
||||||
|
tracked map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup monitoring for the given Spec directories.
|
||||||
|
func (w *watch) setup(dirs []string, dirErrors map[string]error) {
|
||||||
|
var (
|
||||||
|
dir string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
w.tracked = make(map[string]bool)
|
||||||
|
for _, dir = range dirs {
|
||||||
|
w.tracked[dir] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
w.watcher, err = fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
for _, dir := range dirs {
|
||||||
|
dirErrors[dir] = errors.Wrap(err, "failed to create watcher")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.update(dirErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start watching Spec directories for relevant changes.
|
||||||
|
func (w *watch) start(m *sync.Mutex, refresh func() error, dirErrors map[string]error) {
|
||||||
|
go w.watch(w.watcher, m, refresh, dirErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop watching directories.
|
||||||
|
func (w *watch) stop() {
|
||||||
|
if w.watcher == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.watcher.Close()
|
||||||
|
w.tracked = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch Spec directory changes, triggering a refresh if necessary.
|
||||||
|
func (w *watch) watch(fsw *fsnotify.Watcher, m *sync.Mutex, refresh func() error, dirErrors map[string]error) {
|
||||||
|
watch := fsw
|
||||||
|
if watch == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event, ok := <-watch.Events:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.Op & (fsnotify.Rename | fsnotify.Remove | fsnotify.Write)) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if event.Op == fsnotify.Write {
|
||||||
|
if ext := filepath.Ext(event.Name); ext != ".json" && ext != ".yaml" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Lock()
|
||||||
|
if event.Op == fsnotify.Remove && w.tracked[event.Name] {
|
||||||
|
w.update(dirErrors, event.Name)
|
||||||
|
} else {
|
||||||
|
w.update(dirErrors)
|
||||||
|
}
|
||||||
|
refresh()
|
||||||
|
m.Unlock()
|
||||||
|
|
||||||
|
case _, ok := <-watch.Errors:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update watch with pending/missing or removed directories.
|
||||||
|
func (w *watch) update(dirErrors map[string]error, removed ...string) bool {
|
||||||
|
var (
|
||||||
|
dir string
|
||||||
|
ok bool
|
||||||
|
err error
|
||||||
|
update bool
|
||||||
|
)
|
||||||
|
|
||||||
|
for dir, ok = range w.tracked {
|
||||||
|
if ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = w.watcher.Add(dir)
|
||||||
|
if err == nil {
|
||||||
|
w.tracked[dir] = true
|
||||||
|
delete(dirErrors, dir)
|
||||||
|
update = true
|
||||||
|
} else {
|
||||||
|
w.tracked[dir] = false
|
||||||
|
dirErrors[dir] = errors.Wrap(err, "failed to monitor for changes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dir = range removed {
|
||||||
|
w.tracked[dir] = false
|
||||||
|
dirErrors[dir] = errors.New("directory removed")
|
||||||
|
update = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return update
|
||||||
|
}
|
||||||
|
@ -85,11 +85,13 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range e.DeviceNodes {
|
for _, d := range e.DeviceNodes {
|
||||||
dev := d.ToOCI()
|
dn := DeviceNode{d}
|
||||||
if err := fillMissingInfo(&dev); err != nil {
|
|
||||||
|
err := dn.fillMissingInfo()
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
dev := d.ToOCI()
|
||||||
if dev.UID == nil && spec.Process != nil {
|
if dev.UID == nil && spec.Process != nil {
|
||||||
if uid := spec.Process.User.UID; uid > 0 {
|
if uid := spec.Process.User.UID; uid > 0 {
|
||||||
dev.UID = &uid
|
dev.UID = &uid
|
||||||
@ -288,26 +290,31 @@ func ensureOCIHooks(spec *oci.Spec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fillMissingInfo fills in missing mandatory attributes from the host device.
|
// fillMissingInfo fills in missing mandatory attributes from the host device.
|
||||||
func fillMissingInfo(dev *oci.LinuxDevice) error {
|
func (d *DeviceNode) fillMissingInfo() error {
|
||||||
if dev.Type != "" && (dev.Major != 0 || dev.Type == "p") {
|
if d.HostPath == "" {
|
||||||
return nil
|
d.HostPath = d.Path
|
||||||
}
|
|
||||||
hostDev, err := runc.DeviceFromPath(dev.Path, "rwm")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to stat CDI host device %q", dev.Path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if dev.Type == "" {
|
if d.Type != "" && (d.Major != 0 || d.Type == "p") {
|
||||||
dev.Type = string(hostDev.Type)
|
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 {
|
} else {
|
||||||
if dev.Type != string(hostDev.Type) {
|
if d.Type != string(hostDev.Type) {
|
||||||
return errors.Errorf("CDI device %q, host type mismatch (%s, %s)",
|
return errors.Errorf("CDI device (%q, %q), host type mismatch (%s, %s)",
|
||||||
dev.Path, dev.Type, string(hostDev.Type))
|
d.Path, d.HostPath, d.Type, string(hostDev.Type))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if dev.Major == 0 && dev.Type != "p" {
|
if d.Major == 0 && d.Type != "p" {
|
||||||
dev.Major = hostDev.Major
|
d.Major = hostDev.Major
|
||||||
dev.Minor = hostDev.Minor
|
d.Minor = hostDev.Minor
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -67,6 +67,21 @@
|
|||||||
//
|
//
|
||||||
// Cache Refresh
|
// Cache Refresh
|
||||||
//
|
//
|
||||||
|
// By default the CDI Spec cache monitors the configured Spec directories
|
||||||
|
// and automatically refreshes itself when necessary. This behavior can be
|
||||||
|
// disabled using the WithAutoRefresh(false) option.
|
||||||
|
//
|
||||||
|
// Failure to set up monitoring for a Spec directory causes the directory to
|
||||||
|
// get ignored and an error to be recorded among the Spec directory errors.
|
||||||
|
// These errors can be queried using the GetSpecDirErrors() function. If the
|
||||||
|
// error condition is transient, for instance a missing directory which later
|
||||||
|
// gets created, the corresponding error will be removed once the condition
|
||||||
|
// is over.
|
||||||
|
//
|
||||||
|
// With auto-refresh enabled injecting any CDI devices can be done without
|
||||||
|
// an explicit call to Refresh(), using a code snippet similar to the
|
||||||
|
// following:
|
||||||
|
//
|
||||||
// In a runtime implementation one typically wants to make sure the
|
// In a runtime implementation one typically wants to make sure the
|
||||||
// CDI Spec cache is up to date before performing device injection.
|
// CDI Spec cache is up to date before performing device injection.
|
||||||
// A code snippet similar to the following accmplishes that:
|
// A code snippet similar to the following accmplishes that:
|
||||||
@ -146,5 +161,5 @@
|
|||||||
// schema names which switch the used schema to the in-repo validation
|
// schema names which switch the used schema to the in-repo validation
|
||||||
// schema embedded into the binary or the now default no-op schema
|
// schema embedded into the binary or the now default no-op schema
|
||||||
// correspondingly. Other names are interpreted as the path to the actual
|
// correspondingly. Other names are interpreted as the path to the actual
|
||||||
/// validation schema to load and use.
|
// validation schema to load and use.
|
||||||
package cdi
|
package cdi
|
||||||
|
@ -130,7 +130,7 @@ func ValidateVendorName(vendor string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isAlphaNumeric(rune(vendor[len(vendor)-1])) {
|
if !isAlphaNumeric(rune(vendor[len(vendor)-1])) {
|
||||||
return errors.Errorf("invalid vendor %q, should end with letter", vendor)
|
return errors.Errorf("invalid vendor %q, should end with a letter or digit", vendor)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -158,7 +158,7 @@ func ValidateClassName(class string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isAlphaNumeric(rune(class[len(class)-1])) {
|
if !isAlphaNumeric(rune(class[len(class)-1])) {
|
||||||
return errors.Errorf("invalid class %q, should end with letter", class)
|
return errors.Errorf("invalid class %q, should end with a letter or digit", class)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -172,8 +172,11 @@ func ValidateDeviceName(name string) error {
|
|||||||
if name == "" {
|
if name == "" {
|
||||||
return errors.Errorf("invalid (empty) device name")
|
return errors.Errorf("invalid (empty) device name")
|
||||||
}
|
}
|
||||||
if !isLetter(rune(name[0])) {
|
if !isAlphaNumeric(rune(name[0])) {
|
||||||
return errors.Errorf("invalid name %q, should start with letter", name)
|
return errors.Errorf("invalid class %q, should start with a letter or digit", name)
|
||||||
|
}
|
||||||
|
if len(name) == 1 {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
for _, c := range string(name[1 : len(name)-1]) {
|
for _, c := range string(name[1 : len(name)-1]) {
|
||||||
switch {
|
switch {
|
||||||
@ -185,7 +188,7 @@ func ValidateDeviceName(name string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isAlphaNumeric(rune(name[len(name)-1])) {
|
if !isAlphaNumeric(rune(name[len(name)-1])) {
|
||||||
return errors.Errorf("invalid name %q, should start with letter", name)
|
return errors.Errorf("invalid name %q, should end with a letter or digit", name)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ package cdi
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||||
oci "github.com/opencontainers/runtime-spec/specs-go"
|
oci "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,6 +41,8 @@ type Registry interface {
|
|||||||
// RegistryRefresher is the registry interface for refreshing the
|
// RegistryRefresher is the registry interface for refreshing the
|
||||||
// cache of CDI Specs and devices.
|
// cache of CDI Specs and devices.
|
||||||
//
|
//
|
||||||
|
// Configure reconfigures the registry with the given options.
|
||||||
|
//
|
||||||
// Refresh rescans all CDI Spec directories and updates the
|
// Refresh rescans all CDI Spec directories and updates the
|
||||||
// state of the cache to reflect any changes. It returns any
|
// state of the cache to reflect any changes. It returns any
|
||||||
// errors encountered during the refresh.
|
// errors encountered during the refresh.
|
||||||
@ -50,10 +53,15 @@ type Registry interface {
|
|||||||
// GetSpecDirectories returns the set up CDI Spec directories
|
// GetSpecDirectories returns the set up CDI Spec directories
|
||||||
// currently in use. The directories are returned in the scan
|
// currently in use. The directories are returned in the scan
|
||||||
// order of Refresh().
|
// order of Refresh().
|
||||||
|
//
|
||||||
|
// GetSpecDirErrors returns any errors related to the configured
|
||||||
|
// Spec directories.
|
||||||
type RegistryRefresher interface {
|
type RegistryRefresher interface {
|
||||||
|
Configure(...Option) error
|
||||||
Refresh() error
|
Refresh() error
|
||||||
GetErrors() map[string][]error
|
GetErrors() map[string][]error
|
||||||
GetSpecDirectories() []string
|
GetSpecDirectories() []string
|
||||||
|
GetSpecDirErrors() map[string]error
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegistryResolver is the registry interface for injecting CDI
|
// RegistryResolver is the registry interface for injecting CDI
|
||||||
@ -90,11 +98,15 @@ type RegistryDeviceDB interface {
|
|||||||
//
|
//
|
||||||
// GetSpecErrors returns any errors for the Spec encountered during
|
// GetSpecErrors returns any errors for the Spec encountered during
|
||||||
// the last cache refresh.
|
// the last cache refresh.
|
||||||
|
//
|
||||||
|
// WriteSpec writes the Spec with the given content and name to the
|
||||||
|
// last Spec directory.
|
||||||
type RegistrySpecDB interface {
|
type RegistrySpecDB interface {
|
||||||
ListVendors() []string
|
ListVendors() []string
|
||||||
ListClasses() []string
|
ListClasses() []string
|
||||||
GetVendorSpecs(vendor string) []*Spec
|
GetVendorSpecs(vendor string) []*Spec
|
||||||
GetSpecErrors(*Spec) []error
|
GetSpecErrors(*Spec) []error
|
||||||
|
WriteSpec(raw *cdi.Spec, name string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type registry struct {
|
type registry struct {
|
||||||
|
@ -17,10 +17,10 @@
|
|||||||
package cdi
|
package cdi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -45,10 +45,11 @@ var (
|
|||||||
// WithSpecDirs returns an option to override the CDI Spec directories.
|
// WithSpecDirs returns an option to override the CDI Spec directories.
|
||||||
func WithSpecDirs(dirs ...string) Option {
|
func WithSpecDirs(dirs ...string) Option {
|
||||||
return func(c *Cache) error {
|
return func(c *Cache) error {
|
||||||
c.specDirs = make([]string, len(dirs))
|
specDirs := make([]string, len(dirs))
|
||||||
for i, dir := range dirs {
|
for i, dir := range dirs {
|
||||||
c.specDirs[i] = filepath.Clean(dir)
|
specDirs[i] = filepath.Clean(dir)
|
||||||
}
|
}
|
||||||
|
c.specDirs = specDirs
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,6 +79,9 @@ func scanSpecDirs(dirs []string, scanFn scanSpecFunc) error {
|
|||||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
// for initial stat failure Walk calls us with nil info
|
// for initial stat failure Walk calls us with nil info
|
||||||
if info == nil {
|
if info == nil {
|
||||||
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// first call from Walk is for dir itself, others we skip
|
// first call from Walk is for dir itself, others we skip
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package cdi
|
package cdi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -34,7 +35,12 @@ var (
|
|||||||
"0.1.0": {},
|
"0.1.0": {},
|
||||||
"0.2.0": {},
|
"0.2.0": {},
|
||||||
"0.3.0": {},
|
"0.3.0": {},
|
||||||
|
"0.4.0": {},
|
||||||
|
"0.5.0": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Externally set CDI Spec validation function.
|
||||||
|
specValidator func(*cdi.Spec) error
|
||||||
)
|
)
|
||||||
|
|
||||||
// Spec represents a single CDI Spec. It is usually loaded from a
|
// Spec represents a single CDI Spec. It is usually loaded from a
|
||||||
@ -64,7 +70,7 @@ func ReadSpec(path string, priority int) (*Spec, error) {
|
|||||||
return nil, errors.Wrapf(err, "failed to read CDI Spec %q", path)
|
return nil, errors.Wrapf(err, "failed to read CDI Spec %q", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
raw, err := parseSpec(data)
|
raw, err := ParseSpec(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to parse CDI Spec %q", path)
|
return nil, errors.Wrapf(err, "failed to parse CDI Spec %q", path)
|
||||||
}
|
}
|
||||||
@ -85,7 +91,7 @@ func ReadSpec(path string, priority int) (*Spec, error) {
|
|||||||
// priority. If Spec data validation fails NewSpec returns a nil
|
// priority. If Spec data validation fails NewSpec returns a nil
|
||||||
// Spec and an error.
|
// 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 := validateWithSchema(raw)
|
err := validateSpec(raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -105,6 +111,56 @@ func NewSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) {
|
|||||||
return spec, nil
|
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 {
|
||||||
|
var (
|
||||||
|
data []byte
|
||||||
|
dir string
|
||||||
|
tmp *os.File
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
err = validateSpec(s.Spec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.Ext(s.path) == ".yaml" {
|
||||||
|
data, err = yaml.Marshal(s.Spec)
|
||||||
|
} else {
|
||||||
|
data, err = json.Marshal(s.Spec)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to marshal Spec file")
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = filepath.Dir(s.path)
|
||||||
|
err = os.MkdirAll(dir, 0o755)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to create Spec dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp, err = os.CreateTemp(dir, "spec.*.tmp")
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to create Spec file")
|
||||||
|
}
|
||||||
|
_, err = tmp.Write(data)
|
||||||
|
tmp.Close()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to write Spec file")
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// GetVendor returns the vendor of this Spec.
|
// GetVendor returns the vendor of this Spec.
|
||||||
func (s *Spec) GetVendor() string {
|
func (s *Spec) GetVendor() string {
|
||||||
return s.vendor
|
return s.vendor
|
||||||
@ -179,8 +235,8 @@ func validateVersion(version string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse raw CDI Spec file data.
|
// ParseSpec parses CDI Spec data into a raw CDI Spec.
|
||||||
func parseSpec(data []byte) (*cdi.Spec, error) {
|
func ParseSpec(data []byte) (*cdi.Spec, error) {
|
||||||
var raw *cdi.Spec
|
var raw *cdi.Spec
|
||||||
err := yaml.UnmarshalStrict(data, &raw)
|
err := yaml.UnmarshalStrict(data, &raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -188,3 +244,22 @@ func parseSpec(data []byte) (*cdi.Spec, error) {
|
|||||||
}
|
}
|
||||||
return raw, nil
|
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()).
|
||||||
|
func SetSpecValidator(fn func(*cdi.Spec) error) {
|
||||||
|
specValidator = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateSpec validates the Spec using the extneral validator.
|
||||||
|
func validateSpec(raw *cdi.Spec) error {
|
||||||
|
if specValidator == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := specValidator(raw)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Spec validation failed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -17,30 +17,32 @@
|
|||||||
package cdi
|
package cdi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/container-orchestrated-devices/container-device-interface/schema"
|
"os"
|
||||||
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// Rename src to dst, both relative to the directory dir. If dst already exists
|
||||||
// DefaultExternalSchema is the JSON schema to load if found.
|
// refuse renaming with an error unless overwrite is explicitly asked for.
|
||||||
DefaultExternalSchema = "/etc/cdi/schema/schema.json"
|
func renameIn(dir, src, dst string, overwrite bool) error {
|
||||||
)
|
var flags uint
|
||||||
|
|
||||||
// SetSchema sets the Spec JSON validation schema to use.
|
dirf, err := os.Open(dir)
|
||||||
func SetSchema(name string) error {
|
|
||||||
s, err := schema.Load(name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "rename failed")
|
||||||
}
|
}
|
||||||
schema.Set(s)
|
defer dirf.Close()
|
||||||
|
|
||||||
|
if !overwrite {
|
||||||
|
flags = unix.RENAME_NOREPLACE
|
||||||
|
}
|
||||||
|
|
||||||
|
dirFd := int(dirf.Fd())
|
||||||
|
err = unix.Renameat2(dirFd, src, dirFd, dst, flags)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "rename failed")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate CDI Spec against JSON Schema.
|
|
||||||
func validateWithSchema(raw *cdi.Spec) error {
|
|
||||||
return schema.ValidateType(raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SetSchema(DefaultExternalSchema)
|
|
||||||
}
|
|
39
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_other.go
generated
vendored
Normal file
39
vendor/github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/spec_other.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//go:build !linux
|
||||||
|
// +build !linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
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 cdi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Rename src to dst, both relative to the directory dir. If dst already exists
|
||||||
|
// refuse renaming with an error unless overwrite is explicitly asked for.
|
||||||
|
func renameIn(dir, src, dst string, overwrite bool) error {
|
||||||
|
src = filepath.Join(dir, src)
|
||||||
|
dst = filepath.Join(dir, dst)
|
||||||
|
|
||||||
|
_, err := os.Stat(dst)
|
||||||
|
if err == nil && !overwrite {
|
||||||
|
return os.ErrExist
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.Rename(src, dst)
|
||||||
|
}
|
@ -1,27 +0,0 @@
|
|||||||
VALIDATE ?= ../bin/validate
|
|
||||||
SCHEMA ?= schema.json
|
|
||||||
|
|
||||||
test:
|
|
||||||
@FMT_RED=$$(tput setaf 1); \
|
|
||||||
FMT_BLUE=$$(tput setaf 12); \
|
|
||||||
FMT_CLEAR=$$(tput sgr0); \
|
|
||||||
echo "Running Good Tests"; \
|
|
||||||
for FILE in $$(ls "testdata/good"); do \
|
|
||||||
FILE_PATH="testdata/good/$${FILE}"; \
|
|
||||||
if $(VALIDATE) --schema "$(SCHEMA)" "$${FILE_PATH}" > /dev/null ; then \
|
|
||||||
printf '%s[OK]%s %s\n' "$${FMT_BLUE}" "$${FMT_CLEAR}" "$${FILE_PATH}"; \
|
|
||||||
else \
|
|
||||||
printf '%s[KO]%s %s\n' "$${FMT_RED}" "$${FMT_CLEAR}" "$${FILE_PATH}"; \
|
|
||||||
exit 1; \
|
|
||||||
fi \
|
|
||||||
done; \
|
|
||||||
echo "Running Bad Tests"; \
|
|
||||||
for FILE in $$(ls "testdata/bad"); do \
|
|
||||||
FILE_PATH="testdata/bad/$${FILE}"; \
|
|
||||||
if $(VALIDATE) --schema "$(SCHEMA)" "$${FILE_PATH}" > /dev/null ; then \
|
|
||||||
printf '%s[KO]%s %s\n' "$${FMT_RED}" "$${FMT_CLEAR}" "$${FILE_PATH}"; \
|
|
||||||
exit 1; \
|
|
||||||
else \
|
|
||||||
printf '%s[OK]%s %s\n' "$${FMT_BLUE}" "$${FMT_CLEAR}" "$${FILE_PATH}"; \
|
|
||||||
fi \
|
|
||||||
done
|
|
127
vendor/github.com/container-orchestrated-devices/container-device-interface/schema/defs.json
generated
vendored
127
vendor/github.com/container-orchestrated-devices/container-device-interface/schema/defs.json
generated
vendored
@ -1,127 +0,0 @@
|
|||||||
{
|
|
||||||
"description": "Definitions used throughout the Container Device Interface Specification",
|
|
||||||
"definitions": {
|
|
||||||
"uint32": {
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 0,
|
|
||||||
"maximum": 4294967295
|
|
||||||
},
|
|
||||||
"int64": {
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": -9223372036854775808,
|
|
||||||
"maximum": 9223372036854775807
|
|
||||||
},
|
|
||||||
"ArrayOfStrings": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"FilePath": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"Env": {
|
|
||||||
"$ref": "#/definitions/ArrayOfStrings"
|
|
||||||
},
|
|
||||||
"DeviceNode": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"path": {
|
|
||||||
"$ref": "#/definitions/FilePath"
|
|
||||||
},
|
|
||||||
"permissions": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"major": {
|
|
||||||
"$ref": "#/definitions/int64"
|
|
||||||
},
|
|
||||||
"minor": {
|
|
||||||
"$ref": "#/definitions/int64"
|
|
||||||
},
|
|
||||||
"uid": {
|
|
||||||
"$ref": "#/definitions/uint32"
|
|
||||||
},
|
|
||||||
"gid": {
|
|
||||||
"$ref": "#/definitions/uint32"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"path"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Mount": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"hostPath": {
|
|
||||||
"$ref": "#/definitions/FilePath"
|
|
||||||
},
|
|
||||||
"containerPath": {
|
|
||||||
"$ref": "#/definitions/FilePath"
|
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"hostPath",
|
|
||||||
"containerPath"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Hook": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"hookName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"path": {
|
|
||||||
"$ref": "#/definitions/FilePath"
|
|
||||||
},
|
|
||||||
"args": {
|
|
||||||
"$ref": "#/definitions/ArrayOfStrings"
|
|
||||||
},
|
|
||||||
"env": {
|
|
||||||
"$ref": "#/definitions/ArrayOfStrings"
|
|
||||||
},
|
|
||||||
"timeout": {
|
|
||||||
"$ref": "#/definitions/uint32"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"hookName",
|
|
||||||
"path"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"containerEdits": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"env": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"ref": "#definitions/Env"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"deviceNodes": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/DeviceNode"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mounts": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/Mount"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hooks": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/Hook"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
253
vendor/github.com/container-orchestrated-devices/container-device-interface/schema/schema.go
generated
vendored
253
vendor/github.com/container-orchestrated-devices/container-device-interface/schema/schema.go
generated
vendored
@ -1,253 +0,0 @@
|
|||||||
/*
|
|
||||||
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 schema
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"embed"
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
|
|
||||||
"github.com/hashicorp/go-multierror"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
schema "github.com/xeipuuv/gojsonschema"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// BuiltinSchemaName references the builtin schema for Load()/Set().
|
|
||||||
BuiltinSchemaName = "builtin"
|
|
||||||
// NoneSchemaName references a disabled/NOP schema for Load()/Set().
|
|
||||||
NoneSchemaName = "none"
|
|
||||||
// DefaultSchemaName is the none schema.
|
|
||||||
DefaultSchemaName = NoneSchemaName
|
|
||||||
|
|
||||||
// builtinSchemaFile is the builtin schema URI in our embedded FS.
|
|
||||||
builtinSchemaFile = "file:///schema.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Schema is a JSON validation schema.
|
|
||||||
type Schema struct {
|
|
||||||
schema *schema.Schema
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error wraps a JSON validation result.
|
|
||||||
type Error struct {
|
|
||||||
Result *schema.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets the default validating JSON schema.
|
|
||||||
func Set(s *Schema) {
|
|
||||||
current = s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the active validating JSON schema.
|
|
||||||
func Get() *Schema {
|
|
||||||
return current
|
|
||||||
}
|
|
||||||
|
|
||||||
// BuiltinSchema returns the builtin validating JSON Schema.
|
|
||||||
func BuiltinSchema() *Schema {
|
|
||||||
return builtin
|
|
||||||
}
|
|
||||||
|
|
||||||
// NopSchema returns an validating JSON Schema that does no real validation.
|
|
||||||
func NopSchema() *Schema {
|
|
||||||
return &Schema{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadAndValidate all data from the given reader, using the active schema for validation.
|
|
||||||
func ReadAndValidate(r io.Reader) ([]byte, error) {
|
|
||||||
return current.ReadAndValidate(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the data read from an io.Reader against the active schema.
|
|
||||||
func Validate(r io.Reader) error {
|
|
||||||
return current.Validate(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateData validates the given JSON document against the active schema.
|
|
||||||
func ValidateData(data []byte) error {
|
|
||||||
return current.ValidateData(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateFile validates the given JSON file against the active schema.
|
|
||||||
func ValidateFile(path string) error {
|
|
||||||
return current.ValidateFile(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateType validates a go object against the schema.
|
|
||||||
func ValidateType(obj interface{}) error {
|
|
||||||
return current.ValidateType(obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the given JSON Schema.
|
|
||||||
func Load(source string) (*Schema, error) {
|
|
||||||
var (
|
|
||||||
loader schema.JSONLoader
|
|
||||||
err error
|
|
||||||
s *schema.Schema
|
|
||||||
)
|
|
||||||
|
|
||||||
source = strings.TrimSpace(source)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case source == BuiltinSchemaName:
|
|
||||||
return BuiltinSchema(), nil
|
|
||||||
case source == NoneSchemaName, source == "":
|
|
||||||
return NopSchema(), nil
|
|
||||||
case strings.HasPrefix(source, "file://"):
|
|
||||||
case strings.HasPrefix(source, "http://"):
|
|
||||||
case strings.HasPrefix(source, "https://"):
|
|
||||||
default:
|
|
||||||
if strings.Index(source, "://") < 0 {
|
|
||||||
source, err = filepath.Abs(source)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err,
|
|
||||||
"failed to get JSON schema absolute path for %s", source)
|
|
||||||
}
|
|
||||||
source = "file://" + source
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loader = schema.NewReferenceLoader(source)
|
|
||||||
|
|
||||||
s, err = schema.NewSchema(loader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to load JSON schema")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Schema{schema: s}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadAndValidate all data from the given reader, using the schema for validation.
|
|
||||||
func (s *Schema) ReadAndValidate(r io.Reader) ([]byte, error) {
|
|
||||||
loader, reader := schema.NewReaderLoader(r)
|
|
||||||
data, err := ioutil.ReadAll(reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to read data for validation")
|
|
||||||
}
|
|
||||||
return data, s.validate(loader)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the data read from an io.Reader against the schema.
|
|
||||||
func (s *Schema) Validate(r io.Reader) error {
|
|
||||||
_, err := s.ReadAndValidate(r)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateData validates the given JSON data against the schema.
|
|
||||||
func (s *Schema) ValidateData(data []byte) error {
|
|
||||||
var (
|
|
||||||
any interface{}
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
if !bytes.HasPrefix(bytes.TrimSpace(data), []byte{'{'}) {
|
|
||||||
err = yaml.Unmarshal(data, &any)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to YAML unmarshal data for validation")
|
|
||||||
}
|
|
||||||
data, err = json.Marshal(any)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to JSON remarshal data for validation")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.validate(schema.NewBytesLoader(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateFile validates the given JSON file against the schema.
|
|
||||||
func (s *Schema) ValidateFile(path string) error {
|
|
||||||
if filepath.Ext(path) == ".json" {
|
|
||||||
return s.validate(schema.NewReferenceLoader("file://" + path))
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return s.ValidateData(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateType validates a go object against the schema.
|
|
||||||
func (s *Schema) ValidateType(obj interface{}) error {
|
|
||||||
l := schema.NewGoLoader(obj)
|
|
||||||
return s.validate(l)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the (to be) loaded doc against the schema.
|
|
||||||
func (s *Schema) validate(doc schema.JSONLoader) error {
|
|
||||||
if s == nil || s.schema == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
docErr, jsonErr := s.schema.Validate(doc)
|
|
||||||
if jsonErr != nil {
|
|
||||||
return errors.Wrap(jsonErr, "failed to load JSON data for validation")
|
|
||||||
}
|
|
||||||
if docErr.Valid() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Error{Result: docErr}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns the given Result's error as a multierror(.Error()).
|
|
||||||
func (e *Error) Error() string {
|
|
||||||
if e == nil || e.Result == nil || e.Result.Valid() {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var multi error
|
|
||||||
for _, err := range e.Result.Errors() {
|
|
||||||
multi = multierror.Append(multi, errors.Errorf("%v", err))
|
|
||||||
}
|
|
||||||
return strings.TrimRight(multi.Error(), "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
// our builtin schema
|
|
||||||
builtin *Schema
|
|
||||||
// currently loaded schema, builtin by default
|
|
||||||
current *Schema
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed *.json
|
|
||||||
var builtinFS embed.FS
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
s, err := schema.NewSchema(
|
|
||||||
schema.NewReferenceLoaderFileSystem(
|
|
||||||
builtinSchemaFile,
|
|
||||||
http.FS(builtinFS),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
builtin = NopSchema()
|
|
||||||
} else {
|
|
||||||
builtin = &Schema{schema: s}
|
|
||||||
}
|
|
||||||
|
|
||||||
current = builtin
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"description": "Configuration Schema for the Container Device Interface",
|
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"cdiVersion": {
|
|
||||||
"description": "The version of the Container Device Interface Specification that the document complies with",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"kind": {
|
|
||||||
"description": "The kind of the device usually of the form 'vendor.com/device'",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"devices": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"name": {
|
|
||||||
"description": "The name of the device",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"containerEdits": {
|
|
||||||
"$ref": "defs.json#/definitions/containerEdits"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"name",
|
|
||||||
"containerEdits"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"cdiVersion",
|
|
||||||
"kind",
|
|
||||||
"devices"
|
|
||||||
]
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ package specs
|
|||||||
import "os"
|
import "os"
|
||||||
|
|
||||||
// CurrentVersion is the current version of the Spec.
|
// CurrentVersion is the current version of the Spec.
|
||||||
const CurrentVersion = "0.3.0"
|
const CurrentVersion = "0.5.0"
|
||||||
|
|
||||||
// Spec is the base configuration for CDI
|
// Spec is the base configuration for CDI
|
||||||
type Spec struct {
|
type Spec struct {
|
||||||
@ -31,6 +31,7 @@ type ContainerEdits struct {
|
|||||||
// DeviceNode represents a device node that needs to be added to the OCI spec.
|
// DeviceNode represents a device node that needs to be added to the OCI spec.
|
||||||
type DeviceNode struct {
|
type DeviceNode struct {
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
|
HostPath string `json:"hostPath,omitempty"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
Major int64 `json:"major,omitempty"`
|
Major int64 `json:"major,omitempty"`
|
||||||
Minor int64 `json:"minor,omitempty"`
|
Minor int64 `json:"minor,omitempty"`
|
||||||
@ -45,6 +46,7 @@ type Mount struct {
|
|||||||
HostPath string `json:"hostPath"`
|
HostPath string `json:"hostPath"`
|
||||||
ContainerPath string `json:"containerPath"`
|
ContainerPath string `json:"containerPath"`
|
||||||
Options []string `json:"options,omitempty"`
|
Options []string `json:"options,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hook represents a hook that needs to be added to the OCI spec.
|
// Hook represents a hook that needs to be added to the OCI spec.
|
||||||
|
@ -95,6 +95,7 @@ func (m *Mount) ToOCI() spec.Mount {
|
|||||||
Source: m.HostPath,
|
Source: m.HostPath,
|
||||||
Destination: m.ContainerPath,
|
Destination: m.ContainerPath,
|
||||||
Options: m.Options,
|
Options: m.Options,
|
||||||
|
Type: m.Type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
@ -77,10 +77,9 @@ github.com/cilium/ebpf/internal
|
|||||||
github.com/cilium/ebpf/internal/sys
|
github.com/cilium/ebpf/internal/sys
|
||||||
github.com/cilium/ebpf/internal/unix
|
github.com/cilium/ebpf/internal/unix
|
||||||
github.com/cilium/ebpf/link
|
github.com/cilium/ebpf/link
|
||||||
# github.com/container-orchestrated-devices/container-device-interface v0.3.1
|
# github.com/container-orchestrated-devices/container-device-interface v0.5.1
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
github.com/container-orchestrated-devices/container-device-interface/pkg/cdi
|
github.com/container-orchestrated-devices/container-device-interface/pkg/cdi
|
||||||
github.com/container-orchestrated-devices/container-device-interface/schema
|
|
||||||
github.com/container-orchestrated-devices/container-device-interface/specs-go
|
github.com/container-orchestrated-devices/container-device-interface/specs-go
|
||||||
# github.com/containerd/aufs v1.0.0
|
# github.com/containerd/aufs v1.0.0
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
|
Loading…
Reference in New Issue
Block a user