From 93a7e7a539ef9d351b4078dfe7ae3eb3281ac2ea Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Tue, 9 Aug 2022 10:49:27 +0300 Subject: [PATCH] pkg/cdi: add functions for Spec name generation. Add convenience functions for generating names for ordinary and transient Spec files and for removing Spec files created with WriteSpec(). Signed-off-by: Krisztian Litkey --- pkg/cdi/cache.go | 51 ++++++++++++++++++++++++++++++++++++++++--- pkg/cdi/registry.go | 1 + pkg/cdi/spec.go | 53 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 3 deletions(-) diff --git a/pkg/cdi/cache.go b/pkg/cdi/cache.go index 7db851f..8ed4ab5 100644 --- a/pkg/cdi/cache.go +++ b/pkg/cdi/cache.go @@ -17,11 +17,15 @@ package cdi import ( + "io/fs" + "os" "path/filepath" "sort" "strings" "sync" + stderr "errors" + cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go" "github.com/fsnotify/fsnotify" "github.com/hashicorp/go-multierror" @@ -255,6 +259,19 @@ func (c *Cache) InjectDevices(ociSpec *oci.Spec, devices ...string) ([]string, e return nil, nil } +// highestPrioritySpecDir returns the Spec directory with highest priority +// and its priority. +func (c *Cache) highestPrioritySpecDir() (string, int) { + if len(c.specDirs) == 0 { + return "", -1 + } + + prio := len(c.specDirs) - 1 + dir := c.specDirs[prio] + + return dir, prio +} + // WriteSpec writes a Spec file with the given content into the highest // priority Spec directory. If name has a "json" or "yaml" extension it // choses the encoding. Otherwise JSON encoding is used with a "json" @@ -268,12 +285,11 @@ func (c *Cache) WriteSpec(raw *cdi.Spec, name string) error { err error ) - if len(c.specDirs) == 0 { + specDir, prio = c.highestPrioritySpecDir() + if specDir == "" { return errors.New("no Spec directories to write to") } - prio = len(c.specDirs) - 1 - specDir = c.specDirs[prio] path = filepath.Join(specDir, name) if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" { path += ".json" @@ -287,6 +303,35 @@ func (c *Cache) WriteSpec(raw *cdi.Spec, name string) error { return spec.Write(true) } +// RemoveSpec removes a Spec with the given name from the highest +// priority Spec directory. This function can be used to remove a +// Spec previosuly written by WriteSpec() using the same. If the +// file exists and its removal fails RemoveSpec returns an error. +func (c *Cache) RemoveSpec(name string) error { + var ( + specDir string + path string + err error + ) + + specDir, _ = c.highestPrioritySpecDir() + if specDir == "" { + return errors.New("no Spec directories to remove from") + } + + path = filepath.Join(specDir, name) + if ext := filepath.Ext(path); ext != ".json" && ext != ".yaml" { + path += ".json" + } + + err = os.Remove(path) + if err != nil && stderr.Is(err, fs.ErrNotExist) { + err = nil + } + + return err +} + // GetDevice returns the cached device for the given qualified name. func (c *Cache) GetDevice(device string) *Device { c.Lock() diff --git a/pkg/cdi/registry.go b/pkg/cdi/registry.go index d5bd54b..10fab89 100644 --- a/pkg/cdi/registry.go +++ b/pkg/cdi/registry.go @@ -107,6 +107,7 @@ type RegistrySpecDB interface { GetVendorSpecs(vendor string) []*Spec GetSpecErrors(*Spec) []error WriteSpec(raw *cdi.Spec, name string) error + RemoveSpec(name string) error } type registry struct { diff --git a/pkg/cdi/spec.go b/pkg/cdi/spec.go index 50fd37e..926283d 100644 --- a/pkg/cdi/spec.go +++ b/pkg/cdi/spec.go @@ -262,3 +262,56 @@ func validateSpec(raw *cdi.Spec) error { } return nil } + +// GetSpecFileBase returns a Spec file basename. +func GetSpecFileBase(vendor, class, tag string) string { + if tag != "" { + return vendor + "-" + class + "-" + tag + } + return vendor + "-" + class + +} + +// GenerateSpecName generates a vendor+class scoped Spec file name. The +// name can be passed to WriteSpec() to write a Spec file to the file +// system. +// +// vendor and class should match the vendor and class of the CDI Spec. +// The file name is generated without a ".json" or ".yaml" extension. +// The caller can append the desired extension to choose a particular +// encoding. Otherwise WriteSpec() will use its default encoding. +// +// This function always returns the same name for the same vendor/class +// combination. Therefore it cannot be used as such to generate multiple +// Spec file names for a single vendor and class. +func GenerateSpecName(vendor, class string) string { + return vendor + "-" + class +} + +// GenerateTransientSpecName generates a vendor+class scoped transient +// Spec file name. The name can be passed to WriteSpec() to write a Spec +// file to the file system. + +// Transient Specs are those whose lifecycle is tied to that of some +// external entity, for instance a container. vendor and class should +// match the vendor and class of the CDI Spec. driver can be left empty +// if only a single CDI user is generating transient Spec files for the +// given vendor/class combination on the host. Otherwise driver should +// uniquely identify the caller among those that use the same vendor +// and class. transientID should uniquely identify the external entity +// within the scope of the caller/driver. +// +// The file name is generated without a ".json" or ".yaml" extension. +// The caller can append the desired extension to choose a particular +// encoding. Otherwise WriteSpec() will use its default encoding. +func GenerateTransientSpecName(vendor, class, driver, transientID string) string { + name := vendor + "-" + class + + if driver != "" { + name += "+" + driver + } + + name = "transient." + name + "_" + transientID + + return name +}