Skip to content

Commit a59f077

Browse files
committed
Default to strict decoding of OCI runtime spec
This change defaults to strict decoding of the OCI runtime spec to avoid dropping unknown fields when making OCI runtime modifications. We also add an allow-unknown-oci-spec-fields feature flag that can be used to opt-out of this behaviour. Signed-off-by: Evan Lezar <[email protected]>
1 parent e994959 commit a59f077

File tree

6 files changed

+41
-13
lines changed

6 files changed

+41
-13
lines changed

internal/config/features.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ type features struct {
2525
// If this feature flag is not set to 'true' only host-rooted config paths
2626
// (i.e. paths starting with an '@' are considered valid)
2727
AllowLDConfigFromContainer *feature `toml:"allow-ldconfig-from-container,omitempty"`
28+
// AllowUnknownOCISpecFields allows the nvidia-container-runtime to ignore
29+
// unknown fields when loading the config (OCI spec) associated with a
30+
// container.
31+
// If this is enabled, these fields are silently dropped.
32+
AllowUnknownOCISpecFields *feature `toml:"allow-unknown-oci-spec-fields,omitempty"`
2833
// DisableCUDACompatLibHook, when enabled skips the injection of a specific
2934
// hook to process CUDA compatibility libraries.
3035
//

internal/oci/spec.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ import (
2020
"fmt"
2121

2222
"github.com/opencontainers/runtime-spec/specs-go"
23-
24-
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
2523
)
2624

2725
// SpecModifier defines an interface for modifying a (raw) OCI spec
@@ -49,17 +47,24 @@ type Spec interface {
4947

5048
// NewSpec creates fileSpec based on the command line arguments passed to the
5149
// application using the specified logger.
52-
func NewSpec(logger logger.Interface, args []string) (Spec, error) {
50+
func NewSpec(args []string, opts ...Option) (Spec, error) {
51+
o := &options{
52+
allowUnkownFields: false,
53+
}
54+
for _, opt := range opts {
55+
opt(o)
56+
}
57+
5358
bundleDir, err := GetBundleDir(args)
5459
if err != nil {
5560
return nil, fmt.Errorf("error getting bundle directory: %v", err)
5661
}
57-
logger.Debugf("Using bundle directory: %v", bundleDir)
62+
o.logger.Debugf("Using bundle directory: %v", bundleDir)
5863

5964
ociSpecPath := GetSpecFilePath(bundleDir)
60-
logger.Infof("Using OCI specification file path: %v", ociSpecPath)
65+
o.logger.Infof("Using OCI specification file path: %v", ociSpecPath)
6166

62-
ociSpec := NewFileSpec(ociSpecPath)
67+
ociSpec := NewFileSpec(ociSpecPath, !o.allowUnkownFields)
6368

6469
return ociSpec, nil
6570
}

internal/oci/spec_file.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,28 @@ import (
2828
type fileSpec struct {
2929
memorySpec
3030
path string
31+
loader
3132
}
3233

34+
type loader bool
35+
36+
const (
37+
strictLoader = loader(true)
38+
)
39+
3340
var _ Spec = (*fileSpec)(nil)
3441

3542
// NewFileSpec creates an object that encapsulates a file-backed OCI spec.
3643
// This can be used to read from the file, modify the spec, and write to the
3744
// same file.
38-
func NewFileSpec(filepath string) Spec {
45+
func NewFileSpec(filepath string, isStrict bool) Spec {
46+
var loader loader
47+
if isStrict {
48+
loader = strictLoader
49+
}
3950
oci := fileSpec{
40-
path: filepath,
51+
path: filepath,
52+
loader: loader,
4153
}
4254

4355
return &oci
@@ -52,7 +64,7 @@ func (s *fileSpec) Load() (*specs.Spec, error) {
5264
}
5365
defer specFile.Close()
5466

55-
spec, err := LoadFrom(specFile)
67+
spec, err := s.loadFrom(specFile)
5668
if err != nil {
5769
return nil, fmt.Errorf("error loading OCI specification from file: %v", err)
5870
}
@@ -61,8 +73,11 @@ func (s *fileSpec) Load() (*specs.Spec, error) {
6173
}
6274

6375
// LoadFrom reads the contents of the OCI spec from the specified io.Reader.
64-
func LoadFrom(reader io.Reader) (*specs.Spec, error) {
76+
func (isStrict loader) loadFrom(reader io.Reader) (*specs.Spec, error) {
6577
decoder := json.NewDecoder(reader)
78+
if isStrict {
79+
decoder.DisallowUnknownFields()
80+
}
6681

6782
var spec specs.Spec
6883

internal/oci/spec_file_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func TestLoadFrom(t *testing.T) {
4444

4545
for i, tc := range testCases {
4646
var spec *specs.Spec
47-
spec, err := LoadFrom(bytes.NewReader(tc.contents))
47+
spec, err := strictLoader.loadFrom(bytes.NewReader(tc.contents))
4848

4949
if tc.isError {
5050
require.Error(t, err, "%d: %v", i, tc)

internal/oci/spec_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func TestMaintainSpec(t *testing.T) {
2121
for _, f := range files {
2222
inputSpecPath := filepath.Join(moduleRoot, "tests/input", f)
2323

24-
spec := NewFileSpec(inputSpecPath).(*fileSpec)
24+
spec := NewFileSpec(inputSpecPath, true).(*fileSpec)
2525

2626
_, err := spec.Load()
2727
require.NoError(t, err)

internal/runtime/runtime_factory.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ func newNVIDIAContainerRuntime(logger logger.Interface, cfg *config.Config, argv
4242
return lowLevelRuntime, nil
4343
}
4444

45-
ociSpec, err := oci.NewSpec(logger, argv)
45+
ociSpec, err := oci.NewSpec(argv,
46+
oci.WithLogger(logger),
47+
oci.WithAllowUnknownFields(cfg.Features.AllowUnknownOCISpecFields.IsEnabled()),
48+
)
4649
if err != nil {
4750
return nil, fmt.Errorf("error constructing OCI specification: %v", err)
4851
}

0 commit comments

Comments
 (0)