From d59fc653cb5652871062ec69bdf1650cf66860b4 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Wed, 19 Jan 2022 21:13:28 +0200 Subject: [PATCH 1/3] pkg/cdi: improve CDI device validation, injection. Accept all OCI supported device types in CDI Spec files. Address a few bugs and omissions in InjectDevices making it more complete / better usable in runtimes: - generate cgroup device access rules - remove conflicting devices/mounts prior to injection - fill in missing device type, major/minor number from host - use container uid/gid if they are unspecified in CDI Spec - sort mounts after injection Signed-off-by: Krisztian Litkey --- pkg/cdi/cache.go | 16 ++- pkg/cdi/cache_test.go | 188 +++++++++++++++++++++++++++++++- pkg/cdi/container-edits.go | 143 +++++++++++++++++++++++- pkg/cdi/container-edits_test.go | 182 ++++++++++++++++++++++++++++++- pkg/cdi/device.go | 10 +- pkg/cdi/registry_test.go | 132 +++++++++++++++++++++- pkg/cdi/spec.go | 11 +- 7 files changed, 650 insertions(+), 32 deletions(-) diff --git a/pkg/cdi/cache.go b/pkg/cdi/cache.go index c4befc8..5c3f803 100644 --- a/pkg/cdi/cache.go +++ b/pkg/cdi/cache.go @@ -153,10 +153,7 @@ func (c *Cache) Refresh() error { // returns any unresolvable devices and an error if injection fails for // any of the devices. func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, error) { - var ( - unresolved []string - specs = map[*Spec]struct{}{} - ) + var unresolved []string if ociSpec == nil { return devices, errors.Errorf("can't inject devices, nil OCI Spec") @@ -165,6 +162,9 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e c.Lock() defer c.Unlock() + edits := &ContainerEdits{} + specs := map[*Spec]struct{}{} + for _, device := range devices { d := c.devices[device] if d == nil { @@ -173,9 +173,9 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e } if _, ok := specs[d.GetSpec()]; !ok { specs[d.GetSpec()] = struct{}{} - d.GetSpec().ApplyEdits(ociSpec) + edits.Append(d.GetSpec().edits()) } - d.ApplyEdits(ociSpec) + edits.Append(d.edits()) } if unresolved != nil { @@ -183,6 +183,10 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e strings.Join(devices, ", ")) } + if err := edits.Apply(ociSpec); err != nil { + return nil, errors.Wrap(err, "failed to inject devices") + } + return nil, nil } diff --git a/pkg/cdi/cache_test.go b/pkg/cdi/cache_test.go index f24687b..0f4ed4d 100644 --- a/pkg/cdi/cache_test.go +++ b/pkg/cdi/cache_test.go @@ -53,6 +53,9 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, sources: map[string]string{ @@ -70,10 +73,16 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 - name: "dev2" containerEdits: deviceNodes: - path: "/dev/vendor1-dev2" + type: b + major: 10 + minor: 2 `, }, run: map[string]string{ @@ -85,6 +94,9 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, sources: map[string]string{ @@ -103,10 +115,16 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 - name: "dev2" containerEdits: deviceNodes: - path: "/dev/vendor1-dev2" + type: b + major: 10 + minor: 2 `, "vendor1-other.yaml": ` cdiVersion: "0.2.0" @@ -116,6 +134,9 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, sources: map[string]string{ @@ -203,6 +224,9 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, }, @@ -237,6 +261,9 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, }, @@ -250,6 +277,9 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev2" + type: b + major: 10 + minor: 2 `, }, }, @@ -290,6 +320,9 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, "vendor1-other.yaml": ` cdiVersion: "0.2.0" @@ -299,6 +332,9 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev2" + type: b + major: 10 + minor: 2 `, }, }, @@ -344,6 +380,9 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, }, @@ -357,6 +396,9 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, }, @@ -395,10 +437,16 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 - name: "dev2" containerEdits: deviceNodes: - path: "/dev/vendor1-dev2" + type: b + major: 10 + minor: 1 `, }, }, @@ -412,10 +460,16 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 - name: "dev3" containerEdits: deviceNodes: - path: "/dev/vendor1-dev3" + type: b + major: 10 + minor: 3 `, }, }, @@ -541,6 +595,9 @@ devices: - "VENDOR1_VAR1=VAL1" deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, }, @@ -558,7 +615,21 @@ devices: Linux: &oci.Linux{ Devices: []oci.LinuxDevice{ { - Path: "/dev/vendor1-dev1", + Path: "/dev/vendor1-dev1", + Type: "b", + Major: 10, + Minor: 1, + }, + }, + Resources: &oci.LinuxResources{ + Devices: []oci.LinuxDeviceCgroup{ + { + Allow: true, + Type: "b", + Major: int64ptr(10), + Minor: int64ptr(1), + Access: "rwm", + }, }, }, }, @@ -581,6 +652,9 @@ devices: - "VENDOR1_VAR1=VAL1" deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, }, @@ -623,7 +697,21 @@ devices: Path: "/dev/zero", }, { - Path: "/dev/vendor1-dev1", + Path: "/dev/vendor1-dev1", + Type: "b", + Major: 10, + Minor: 1, + }, + }, + Resources: &oci.LinuxResources{ + Devices: []oci.LinuxDeviceCgroup{ + { + Allow: true, + Type: "b", + Major: int64ptr(10), + Minor: int64ptr(1), + Access: "rwm", + }, }, }, }, @@ -646,12 +734,18 @@ devices: - "VENDOR1_DEV1=VAL1" deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 - name: "dev2" containerEdits: env: - "VENDOR1_DEV2=VAL2" deviceNodes: - path: "/dev/vendor1-dev2" + type: b + major: 10 + minor: 2 hooks: - hookName: prestart path: "/usr/local/bin/prestart-vendor-hook" @@ -671,6 +765,9 @@ devices: - "VENDOR1_DEV3=VAL3" deviceNodes: - path: "/dev/vendor1-dev3" + type: b + major: 10 + minor: 3 `, }, }, @@ -733,13 +830,47 @@ devices: Path: "/dev/zero", }, { - Path: "/dev/vendor1-dev1", + Path: "/dev/vendor1-dev1", + Type: "b", + Major: 10, + Minor: 1, }, { - Path: "/dev/vendor1-dev2", + Path: "/dev/vendor1-dev2", + Type: "b", + Major: 10, + Minor: 2, }, { - Path: "/dev/vendor1-dev3", + Path: "/dev/vendor1-dev3", + Type: "b", + Major: 10, + Minor: 3, + }, + }, + Resources: &oci.LinuxResources{ + Devices: []oci.LinuxDeviceCgroup{ + { + Allow: true, + Type: "b", + Major: int64ptr(10), + Minor: int64ptr(1), + Access: "rwm", + }, + { + Allow: true, + Type: "b", + Major: int64ptr(10), + Minor: int64ptr(2), + Access: "rwm", + }, + { + Allow: true, + Type: "b", + Major: int64ptr(10), + Minor: int64ptr(3), + Access: "rwm", + }, }, }, }, @@ -762,6 +893,9 @@ devices: - "VENDOR1_VAR1=VAL1" deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, }, @@ -840,6 +974,9 @@ devices: - "VENDOR1_VAR1=VAL1" deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, }, @@ -863,12 +1000,18 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 - name: "dev2" containerEdits: env: - "VENDOR1_DEV2=VAL2" deviceNodes: - path: "/dev/vendor1-dev2" + type: b + major: 10 + minor: 2 `, "vendor1-other.yaml": ` cdiVersion: "0.2.0" @@ -879,12 +1022,18 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-other-dev1" + type: b + major: 11 + minor: 1 - name: "dev2" containerEdits: env: - "VENDOR1_DEV2=VAL2" deviceNodes: - path: "/dev/vendor1-other-dev2" + type: b + major: 11 + minor: 2 `, }, }, @@ -909,12 +1058,18 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 - name: "dev2" containerEdits: env: - "VENDOR1_DEV2=VAL2" deviceNodes: - path: "/dev/vendor1-dev2" + type: b + major: 10 + minor: 2 `, "vendor2.yaml": ` cdiVersion: "0.2.0" @@ -925,10 +1080,16 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor2-dev1" + type: b + major: 12 + minor: 1 - name: "dev2" containerEdits: deviceNodes: - path: "/dev/vendor2-dev2" + type: b + major: 12 + minor: 2 `, "vendor2-other.yaml": ` cdiVersion: "0.2.0" @@ -939,10 +1100,16 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor2-another-dev1" + type: b + major: 13 + minor: 1 - name: "dev2" containerEdits: deviceNodes: - path: "/dev/vendor2-another-dev2" + type: b + major: 13 + minor: 2 `, "vendor3.yaml": ` cdiVersion: "0.2.0" @@ -953,10 +1120,17 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor3-dev1" + type: b + major: 11 + minor: 1 + - name: "dev2" containerEdits: deviceNodes: - path: "/dev/vendor3-dev2" + type: b + major: 14 + minor: 2 `, }, }, @@ -1030,3 +1204,7 @@ func updateSpecDirs(t *testing.T, dir string, etc, run map[string]string) error } return updateTestDir(t, dir, updates) } + +func int64ptr(v int64) *int64 { + return &v +} diff --git a/pkg/cdi/container-edits.go b/pkg/cdi/container-edits.go index 767cba5..80d88b1 100644 --- a/pkg/cdi/container-edits.go +++ b/pkg/cdi/container-edits.go @@ -17,6 +17,9 @@ package cdi import ( + "os" + "path/filepath" + "sort" "strings" "github.com/pkg/errors" @@ -24,6 +27,8 @@ import ( "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 ( @@ -78,12 +83,44 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error { if len(e.Env) > 0 { specgen.AddMultipleProcessEnv(e.Env) } + for _, d := range e.DeviceNodes { - specgen.AddDevice(d.ToOCI()) + dev := d.ToOCI() + if err := fillMissingInfo(&dev); err != nil { + return err + } + + if dev.UID == nil && spec.Process != nil { + if uid := spec.Process.User.UID; uid > 0 { + dev.UID = &uid + } + } + if dev.GID == nil && spec.Process != nil { + if gid := spec.Process.User.GID; gid > 0 { + dev.GID = &gid + } + } + + specgen.RemoveDevice(dev.Path) + specgen.AddDevice(dev) + + if dev.Type == "b" || dev.Type == "c" { + access := d.Permissions + if access == "" { + access = "rwm" + } + specgen.AddLinuxResourcesDevice(true, dev.Type, &dev.Major, &dev.Minor, access) + } } - for _, m := range e.Mounts { - specgen.AddMount(m.ToOCI()) + + if len(e.Mounts) > 0 { + for _, m := range e.Mounts { + specgen.RemoveMount(m.ContainerPath) + specgen.AddMount(m.ToOCI()) + } + sortMounts(&specgen) } + for _, h := range e.Hooks { switch h.HookName { case PrestartHook: @@ -138,6 +175,27 @@ func (e *ContainerEdits) Validate() error { return nil } +// Append other edits into this one. If called with a nil receiver, +// allocates and returns newly allocated edits. +func (e *ContainerEdits) Append(o *ContainerEdits) *ContainerEdits { + if o == nil || o.ContainerEdits == nil { + return e + } + if e == nil { + e = &ContainerEdits{} + } + if e.ContainerEdits == nil { + e.ContainerEdits = &specs.ContainerEdits{} + } + + e.Env = append(e.Env, o.Env...) + e.DeviceNodes = append(e.DeviceNodes, o.DeviceNodes...) + e.Hooks = append(e.Hooks, o.Hooks...) + e.Mounts = append(e.Mounts, o.Mounts...) + + return e +} + // isEmpty returns true if these edits are empty. This is valid in a // global Spec context but invalid in a Device context. func (e *ContainerEdits) isEmpty() bool { @@ -164,10 +222,18 @@ type DeviceNode struct { // Validate a CDI Spec DeviceNode. func (d *DeviceNode) Validate() error { + validTypes := map[string]struct{}{ + "": {}, + "b": {}, + "c": {}, + "u": {}, + "p": {}, + } + if d.Path == "" { return errors.New("invalid (empty) device path") } - if d.Type != "" && d.Type != "b" && d.Type != "c" { + if _, ok := validTypes[d.Type]; !ok { return errors.Errorf("device %q: invalid type %q", d.Path, d.Type) } for _, bit := range d.Permissions { @@ -220,3 +286,72 @@ func ensureOCIHooks(spec *oci.Spec) { spec.Hooks = &oci.Hooks{} } } + +// fillMissingInfo fills in missing mandatory attributes from the host device. +func fillMissingInfo(dev *oci.LinuxDevice) error { + if dev.Type != "" && (dev.Major != 0 || dev.Type == "p") { + return nil + } + 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 == "" { + dev.Type = string(hostDev.Type) + } else { + if dev.Type != string(hostDev.Type) { + return errors.Errorf("CDI device %q, host type mismatch (%s, %s)", + dev.Path, dev.Type, string(hostDev.Type)) + } + } + if dev.Major == 0 && dev.Type != "p" { + dev.Major = hostDev.Major + dev.Minor = hostDev.Minor + } + + return nil +} + +// sortMounts sorts the mounts in the given OCI Spec. +func sortMounts(specgen *ocigen.Generator) { + mounts := specgen.Mounts() + specgen.ClearMounts() + sort.Sort(orderedMounts(mounts)) + specgen.Config.Mounts = mounts +} + +// 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 +type orderedMounts []oci.Mount + +// Len returns the number of mounts. Used in sorting. +func (m orderedMounts) Len() int { + return len(m) +} + +// Less returns true if the number of parts (a/b/c would be 3 parts) in the +// mount indexed by parameter 1 is less than that of the mount indexed by +// parameter 2. Used in sorting. +func (m orderedMounts) Less(i, j int) bool { + ip, jp := m.parts(i), m.parts(j) + if ip < jp { + return true + } + if jp < ip { + return false + } + return m[i].Destination < m[j].Destination +} + +// Swap swaps two items in an array of mounts. Used in sorting +func (m orderedMounts) Swap(i, j int) { + m[i], m[j] = m[j], m[i] +} + +// parts returns the number of parts in the destination of a mount. Used in sorting. +func (m orderedMounts) parts(i int) int { + return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator)) +} diff --git a/pkg/cdi/container-edits_test.go b/pkg/cdi/container-edits_test.go index 04e9add..e09b018 100644 --- a/pkg/cdi/container-edits_test.go +++ b/pkg/cdi/container-edits_test.go @@ -20,6 +20,7 @@ import ( "os" "testing" + "github.com/container-orchestrated-devices/container-device-interface/specs-go" cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go" oci "github.com/opencontainers/runtime-spec/specs-go" "github.com/stretchr/testify/require" @@ -80,7 +81,7 @@ func TestValidateContainerEdits(t *testing.T) { DeviceNodes: []*cdi.DeviceNode{ { Path: "/dev/null", - Type: "b", + Type: "c", }, }, }, @@ -302,7 +303,21 @@ func TestApplyContainerEdits(t *testing.T) { Linux: &oci.Linux{ Devices: []oci.LinuxDevice{ { - Path: "/dev/null", + Path: "/dev/null", + Type: "c", + Major: 1, + Minor: 3, + }, + }, + Resources: &oci.LinuxResources{ + Devices: []oci.LinuxDeviceCgroup{ + { + Allow: true, + Type: "c", + Major: int64ptr(1), + Minor: int64ptr(3), + Access: "rwm", + }, }, }, }, @@ -318,7 +333,7 @@ func TestApplyContainerEdits(t *testing.T) { DeviceNodes: []*cdi.DeviceNode{ { Path: "/dev/null", - Type: "b", + Type: "c", }, }, }, @@ -331,8 +346,21 @@ func TestApplyContainerEdits(t *testing.T) { Linux: &oci.Linux{ Devices: []oci.LinuxDevice{ { - Path: "/dev/null", - Type: "b", + Path: "/dev/null", + Type: "c", + Major: 1, + Minor: 3, + }, + }, + Resources: &oci.LinuxResources{ + Devices: []oci.LinuxDeviceCgroup{ + { + Allow: true, + Type: "c", + Major: int64ptr(1), + Minor: int64ptr(3), + Access: "rwm", + }, }, }, }, @@ -454,6 +482,150 @@ func TestApplyContainerEdits(t *testing.T) { } } +func TestAppend(t *testing.T) { + type testCase struct { + name string + dst *ContainerEdits + src []*ContainerEdits + result *ContainerEdits + } + for _, tc := range []*testCase{ + { + name: "merge nil into nil", + dst: nil, + src: []*ContainerEdits{ + nil, + }, + result: nil, + }, + { + name: "merge non-nil into nil", + dst: nil, + src: []*ContainerEdits{ + { + ContainerEdits: &specs.ContainerEdits{ + Env: []string{ + "var1=val1", + }, + }, + }, + }, + result: &ContainerEdits{ + ContainerEdits: &specs.ContainerEdits{ + Env: []string{ + "var1=val1", + }, + }, + }, + }, + { + name: "merge nil into non-nil", + dst: &ContainerEdits{ + ContainerEdits: &specs.ContainerEdits{ + Env: []string{ + "var1=val1", + }, + }, + }, + src: []*ContainerEdits{ + nil, + }, + result: &ContainerEdits{ + ContainerEdits: &specs.ContainerEdits{ + Env: []string{ + "var1=val1", + }, + }, + }, + }, + { + name: "merge multiple into non-nil", + dst: &ContainerEdits{ + ContainerEdits: &specs.ContainerEdits{ + Env: []string{ + "var0=val0", + }, + }, + }, + src: []*ContainerEdits{ + { + ContainerEdits: &specs.ContainerEdits{ + Env: []string{ + "var1=val1", + }, + DeviceNodes: []*specs.DeviceNode{ + { + Path: "/dev/dev1", + }, + }, + }, + }, + { + ContainerEdits: &specs.ContainerEdits{ + Env: []string{ + "var2=val2", + "var3=val3", + }, + DeviceNodes: []*specs.DeviceNode{ + { + Path: "/dev/dev2", + }, + { + Path: "/dev/dev3", + }, + }, + }, + }, + { + ContainerEdits: &specs.ContainerEdits{ + Env: []string{ + "var4=val4", + }, + DeviceNodes: []*specs.DeviceNode{ + { + Path: "/dev/dev4", + }, + }, + }, + }, + }, + result: &ContainerEdits{ + ContainerEdits: &specs.ContainerEdits{ + Env: []string{ + "var0=val0", + "var1=val1", + "var2=val2", + "var3=val3", + "var4=val4", + }, + DeviceNodes: []*specs.DeviceNode{ + { + Path: "/dev/dev1", + }, + { + Path: "/dev/dev2", + }, + { + Path: "/dev/dev3", + }, + { + Path: "/dev/dev4", + }, + }, + }, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + dst := tc.dst + for _, src := range tc.src { + dst = dst.Append(src) + } + require.Equal(t, tc.result, dst, "append container edits") + }) + } +} + func fileMode(mode int) *os.FileMode { fm := os.FileMode(mode) return &fm diff --git a/pkg/cdi/device.go b/pkg/cdi/device.go index 84b7142..0bb1f53 100644 --- a/pkg/cdi/device.go +++ b/pkg/cdi/device.go @@ -54,8 +54,12 @@ func (d *Device) GetQualifiedName() string { // ApplyEdits applies the device-speific container edits to an OCI Spec. func (d *Device) ApplyEdits(ociSpec *oci.Spec) error { - e := ContainerEdits{&d.ContainerEdits} - return e.Apply(ociSpec) + return d.edits().Apply(ociSpec) +} + +// edits returns the applicable container edits for this spec. +func (d *Device) edits() *ContainerEdits { + return &ContainerEdits{&d.ContainerEdits} } // Validate the device. @@ -63,7 +67,7 @@ func (d *Device) validate() error { if err := ValidateDeviceName(d.Name); err != nil { return err } - edits := ContainerEdits{&d.ContainerEdits} + edits := d.edits() if edits.isEmpty() { return errors.Errorf("invalid device, empty device edits") } diff --git a/pkg/cdi/registry_test.go b/pkg/cdi/registry_test.go index ac799c6..036d096 100644 --- a/pkg/cdi/registry_test.go +++ b/pkg/cdi/registry_test.go @@ -55,6 +55,9 @@ devices: - "VENDOR1_VAR1=VAL1" deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, }, @@ -72,7 +75,21 @@ devices: Linux: &oci.Linux{ Devices: []oci.LinuxDevice{ { - Path: "/dev/vendor1-dev1", + Path: "/dev/vendor1-dev1", + Type: "b", + Major: 10, + Minor: 1, + }, + }, + Resources: &oci.LinuxResources{ + Devices: []oci.LinuxDeviceCgroup{ + { + Allow: true, + Type: "b", + Major: int64ptr(10), + Minor: int64ptr(1), + Access: "rwm", + }, }, }, }, @@ -95,6 +112,9 @@ devices: - "VENDOR1_VAR1=VAL1" deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, }, @@ -137,7 +157,21 @@ devices: Path: "/dev/zero", }, { - Path: "/dev/vendor1-dev1", + Path: "/dev/vendor1-dev1", + Type: "b", + Major: 10, + Minor: 1, + }, + }, + Resources: &oci.LinuxResources{ + Devices: []oci.LinuxDeviceCgroup{ + { + Allow: true, + Type: "b", + Major: int64ptr(10), + Minor: int64ptr(1), + Access: "rwm", + }, }, }, }, @@ -160,12 +194,18 @@ devices: - "VENDOR1_DEV1=VAL1" deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 - name: "dev2" containerEdits: env: - "VENDOR1_DEV2=VAL2" deviceNodes: - path: "/dev/vendor1-dev2" + type: b + major: 10 + minor: 2 hooks: - hookName: prestart path: "/usr/local/bin/prestart-vendor-hook" @@ -185,6 +225,9 @@ devices: - "VENDOR1_DEV3=VAL3" deviceNodes: - path: "/dev/vendor1-dev3" + type: b + major: 10 + minor: 3 `, }, }, @@ -247,13 +290,47 @@ devices: Path: "/dev/zero", }, { - Path: "/dev/vendor1-dev1", + Path: "/dev/vendor1-dev1", + Type: "b", + Major: 10, + Minor: 1, }, { - Path: "/dev/vendor1-dev2", + Path: "/dev/vendor1-dev2", + Type: "b", + Major: 10, + Minor: 2, }, { - Path: "/dev/vendor1-dev3", + Path: "/dev/vendor1-dev3", + Type: "b", + Major: 10, + Minor: 3, + }, + }, + Resources: &oci.LinuxResources{ + Devices: []oci.LinuxDeviceCgroup{ + { + Allow: true, + Type: "b", + Major: int64ptr(10), + Minor: int64ptr(1), + Access: "rwm", + }, + { + Allow: true, + Type: "b", + Major: int64ptr(10), + Minor: int64ptr(2), + Access: "rwm", + }, + { + Allow: true, + Type: "b", + Major: int64ptr(10), + Minor: int64ptr(3), + Access: "rwm", + }, }, }, }, @@ -276,6 +353,9 @@ devices: - "VENDOR1_VAR1=VAL1" deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, }, @@ -354,6 +434,9 @@ devices: - "VENDOR1_VAR1=VAL1" deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, }, @@ -377,12 +460,18 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 - name: "dev2" containerEdits: env: - "VENDOR1_DEV2=VAL2" deviceNodes: - path: "/dev/vendor1-dev2" + type: b + major: 10 + minor: 2 `, "vendor1-other.yaml": ` cdiVersion: "0.2.0" @@ -393,12 +482,18 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-other-dev1" + type: b + major: 12 + minor: 1 - name: "dev2" containerEdits: env: - "VENDOR1_DEV2=VAL2" deviceNodes: - path: "/dev/vendor1-other-dev2" + type: b + major: 11 + minor: 2 `, }, }, @@ -423,12 +518,18 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 - name: "dev2" containerEdits: env: - "VENDOR1_DEV2=VAL2" deviceNodes: - path: "/dev/vendor1-dev2" + type: b + major: 10 + minor: 2 `, "vendor2.yaml": ` cdiVersion: "0.2.0" @@ -546,6 +647,9 @@ devices: - "VENDOR1_VAR1=VAL1" deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 `, }, }, @@ -566,12 +670,18 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 - name: "dev2" containerEdits: env: - "VENDOR1_DEV2=VAL2" deviceNodes: - path: "/dev/vendor1-dev2" + type: b + major: 10 + minor: 2 `, "vendor1-other.yaml": ` cdiVersion: "0.2.0" @@ -582,12 +692,18 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-other-dev1" + type: b + major: 11 + minor: 1 - name: "dev2" containerEdits: env: - "VENDOR1_DEV2=VAL2" deviceNodes: - path: "/dev/vendor1-other-dev2" + type: b + major: 11 + minor: 2 `, }, }, @@ -611,12 +727,18 @@ devices: containerEdits: deviceNodes: - path: "/dev/vendor1-dev1" + type: b + major: 10 + minor: 1 - name: "dev2" containerEdits: env: - "VENDOR1_DEV2=VAL2" deviceNodes: - path: "/dev/vendor1-dev2" + type: b + major: 10 + minor: 2 `, "vendor2.yaml": ` cdiVersion: "0.2.0" diff --git a/pkg/cdi/spec.go b/pkg/cdi/spec.go index 20f1884..0341ae3 100644 --- a/pkg/cdi/spec.go +++ b/pkg/cdi/spec.go @@ -120,8 +120,12 @@ func (s *Spec) GetPriority() int { // ApplyEdits applies the Spec's global-scope container edits to an OCI Spec. func (s *Spec) ApplyEdits(ociSpec *oci.Spec) error { - e := ContainerEdits{&s.ContainerEdits} - return e.Apply(ociSpec) + return s.edits().Apply(ociSpec) +} + +// edits returns the applicable global container edits for this spec. +func (s *Spec) edits() *ContainerEdits { + return &ContainerEdits{&s.ContainerEdits} } // Validate the Spec. @@ -135,8 +139,7 @@ func (s *Spec) validate() (map[string]*Device, error) { if err := ValidateClassName(s.class); err != nil { return nil, err } - edits := &ContainerEdits{&s.ContainerEdits} - if err := edits.Validate(); err != nil { + if err := s.edits().Validate(); err != nil { return nil, err } From 8e515b1d5085b4398ffba331af495675fb897c07 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Mon, 17 Jan 2022 16:42:40 +0200 Subject: [PATCH 2/3] pkg/cdi: add K8s annotation handling functions. Add functions for annotating containers for CDI device injection and for extracting CDI device names from such annotations. These functions can be used by CDI-aware devices plugins and runtimes, to request the injection of CDI devices and to recover the names of devices to inject correspondingly. For example, the following call to AnnotateInjection annotations := map[string]string{} AnnotateInjection(annotations, "vendor.gpu", []string{ "vendor.com/gpu=gpu0", "vendor.com/gpu=gpu1", }) produces the following annotations map[string]string{ "cdi.k8s.io/vendor.gpu": "vendor.com/gpu=gpu0,vendor.com/gpu=gpu1", } Subsequently parsing these annotations with devices, err := ParseAnnotations(annotations) extracts and returns the original slice of devices to inject: []string{ "vendor.com/gpu=gpu0", "vendor.com/gpu=gpu1", } Notes: These functions provide a temporary solution for delivering CDI device injection requests from either a client or a K8s Device Plugin to the container runtime. They can be removed once dedicated fields to the same effect are added natively to the CRI protocol. Signed-off-by: Krisztian Litkey --- pkg/cdi/annotations.go | 139 +++++++++++ pkg/cdi/annotations_test.go | 458 ++++++++++++++++++++++++++++++++++++ 2 files changed, 597 insertions(+) create mode 100644 pkg/cdi/annotations.go create mode 100644 pkg/cdi/annotations_test.go diff --git a/pkg/cdi/annotations.go b/pkg/cdi/annotations.go new file mode 100644 index 0000000..1055c7d --- /dev/null +++ b/pkg/cdi/annotations.go @@ -0,0 +1,139 @@ +/* + Copyright © 2021-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 ( + "strings" + + "github.com/pkg/errors" +) + +const ( + // AnnotationPrefix is the prefix for CDI container annotation keys. + AnnotationPrefix = "cdi.k8s.io/" +) + +// UpdateAnnotations updates annotations with a plugin-specific CDI device +// injection request for the given devices. Upon any error a non-nil error +// is returned and annotations are left intact. By convention plugin should +// be in the format of "vendor.device-type". +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") + } + if _, ok := annotations[key]; ok { + return annotations, errors.Errorf("CDI annotation failed, key %q used", key) + } + value, err := AnnotationValue(devices) + if err != nil { + return annotations, errors.Wrap(err, "CDI annotation failed") + } + + if annotations == nil { + annotations = make(map[string]string) + } + annotations[key] = value + + return annotations, nil +} + +// ParseAnnotations parses annotations for CDI device injection requests. +// The keys and devices from all such requests are collected into slices +// which are returned as the result. All devices are expected to be fully +// qualified CDI device names. If any device fails this check empty slices +// are returned along with a non-nil error. The annotations are expected +// to be formatted by, or in a compatible fashion to UpdateAnnotations(). +func ParseAnnotations(annotations map[string]string) ([]string, []string, error) { + var ( + keys []string + devices []string + ) + + for key, value := range annotations { + if !strings.HasPrefix(key, AnnotationPrefix) { + continue + } + for _, d := range strings.Split(value, ",") { + if !IsQualifiedName(d) { + return nil, nil, errors.Errorf("invalid CDI device name %q", d) + } + devices = append(devices, d) + } + keys = append(keys, key) + } + + return keys, devices, nil +} + +// AnnotationKey returns a unique annotation key for an device allocation +// by a K8s device plugin. pluginName should be in the format of +// "vendor.device-type". deviceID is the ID of the device the plugin is +// allocating. It is used to make sure that the generated key is unique +// even if multiple allocations by a single plugin needs to be annotated. +func AnnotationKey(pluginName, deviceID string) (string, error) { + const maxNameLen = 63 + + if pluginName == "" { + return "", errors.New("invalid plugin name, empty") + } + if deviceID == "" { + return "", errors.New("invalid deviceID, empty") + } + + name := pluginName + "_" + strings.ReplaceAll(deviceID, "/", "_") + + if len(name) > maxNameLen { + return "", errors.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", + name, c) + } + if len(name) > 2 { + for _, c := range name[1 : len(name)-1] { + switch { + case isAlphaNumeric(c): + case c == '_' || c == '-' || c == '.': + default: + return "", errors.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", + name, c) + } + + return AnnotationPrefix + name, nil +} + +// AnnotationValue returns an annotation value for the given devices. +func AnnotationValue(devices []string) (string, error) { + value, sep := "", "" + for _, d := range devices { + if _, _, _, err := ParseQualifiedName(d); err != nil { + return "", err + } + value += sep + d + sep = "," + } + + return value, nil +} diff --git a/pkg/cdi/annotations_test.go b/pkg/cdi/annotations_test.go new file mode 100644 index 0000000..a253c22 --- /dev/null +++ b/pkg/cdi/annotations_test.go @@ -0,0 +1,458 @@ +/* + 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 ( + "sort" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestAnnotationKey(t *testing.T) { + type testCase = struct { + name string + plugin string + devID string + key string + invalid bool + } + + for _, tc := range []*testCase{ + { + name: "invalid, empty plugin", + plugin: "", + invalid: true, + }, + { + name: "invalid, empty device ID", + plugin: "plugin", + devID: "", + invalid: true, + }, + { + name: "invalid, non-alphanumeric first character", + plugin: "_vendor.class", + devID: "device", + invalid: true, + }, + { + name: "invalid, non-alphanumeric last character", + plugin: "vendor.class", + devID: "device_", + invalid: true, + }, + { + name: "invalid, plugin contains invalid characters", + plugin: "ven.dor-cl+ass", + devID: "device", + invalid: true, + }, + { + name: "invalid, devID contains invalid characters", + plugin: "vendor.class", + devID: "dev+ice", + invalid: true, + }, + { + name: "invalid, too plugin long", + plugin: "123456789012345678901234567890123456789012345678901234567", + devID: "device", + invalid: true, + }, + { + name: "valid, simple", + plugin: "vendor.class", + devID: "device", + key: AnnotationPrefix + "vendor.class" + "_" + "device", + }, + { + name: "valid, with special characters", + plugin: "v-e.n_d.or.cl-as_s", + devID: "d_e-v-i-c_e", + key: AnnotationPrefix + "v-e.n_d.or.cl-as_s" + "_" + "d_e-v-i-c_e", + }, + { + name: "valid, with /'s replaced in devID", + plugin: "v-e.n_d.or.cl-as_s", + devID: "d-e/v/i/c-e", + key: AnnotationPrefix + "v-e.n_d.or.cl-as_s" + "_" + "d-e_v_i_c-e", + }, + } { + t.Run(tc.name, func(t *testing.T) { + key, err := AnnotationKey(tc.plugin, tc.devID) + if !tc.invalid { + require.NoError(t, err, "annotation key") + require.Equal(t, tc.key, key, "annotation key") + } else { + require.Error(t, err) + } + }) + } +} + +func TestUpdateAnnotations(t *testing.T) { + type inject = struct { + plugin string + devID string + devices []string + } + type testCase = struct { + name string + existing map[string]string + injections []*inject + annotations map[string]string + parsed []string + invalid bool + } + + for _, tc := range []*testCase{ + { + name: "one plugin, one device", + injections: []*inject{ + { + plugin: "vendor.class", + devID: "device", + devices: []string{ + "vendor.com/class=device", + }, + }, + }, + annotations: map[string]string{ + AnnotationPrefix + "vendor.class_device": "vendor.com/class=device", + }, + parsed: []string{ + "vendor.com/class=device", + }, + }, + { + name: "one plugin, multiple devices", + injections: []*inject{ + { + plugin: "vendor.class", + devID: "device", + devices: []string{ + "vendor.com/class=device1", + "vendor.com/class=device2", + "vendor.com/class=device3", + }, + }, + }, + annotations: map[string]string{ + AnnotationPrefix + "vendor.class_device": "vendor.com/class=device1,vendor.com/class=device2,vendor.com/class=device3", + }, + parsed: []string{ + "vendor.com/class=device1", + "vendor.com/class=device2", + "vendor.com/class=device3", + }, + }, + { + name: "multiple plugins, multiple devices", + injections: []*inject{ + { + plugin: "vendor1.class", + devID: "device1", + devices: []string{ + "vendor1.com/class=device1", + }, + }, + { + plugin: "vendor1.class", + devID: "device2", + devices: []string{ + "vendor2.com/class=device1", + "vendor2.com/class=device2", + }, + }, + { + plugin: "vendor3.class2", + devID: "device", + devices: []string{ + "vendor3.com/class2=device1", + "vendor3.com/class2=device2", + "vendor3.com/class2=device3", + }, + }, + }, + annotations: map[string]string{ + AnnotationPrefix + "vendor1.class_device1": "vendor1.com/class=device1", + AnnotationPrefix + "vendor1.class_device2": "vendor2.com/class=device1,vendor2.com/class=device2", + AnnotationPrefix + "vendor3.class2_device": "vendor3.com/class2=device1,vendor3.com/class2=device2,vendor3.com/class2=device3", + }, + parsed: []string{ + "vendor1.com/class=device1", + "vendor2.com/class=device1", + "vendor2.com/class=device2", + "vendor3.com/class2=device1", + "vendor3.com/class2=device2", + "vendor3.com/class2=device3", + }, + }, + { + name: "invalid, empty plugin", + injections: []*inject{ + { + plugin: "vendor1.class", + devID: "device", + devices: []string{ + "vendor1.com/class=device1", + }, + }, + { + plugin: "vendor2.class", + devID: "device", + devices: []string{ + "vendor2.com/class=device1", + "vendor2.com/class=device2", + }, + }, + { + plugin: "", + devID: "device", + devices: []string{ + "vendor3.com/class2=device1", + "vendor3.com/class2=device2", + "vendor3.com/class2=device3", + }, + }, + }, + invalid: true, + }, + { + name: "invalid, malformed device reference", + injections: []*inject{ + { + plugin: "vendor1.class", + devID: "device", + devices: []string{ + "vendor1.com/class=device1", + }, + }, + { + plugin: "vendor2.class", + devID: "device", + devices: []string{ + "vendor2.com/class=device1", + "vendor2.com/device2", + }, + }, + { + plugin: "vendor3.class2", + devID: "device", + devices: []string{ + "vendor3.com/class2=device1", + "vendor3.com/class2=device2", + "vendor3.com/class2=device3", + }, + }, + }, + invalid: true, + }, + { + name: "invalid, pre-resolved device", + injections: []*inject{ + { + plugin: "vendor1.class", + devID: "device", + devices: []string{ + "vendor1.com/class=device1", + }, + }, + { + plugin: "vendor2.class", + devID: "device", + devices: []string{ + "vendor2.com/class=device1", + "vendor2.com/class=device2", + }, + }, + { + plugin: "vendor3.class2", + devID: "device", + devices: []string{ + "vendor3.com/class2=device1", + "vendor3.com/class2=device2", + "/dev/null", + }, + }, + }, + invalid: true, + }, + { + name: "invalid, conflicting keys", + existing: map[string]string{ + AnnotationPrefix + "vendor3.class2_device": "vendor3.com/class2=device0", + }, + injections: []*inject{ + { + plugin: "vendor1.class", + devID: "device", + devices: []string{ + "vendor1.com/class=device1", + }, + }, + { + plugin: "vendor2.class", + devID: "device", + devices: []string{ + "vendor2.com/class=device1", + "vendor2.com/class=device2", + }, + }, + { + plugin: "vendor3.class2", + devID: "device", + devices: []string{ + "vendor3.com/class2=device1", + "vendor3.com/class2=device2", + }, + }, + }, + invalid: true, + }, + } { + t.Run(tc.name, func(t *testing.T) { + var ( + annotations map[string]string + parsed []string + err error + ) + for _, i := range tc.injections { + if tc.existing != nil { + annotations = tc.existing + } + annotations, err = UpdateAnnotations(annotations, i.plugin, i.devID, i.devices) + if !tc.invalid { + require.NoError(t, err, "CDI device injection annotation") + } else { + if err != nil { + break + } + } + } + if tc.invalid { + require.Error(t, err, "invalid injection") + } else { + require.Equal(t, tc.annotations, annotations) + _, parsed, err = ParseAnnotations(annotations) + require.NoError(t, err, "annotation parsing") + sort.Strings(tc.parsed) + sort.Strings(parsed) + require.Equal(t, tc.parsed, parsed, "parsed annotations") + } + }) + } +} + +func TestParseAnnotation(t *testing.T) { + type testCase = struct { + name string + annotations map[string]string + devices []string + invalid bool + } + + for _, tc := range []*testCase{ + { + name: "one vendor, one device", + annotations: map[string]string{ + AnnotationPrefix + "vendor.class_device": "vendor.com/class=device1", + }, + devices: []string{ + "vendor.com/class=device1", + }, + }, + { + name: "one vendor, multiple devices", + annotations: map[string]string{ + AnnotationPrefix + "vendor.class_device": "vendor.com/class=device1,vendor.com/class=device2,vendor.com/class=device3", + }, + devices: []string{ + "vendor.com/class=device1", + "vendor.com/class=device2", + "vendor.com/class=device3", + }, + }, + + { + name: "one plugin, one device", + annotations: map[string]string{ + AnnotationPrefix + "vendor.class_device": "vendor.com/class=device", + }, + devices: []string{ + "vendor.com/class=device", + }, + }, + { + name: "one plugin, multiple devices", + annotations: map[string]string{ + AnnotationPrefix + "vendor.class_device": "vendor.com/class=device1,vendor.com/class=device2,vendor.com/class=device3", + }, + devices: []string{ + "vendor.com/class=device1", + "vendor.com/class=device2", + "vendor.com/class=device3", + }, + }, + { + name: "multiple plugins, multiple devices", + annotations: map[string]string{ + AnnotationPrefix + "vendor1.class_device": "vendor1.com/class=device1", + AnnotationPrefix + "vendor2.class_device": "vendor2.com/class=device1,vendor2.com/class=device2", + AnnotationPrefix + "vendor3.class2_device": "vendor3.com/class2=device1,vendor3.com/class2=device2,vendor3.com/class2=device3", + }, + devices: []string{ + "vendor1.com/class=device1", + "vendor2.com/class=device1", + "vendor2.com/class=device2", + "vendor3.com/class2=device1", + "vendor3.com/class2=device2", + "vendor3.com/class2=device3", + }, + }, + { + name: "invalid, malformed device reference", + annotations: map[string]string{ + AnnotationPrefix + "vendor1.class1_device": "vendor1.com/class1=device1", + AnnotationPrefix + "vendor2.class2_device": "vendor2.com/class2=device2", + AnnotationPrefix + "vendor3.class_device": "vendor3.com=device3", + }, + invalid: true, + }, + { + name: "invalid, pre-resolved device", + annotations: map[string]string{ + AnnotationPrefix + "vendor1.class2_device": "vendor1.com/class2=device1,vendor1.com/class2=device2", + AnnotationPrefix + "vendor2.class_device": "/dev/null", + }, + invalid: true, + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, devices, err := ParseAnnotations(tc.annotations) + if !tc.invalid { + require.NoError(t, err, "parsing annotations") + sort.Strings(tc.devices) + sort.Strings(devices) + require.Equal(t, tc.devices, devices, "parsing annotations") + } else { + require.Error(t, err) + } + }) + } +} From 10f8f04a3ddccbb651a21367cb27ab90764045b3 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Sat, 22 Jan 2022 01:48:14 +0200 Subject: [PATCH 3/3] go.{mod,sum}: update dependencies. Signed-off-by: Krisztian Litkey --- go.mod | 35 +++++++++++------------ go.sum | 89 ++++++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 77 insertions(+), 47 deletions(-) diff --git a/go.mod b/go.mod index 62ff624..42bcbf7 100644 --- a/go.mod +++ b/go.mod @@ -3,28 +3,25 @@ module github.com/container-orchestrated-devices/container-device-interface go 1.14 require ( - github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 // indirect github.com/blang/semver v3.5.1+incompatible // indirect - github.com/coreos/etcd v3.3.10+incompatible // indirect - github.com/coreos/go-etcd v2.0.0+incompatible // indirect - github.com/cpuguy83/go-md2man v1.0.10 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/checkpoint-restore/go-criu/v5 v5.3.0 // indirect + github.com/cilium/ebpf v0.7.0 // indirect + github.com/containerd/console v1.0.3 // indirect + github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/opencontainers/runtime-spec v1.0.2 - github.com/opencontainers/runtime-tools v0.0.0-20190417131837-cd1349b7c47e // indirect + github.com/godbus/dbus/v5 v5.0.6 // indirect + github.com/hashicorp/go-multierror v1.1.1 + github.com/moby/sys/mountinfo v0.5.0 // indirect + github.com/opencontainers/runc v1.0.3 + github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 + github.com/opencontainers/runtime-tools v0.0.0-20190417131837-cd1349b7c47e github.com/opencontainers/selinux v1.10.0 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect - github.com/spf13/cobra v1.2.1 // indirect - github.com/spf13/viper v1.8.1 // indirect + github.com/pkg/errors v0.9.1 + github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921 // indirect + github.com/spf13/cobra v1.2.1 github.com/stretchr/testify v1.7.0 - github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect - github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 // indirect github.com/xeipuuv/gojsonschema v1.2.0 - github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 // indirect - gopkg.in/fsnotify.v1 v1.4.7 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c // indirect + gopkg.in/fsnotify.v1 v1.4.7 + sigs.k8s.io/yaml v1.3.0 ) diff --git a/go.sum b/go.sum index 6c2c894..76a19f3 100644 --- a/go.sum +++ b/go.sum @@ -41,31 +41,45 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8= +github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.7.0 h1:1k/q3ATgxSXRdrmPfH8d7YK0GfqVsEKZAX9dQZvs56k= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -74,6 +88,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= 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= @@ -82,6 +97,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -164,7 +181,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -182,36 +198,43 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 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 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= +github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/opencontainers/runc v1.0.3 h1:1hbqejyQWCJBvtKAfdO0b1FmaEf2z/bxnjqbARass5k= +github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runc v1.1.0 h1:O9+X96OcDjkmmZyfaG996kV7yq8HsoU2h1XRRQcefG8= +github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= 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.9.0 h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU= -github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/opencontainers/selinux v1.10.0 h1:rAiKF8hTcgLI3w0DHm6i0ylVVcOrlgR1kK99DRLDhyU= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -223,49 +246,50 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 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.2-0.20210429002308-3879420cc921 h1:58EBmR2dMNL2n/FnbQewK3D14nXr0V9CObDSvMJLq+Y= +github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -358,9 +382,11 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -394,12 +420,12 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777 h1:wejkGHRTr38uaKRqECZlsCsJ1/TGxIyFbH32x5zUdu4= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -417,20 +443,26 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c h1:DHcbWVXeY+0Y8HHKR+rbLwnoh2F4tNCY7rTiHJ30RmA= +golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -438,7 +470,6 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -600,12 +631,14 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=