Skip to content

Commit 711b138

Browse files
committed
pkg/cdi,schema: add schema-based Spec validation.
Add boilerplate code for load-time Spec file validation using a built-in or externally-provided JSON schema. Validate Spec files during loading. Signed-off-by: Krisztian Litkey <[email protected]>
1 parent 4adc5b7 commit 711b138

File tree

5 files changed

+336
-14
lines changed

5 files changed

+336
-14
lines changed

pkg/cdi/doc.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,24 @@
127127
// The default directories are '/etc/cdi' and '/var/run/cdi'. By putting
128128
// dynamically generated Spec files under '/var/run/cdi', those take
129129
// precedence over static ones in '/etc/cdi'.
130+
//
131+
// CDI Spec Validation
132+
//
133+
// This package performs both syntactic and semantic validation of CDI
134+
// Spec file data when a Spec file is loaded via the registry or using
135+
// the ReadSpec API function. As part of the semantic verification, the
136+
// Spec file is verified against the CDI Spec JSON validation schema.
137+
//
138+
// If a valid externally provided JSON validation schema is found in
139+
// the filesystem at /etc/cdi/schema/schema.json it is loaded and used
140+
// as the default validation schema. If such a file is not found or
141+
// fails to load, an embedded no-op schema is used.
142+
//
143+
// The used validation schema can also be changed programmatically using
144+
// the SetSchema API convenience function. This function also accepts
145+
// the special "builtin" (BuiltinSchemaName) and "none" (NoneSchemaName)
146+
// schema names which switch the used schema to the in-repo validation
147+
// schema embedded into the binary or the now default no-op schema
148+
// correspondingly. Other names are interpreted as the path to the actual
149+
/// validation schema to load and use.
130150
package cdi

pkg/cdi/schema.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
Copyright © 2022 The CDI Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cdi
18+
19+
import (
20+
"github.com/container-orchestrated-devices/container-device-interface/schema"
21+
cdi "github.com/container-orchestrated-devices/container-device-interface/specs-go"
22+
)
23+
24+
const (
25+
// DefaultExternalSchema is the JSON schema to load if found.
26+
DefaultExternalSchema = "/etc/cdi/schema/schema.json"
27+
)
28+
29+
// SetSchema sets the Spec JSON validation schema to use.
30+
func SetSchema(name string) error {
31+
s, err := schema.Load(name)
32+
if err != nil {
33+
return err
34+
}
35+
schema.Set(s)
36+
return nil
37+
}
38+
39+
// Validate CDI Spec against JSON Schema.
40+
func validateWithSchema(raw *cdi.Spec) error {
41+
return schema.ValidateType(raw)
42+
}
43+
44+
func init() {
45+
SetSchema(DefaultExternalSchema)
46+
}

pkg/cdi/spec.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,23 @@ func ReadSpec(path string, priority int) (*Spec, error) {
7272
return nil, errors.Errorf("failed to parse CDI Spec %q, no Spec data", path)
7373
}
7474

75-
return NewSpec(raw, path, priority)
75+
spec, err := NewSpec(raw, path, priority)
76+
if err != nil {
77+
return nil, err
78+
}
79+
80+
return spec, nil
7681
}
7782

7883
// NewSpec creates a new Spec from the given CDI Spec data. The
7984
// Spec is marked as loaded from the given path with the given
8085
// priority. If Spec data validation fails NewSpec returns a nil
8186
// Spec and an error.
8287
func NewSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) {
83-
var err error
88+
err := validateWithSchema(raw)
89+
if err != nil {
90+
return nil, err
91+
}
8492

8593
spec := &Spec{
8694
Spec: raw,
@@ -178,11 +186,5 @@ func parseSpec(data []byte) (*cdi.Spec, error) {
178186
if err != nil {
179187
return nil, errors.Wrap(err, "failed to unmarshal CDI Spec")
180188
}
181-
return raw, validateJSONSchema(raw)
182-
}
183-
184-
// Validate CDI Spec against JSON Schema.
185-
func validateJSONSchema(raw *cdi.Spec) error {
186-
// TODO
187-
return nil
189+
return raw, nil
188190
}

pkg/cdi/spec_test.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ func TestNewSpec(t *testing.T) {
101101
name string
102102
data string
103103
unparsable bool
104+
schemaFail bool
104105
invalid bool
105106
}
106107
for _, tc := range []*testCase{
@@ -121,7 +122,7 @@ devices:
121122
env:
122123
- "FOO=BAR"
123124
`,
124-
invalid: true,
125+
schemaFail: true,
125126
},
126127
{
127128
name: "invalid, invalid CDI version",
@@ -146,7 +147,7 @@ devices:
146147
env:
147148
- "FOO=BAR"
148149
`,
149-
invalid: true,
150+
schemaFail: true,
150151
},
151152
{
152153
name: "invalid, invalid vendor",
@@ -175,14 +176,14 @@ devices:
175176
invalid: true,
176177
},
177178
{
178-
name: "invalid, invalid device",
179+
name: "invalid, missing required edits",
179180
data: `
180181
cdiVersion: "0.3.0"
181182
kind: vendor.com/device
182183
devices:
183184
- name: "dev1"
184185
`,
185-
invalid: true,
186+
schemaFail: true,
186187
},
187188
{
188189
name: "invalid, conflicting devices",
@@ -241,7 +242,7 @@ devices:
241242
require.NoError(t, err)
242243

243244
spec, err = NewSpec(raw, tc.name, 0)
244-
if tc.invalid {
245+
if tc.invalid || tc.schemaFail {
245246
require.Error(t, err)
246247
require.Nil(t, spec)
247248
return

0 commit comments

Comments
 (0)