mirror of
https://github.com/NVIDIA/nvidia-container-toolkit
synced 2024-11-22 00:08:11 +00:00
Add top-level GetSpec function to nvcdi API
Signed-off-by: Evan Lezar <elezar@nvidia.com>
This commit is contained in:
parent
6d6cd56196
commit
89321edae6
@ -187,6 +187,7 @@ func (m command) run(c *cli.Context, cfg *config) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to convert CDI spec from YAML to JSON: %v", err)
|
return fmt.Errorf("failed to convert CDI spec from YAML to JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = writeToOutput(cfg.format, data, outputTo)
|
err = writeToOutput(cfg.format, data, outputTo)
|
||||||
|
@ -98,7 +98,11 @@ func (l *wrapper) GetSpec() (spec.Interface, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return spec.New(deviceSpecs, *edits.ContainerEdits)
|
return spec.New(
|
||||||
|
spec.WithDeviceSpecs(deviceSpecs),
|
||||||
|
spec.WithEdits(*edits.ContainerEdits),
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveMode resolves the mode for CDI spec generation based on the current system.
|
// resolveMode resolves the mode for CDI spec generation based on the current system.
|
||||||
|
@ -16,10 +16,20 @@
|
|||||||
|
|
||||||
package spec
|
package spec
|
||||||
|
|
||||||
import "github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DetectMinimumVersion is a constant that triggers a spec to detect the minimum required version.
|
||||||
|
DetectMinimumVersion = "DETECT_MINIMUM_VERSION"
|
||||||
|
)
|
||||||
|
|
||||||
// Interface is the interface for the spec API
|
// Interface is the interface for the spec API
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
|
io.WriterTo
|
||||||
Save(string) error
|
Save(string) error
|
||||||
Raw() *specs.Spec
|
Raw() *specs.Spec
|
||||||
}
|
}
|
||||||
|
127
pkg/nvcdi/spec/builder.go
Normal file
127
pkg/nvcdi/spec/builder.go
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/**
|
||||||
|
# Copyright (c) NVIDIA CORPORATION. All rights reserved.
|
||||||
|
#
|
||||||
|
# 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 spec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
|
||||||
|
"github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type builder struct {
|
||||||
|
raw *specs.Spec
|
||||||
|
version string
|
||||||
|
vendor string
|
||||||
|
class string
|
||||||
|
deviceSpecs []specs.Device
|
||||||
|
edits specs.ContainerEdits
|
||||||
|
format string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBuilder creates a new spec builder with the supplied options
|
||||||
|
func NewBuilder(opts ...Option) *builder {
|
||||||
|
s := &builder{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(s)
|
||||||
|
}
|
||||||
|
if s.version == "" {
|
||||||
|
s.version = DetectMinimumVersion
|
||||||
|
}
|
||||||
|
if s.vendor == "" {
|
||||||
|
s.vendor = "nvidia.com"
|
||||||
|
}
|
||||||
|
if s.class == "" {
|
||||||
|
s.class = "gpu"
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build builds a CDI spec form the spec builder.
|
||||||
|
func (o *builder) Build() (*spec, error) {
|
||||||
|
raw := o.raw
|
||||||
|
|
||||||
|
if raw == nil {
|
||||||
|
raw = &specs.Spec{
|
||||||
|
Version: o.version,
|
||||||
|
Kind: fmt.Sprintf("%s/%s", o.vendor, o.class),
|
||||||
|
Devices: o.deviceSpecs,
|
||||||
|
ContainerEdits: o.edits,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if raw.Version == DetectMinimumVersion {
|
||||||
|
minVersion, err := cdi.MinimumRequiredVersion(raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get minumum required CDI spec version: %v", err)
|
||||||
|
}
|
||||||
|
raw.Version = minVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
s := spec{
|
||||||
|
Spec: raw,
|
||||||
|
format: o.format,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option defines a function that can be used to configure the spec builder.
|
||||||
|
type Option func(*builder)
|
||||||
|
|
||||||
|
// WithDeviceSpecs sets the device specs for the spec builder
|
||||||
|
func WithDeviceSpecs(deviceSpecs []specs.Device) Option {
|
||||||
|
return func(o *builder) {
|
||||||
|
o.deviceSpecs = deviceSpecs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEdits sets the container edits for the spec builder
|
||||||
|
func WithEdits(edits specs.ContainerEdits) Option {
|
||||||
|
return func(o *builder) {
|
||||||
|
o.edits = edits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithVersion sets the version for the spec builder
|
||||||
|
func WithVersion(version string) Option {
|
||||||
|
return func(o *builder) {
|
||||||
|
o.version = version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithVendor sets the vendor for the spec builder
|
||||||
|
func WithVendor(vendor string) Option {
|
||||||
|
return func(o *builder) {
|
||||||
|
o.vendor = vendor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithClass sets the class for the spec builder
|
||||||
|
func WithClass(class string) Option {
|
||||||
|
return func(o *builder) {
|
||||||
|
o.class = class
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFormat sets the output file format
|
||||||
|
func WithFormat(format string) Option {
|
||||||
|
return func(o *builder) {
|
||||||
|
o.format = format
|
||||||
|
}
|
||||||
|
}
|
@ -17,44 +17,92 @@
|
|||||||
package spec
|
package spec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"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/specs-go"
|
"github.com/container-orchestrated-devices/container-device-interface/specs-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
type spec specs.Spec
|
type spec struct {
|
||||||
|
*specs.Spec
|
||||||
|
format string
|
||||||
|
}
|
||||||
|
|
||||||
var _ Interface = (*spec)(nil)
|
var _ Interface = (*spec)(nil)
|
||||||
|
|
||||||
// New creates a new spec with the specified deivice specs and edits.
|
// New creates a new spec with the specified options.
|
||||||
func New(deviceSpecs []specs.Device, edits specs.ContainerEdits) (Interface, error) {
|
func New(opts ...Option) (Interface, error) {
|
||||||
s := specs.Spec{
|
return NewBuilder(opts...).Build()
|
||||||
// TODO: Should be set through an option
|
|
||||||
Version: "NOT_SET",
|
|
||||||
// TODO: Should be set through an option
|
|
||||||
Kind: "nvidia.com/gpu",
|
|
||||||
// TODO: Should be set through an option
|
|
||||||
Devices: deviceSpecs,
|
|
||||||
// TODO: Should be set through an option
|
|
||||||
ContainerEdits: edits,
|
|
||||||
}
|
|
||||||
|
|
||||||
minVersion, err := cdi.MinimumRequiredVersion(&s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get minumum required CDI spec version: %v", err)
|
|
||||||
}
|
|
||||||
s.Version = minVersion
|
|
||||||
|
|
||||||
return (*spec)(&s), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save writes the spec to the specified path and overwrites the file if it exists.
|
// Save writes the spec to the specified path and overwrites the file if it exists.
|
||||||
func (s *spec) Save(path string) error {
|
func (s *spec) Save(path string) error {
|
||||||
return cdi.WriteSpec(s, path, true)
|
path = s.normalizePath(path)
|
||||||
|
|
||||||
|
specDir := filepath.Dir(path)
|
||||||
|
registry := cdi.GetRegistry(
|
||||||
|
cdi.WithAutoRefresh(false),
|
||||||
|
cdi.WithSpecDirs(specDir),
|
||||||
|
)
|
||||||
|
|
||||||
|
return registry.SpecDB().WriteSpec(s.Raw(), filepath.Base(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo writes the spec to the specified writer.
|
||||||
|
func (s *spec) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
name, err := cdi.GenerateNameForSpec(s.Raw())
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
path := s.normalizePath(name)
|
||||||
|
tmpFile, err := os.CreateTemp("", "*"+filepath.Base(path))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer os.Remove(tmpFile.Name())
|
||||||
|
|
||||||
|
if err := s.Save(tmpFile.Name()); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tmpFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to close temporary file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := os.Open(tmpFile.Name())
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("failed to open temporary file: %w", err)
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
return io.Copy(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raw returns a pointer to the raw spec.
|
// Raw returns a pointer to the raw spec.
|
||||||
func (s *spec) Raw() *specs.Spec {
|
func (s *spec) Raw() *specs.Spec {
|
||||||
return (*specs.Spec)(s)
|
return s.Spec
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalizePath ensures that the specified path has a supported extension
|
||||||
|
func (s *spec) normalizePath(path string) string {
|
||||||
|
if ext := filepath.Ext(path); ext != ".yaml" && ext != ".json" {
|
||||||
|
path += s.extension()
|
||||||
|
}
|
||||||
|
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *spec) extension() string {
|
||||||
|
switch s.format {
|
||||||
|
case "json":
|
||||||
|
return ".json"
|
||||||
|
case "yaml", "yml":
|
||||||
|
return ".yaml"
|
||||||
|
}
|
||||||
|
|
||||||
|
return ".yaml"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user