Skip to content

Commit 0635769

Browse files
authored
Merge pull request #62 from klihub/devel/split-out-schema-validation
Don't force downstream code pulling in (the huge) JSON Schema validation package.
2 parents 4b00593 + 22b6a2a commit 0635769

File tree

5 files changed

+110
-73
lines changed

5 files changed

+110
-73
lines changed

cmd/cdi/cmd/root.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import (
2323
"github.com/spf13/cobra"
2424

2525
"github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
26+
"github.com/container-orchestrated-devices/container-device-interface/pkg/cdi/validate"
27+
"github.com/container-orchestrated-devices/container-device-interface/schema"
2628
)
2729

2830
var (
@@ -58,14 +60,17 @@ func init() {
5860
}
5961

6062
func initSpecDirs() {
61-
err := cdi.SetSchema(schemaName)
63+
s, err := schema.Load(schemaName)
6264
if err != nil {
6365
fmt.Printf("failed to load JSON schema %s: %v\n", schemaName, err)
6466
os.Exit(1)
6567
}
68+
cdi.SetSpecValidator(validate.WithSchema(s))
6669

6770
if len(specDirs) > 0 {
68-
cdi.GetRegistry(cdi.WithSpecDirs(specDirs...))
71+
cdi.GetRegistry(
72+
cdi.WithSpecDirs(specDirs...),
73+
)
6974
if len(cdi.GetRegistry().GetErrors()) > 0 {
7075
cdiPrintRegistryErrors()
7176
}

pkg/cdi/schema.go

Lines changed: 0 additions & 46 deletions
This file was deleted.

pkg/cdi/spec.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ var (
3535
"0.2.0": {},
3636
"0.3.0": {},
3737
}
38+
39+
// Externally set CDI Spec validation function.
40+
specValidator func(*cdi.Spec) error
3841
)
3942

4043
// Spec represents a single CDI Spec. It is usually loaded from a
@@ -85,7 +88,7 @@ func ReadSpec(path string, priority int) (*Spec, error) {
8588
// priority. If Spec data validation fails NewSpec returns a nil
8689
// Spec and an error.
8790
func NewSpec(raw *cdi.Spec, path string, priority int) (*Spec, error) {
88-
err := validateWithSchema(raw)
91+
err := validateSpec(raw)
8992
if err != nil {
9093
return nil, err
9194
}
@@ -188,3 +191,22 @@ func parseSpec(data []byte) (*cdi.Spec, error) {
188191
}
189192
return raw, nil
190193
}
194+
195+
// SetSpecValidator sets a CDI Spec validator function. This function
196+
// is used for extra CDI Spec content validation whenever a Spec file
197+
// loaded (using ReadSpec() or NewSpec()) or written (Spec.Write()).
198+
func SetSpecValidator(fn func(*cdi.Spec) error) {
199+
specValidator = fn
200+
}
201+
202+
// validateSpec validates the Spec using the extneral validator.
203+
func validateSpec(raw *cdi.Spec) error {
204+
if specValidator == nil {
205+
return nil
206+
}
207+
err := specValidator(raw)
208+
if err != nil {
209+
return errors.Wrap(err, "Spec validation failed")
210+
}
211+
return nil
212+
}

pkg/cdi/validate/schema.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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 validate
18+
19+
import (
20+
"github.com/container-orchestrated-devices/container-device-interface/schema"
21+
raw "github.com/container-orchestrated-devices/container-device-interface/specs-go"
22+
)
23+
24+
const (
25+
// DefaultExternalSchema is the default JSON schema to load for validation.
26+
DefaultExternalSchema = "/etc/cdi/schema/schema.json"
27+
)
28+
29+
// WithSchema returns a CDI Spec validator that uses the given Schema.
30+
func WithSchema(s *schema.Schema) func(*raw.Spec) error {
31+
if s == nil {
32+
return func(*raw.Spec) error {
33+
return nil
34+
}
35+
}
36+
return func(spec *raw.Spec) error {
37+
return s.ValidateType(spec)
38+
}
39+
}
40+
41+
// WithNamedSchema loads the named JSON schema and returns a CDI Spec
42+
// validator for it. If loading the schema fails a dummy validator is
43+
// returned.
44+
func WithNamedSchema(name string) func(*raw.Spec) error {
45+
s, _ := schema.Load(name)
46+
return WithSchema(s)
47+
}
48+
49+
// WithDefaultSchema returns a CDI Spec validator that uses the default
50+
// external JSON schema, or the default builtin one if the external one
51+
// fails to load.
52+
func WithDefaultSchema() func(*raw.Spec) error {
53+
s, err := schema.Load(DefaultExternalSchema)
54+
if err == nil {
55+
return WithSchema(s)
56+
}
57+
return WithSchema(schema.BuiltinSchema())
58+
}

schema/schema.go

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,10 @@ import (
3434
)
3535

3636
const (
37-
// BuiltinSchemaName references the builtin schema for Load()/Set().
37+
// BuiltinSchemaName names the builtin schema for Load()/Set().
3838
BuiltinSchemaName = "builtin"
39-
// NoneSchemaName references a disabled/NOP schema for Load()/Set().
39+
// NoneSchemaName names the NOP-schema for Load()/Set().
4040
NoneSchemaName = "none"
41-
// DefaultSchemaName is the none schema.
42-
DefaultSchemaName = NoneSchemaName
43-
4441
// builtinSchemaFile is the builtin schema URI in our embedded FS.
4542
builtinSchemaFile = "file:///schema.json"
4643
)
@@ -65,8 +62,26 @@ func Get() *Schema {
6562
return current
6663
}
6764

68-
// BuiltinSchema returns the builtin validating JSON Schema.
65+
// BuiltinSchema returns the builtin schema if we have a valid one. Otherwise
66+
// it falls back to NopSchema().
6967
func BuiltinSchema() *Schema {
68+
if builtin != nil {
69+
return builtin
70+
}
71+
72+
s, err := schema.NewSchema(
73+
schema.NewReferenceLoaderFileSystem(
74+
builtinSchemaFile,
75+
http.FS(builtinFS),
76+
),
77+
)
78+
79+
if err == nil {
80+
builtin = &Schema{schema: s}
81+
} else {
82+
builtin = NopSchema()
83+
}
84+
7085
return builtin
7186
}
7287

@@ -229,25 +244,8 @@ var (
229244
// our builtin schema
230245
builtin *Schema
231246
// currently loaded schema, builtin by default
232-
current *Schema
247+
current = BuiltinSchema()
233248
)
234249

235250
//go:embed *.json
236251
var builtinFS embed.FS
237-
238-
func init() {
239-
s, err := schema.NewSchema(
240-
schema.NewReferenceLoaderFileSystem(
241-
builtinSchemaFile,
242-
http.FS(builtinFS),
243-
),
244-
)
245-
246-
if err != nil {
247-
builtin = NopSchema()
248-
} else {
249-
builtin = &Schema{schema: s}
250-
}
251-
252-
current = builtin
253-
}

0 commit comments

Comments
 (0)