Skip to content

Commit

Permalink
fix(PL-660): validate crd schemas against file content not deserializ…
Browse files Browse the repository at this point in the history
…ed representation
  • Loading branch information
davidmdm committed Jul 25, 2024
1 parent 655b059 commit ce84b03
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 14 deletions.
2 changes: 1 addition & 1 deletion api/v1alpha1/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (e Environment) Validate(validChartRefs []string) error {
}
}
return xerr.MultiErrOrderedFrom("",
internal.ValidateAgainstSchema(schemas.Environment, e),
internal.ValidateAgainstSchema(schemas.Environment, e.File.Tree),
xerr.MultiErrOrderedFrom("validating chart references", errs...),
)
}
Expand Down
2 changes: 1 addition & 1 deletion api/v1alpha1/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ type Project struct {
}

func (project *Project) Validate() error {
return internal.ValidateAgainstSchema(schemas.Project, project)
return internal.ValidateAgainstSchema(schemas.Project, project.File.Tree)
}

func IsValidProject(apiVersion, kind string) bool {
Expand Down
2 changes: 1 addition & 1 deletion api/v1alpha1/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ type Release struct {
}

func (release Release) Validate() error {
return internal.ValidateAgainstSchema(schemas.Release, release)
return internal.ValidateAgainstSchema(schemas.Release, release.File.Tree)
}

func (release *Release) UnmarshalYAML(node *yaml.Node) error {
Expand Down
43 changes: 34 additions & 9 deletions internal/schema.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package internal

import (
"encoding/json"
"fmt"

"cuelang.org/go/cue"
cueerrors "cuelang.org/go/cue/errors"
"cuelang.org/go/cue/format"
"gopkg.in/yaml.v3"

"github.com/davidmdm/x/xerr"
)
Expand All @@ -24,18 +25,15 @@ func StringifySchema(value cue.Value) string {
return string(out)
}

func ValidateAgainstSchema(schema cue.Value, value any) error {
data, err := json.Marshal(value)
if err != nil {
func ValidateAgainstSchema(schema cue.Value, node *yaml.Node) error {
var value any
if err := node.Decode(&value); err != nil {
return err
}

var generic any
if err := json.Unmarshal(data, &generic); err != nil {
return err
}
value = JsonCompat(value)

baseValue := schema.Context().Encode(generic)
baseValue := schema.Context().Encode(value)
if err := schema.Unify(baseValue).Validate(cue.Final(), cue.Concrete(true)); err != nil {
var errs []error
for _, e := range cueerrors.Errors(err) {
Expand All @@ -46,3 +44,30 @@ func ValidateAgainstSchema(schema cue.Value, value any) error {

return nil
}

// JsonCompat takes a generic as returned from yaml.Unmarshal and returns a new instance with all map[any]any converted to map[string]any
// for compatiblilty with Apis that only support JSON generic objects. (Objects marshalled from json only support string keys).
func JsonCompat(value any) any {
switch value := value.(type) {
case []any:
copy := make([]any, len(value))
for i, elem := range value {
copy[i] = JsonCompat(elem)
}
return copy
case map[string]any:
copy := make(map[string]any, len(value))
for k, v := range value {
copy[k] = JsonCompat(v)
}
return copy
case map[any]any:
copy := make(map[string]any, len(value))
for k, v := range value {
copy[fmt.Sprint(k)] = JsonCompat(v)
}
return copy
default:
return value
}
}
8 changes: 6 additions & 2 deletions internal/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
)

func TestValidateAgainstSchema(t *testing.T) {
Expand All @@ -24,13 +25,16 @@ func TestValidateAgainstSchema(t *testing.T) {
Name: "is invalid",
Schema: cuecontext.New().CompileString(`string`),
Input: 3.14159265,
Err: "error: conflicting values string and 3.14159265 (mismatched types string and float)",
Err: "error: conflicting values string and 3.14159265 (mismatched types string and float)",
},
}

for _, tc := range cases {
t.Run(tc.Name, func(t *testing.T) {
err := ValidateAgainstSchema(tc.Schema, tc.Input)
var node yaml.Node
require.NoError(t, node.Encode(tc.Input))

err := ValidateAgainstSchema(tc.Schema, &node)
if tc.Err == "" {
require.NoError(t, err)
return
Expand Down

0 comments on commit ce84b03

Please sign in to comment.