Skip to content

Commit de704e3

Browse files
committed
Wrap CRD code closer together and test it
Signed-off-by: jose.vazquez <[email protected]>
1 parent 7c8b9e9 commit de704e3

File tree

9 files changed

+141
-109
lines changed

9 files changed

+141
-109
lines changed
Lines changed: 13 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
14-
//
1514

16-
package translate
15+
package crds
1716

1817
import (
1918
"bytes"
@@ -24,64 +23,13 @@ import (
2423
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2524
)
2625

27-
// crdTranslator implements Translator to translate from a given CRD to and from
28-
// a given SDK version using the same upstream OpenAPI schema
29-
type crdTranslator struct {
30-
majorVersion string
31-
jsonSchema *jsonschema.Schema
32-
annotations map[string]string
33-
}
34-
35-
func (tr *crdTranslator) Annotation(annotation string) string {
36-
return tr.annotations[annotation]
37-
}
38-
39-
func (tr *crdTranslator) MajorVersion() string {
40-
return tr.majorVersion
41-
}
42-
43-
func (tr *crdTranslator) Validate(unstructuredObj map[string]any) error {
44-
if err := tr.jsonSchema.Validate(unstructuredObj); err != nil {
45-
return fmt.Errorf("object validation failed against CRD schema: %w", err)
46-
}
47-
return nil
48-
}
49-
50-
// NewTranslator creates a translator for a particular CRD and major version pairs,
51-
// and with a particular set of known Kubernetes object dependencies.
52-
//
53-
// Given the following example resource:
54-
//
55-
// apiVersion: atlas.generated.mongodb.com/v1
56-
// kind: SearchIndex
57-
// metadata:
58-
// name: search-index
59-
// spec:
60-
// v20250312:
61-
//
62-
// In the above case crdVersion is "v1" and majorVersion is "v20250312".
63-
func NewTranslator(crd *apiextensionsv1.CustomResourceDefinition, crdVersion string, majorVersion string) (Translator, error) {
64-
specVersion := selectVersion(&crd.Spec, crdVersion)
65-
if err := assertMajorVersion(specVersion, crd.Spec.Names.Kind, majorVersion); err != nil {
66-
return nil, fmt.Errorf("failed to assert major version %s in CRD: %w", majorVersion, err)
67-
}
68-
schema, err := compileCRDSchema(specVersion.Schema.OpenAPIV3Schema)
69-
if err != nil {
70-
return nil, fmt.Errorf("failed to compile schema: %w", err)
71-
}
72-
return &crdTranslator{
73-
majorVersion: majorVersion,
74-
jsonSchema: schema,
75-
annotations: crd.Annotations,
76-
}, nil
77-
}
78-
79-
func assertMajorVersion(specVersion *apiextensionsv1.CustomResourceDefinitionVersion, kind string, majorVersion string) error {
80-
props, err := getOpenAPIProperties(kind, specVersion)
26+
// AssertMajorVersion checks the given majorVersion exists for the given kind and CRD version
27+
func AssertMajorVersion(specVersion *apiextensionsv1.CustomResourceDefinitionVersion, kind string, majorVersion string) error {
28+
props, err := GetOpenAPIProperties(kind, specVersion)
8129
if err != nil {
8230
return fmt.Errorf("failed to enumerate CRD schema properties: %w", err)
8331
}
84-
specProps, err := getSpecPropertiesFor(kind, props, "spec")
32+
specProps, err := GetSpecPropertiesFor(kind, props, "spec")
8533
if err != nil {
8634
return fmt.Errorf("failed to enumerate CRD spec properties: %w", err)
8735
}
@@ -92,7 +40,8 @@ func assertMajorVersion(specVersion *apiextensionsv1.CustomResourceDefinitionVer
9240
return nil
9341
}
9442

95-
func compileCRDSchema(openAPISchema *apiextensionsv1.JSONSchemaProps) (*jsonschema.Schema, error) {
43+
// CompileCRDSchema compiles the given JSON schema properties
44+
func CompileCRDSchema(openAPISchema *apiextensionsv1.JSONSchemaProps) (*jsonschema.Schema, error) {
9645
schemaBytes, err := json.Marshal(openAPISchema)
9746
if err != nil {
9847
return nil, fmt.Errorf("failed to marshal CRD schema to JSON: %w", err)
@@ -108,8 +57,8 @@ func compileCRDSchema(openAPISchema *apiextensionsv1.JSONSchemaProps) (*jsonsche
10857
return schema, nil
10958
}
11059

111-
// selectVersion returns the version from the CRD spec that matches the given version string
112-
func selectVersion(spec *apiextensionsv1.CustomResourceDefinitionSpec, version string) *apiextensionsv1.CustomResourceDefinitionVersion {
60+
// SelectVersion returns the version from the CRD spec that matches the given version string
61+
func SelectVersion(spec *apiextensionsv1.CustomResourceDefinitionSpec, version string) *apiextensionsv1.CustomResourceDefinitionVersion {
11362
if len(spec.Versions) == 0 {
11463
return nil
11564
}
@@ -124,7 +73,8 @@ func selectVersion(spec *apiextensionsv1.CustomResourceDefinitionSpec, version s
12473
return nil
12574
}
12675

127-
func getOpenAPIProperties(kind string, version *apiextensionsv1.CustomResourceDefinitionVersion) (map[string]apiextensionsv1.JSONSchemaProps, error) {
76+
// GetOpenAPIProperties digs up the schema properties of a given kind on a given CRD version
77+
func GetOpenAPIProperties(kind string, version *apiextensionsv1.CustomResourceDefinitionVersion) (map[string]apiextensionsv1.JSONSchemaProps, error) {
12878
if version == nil {
12979
return nil, fmt.Errorf("missing version (nil) from %v spec", kind)
13080
}
@@ -140,7 +90,8 @@ func getOpenAPIProperties(kind string, version *apiextensionsv1.CustomResourceDe
14090
return version.Schema.OpenAPIV3Schema.Properties, nil
14191
}
14292

143-
func getSpecPropertiesFor(kind string, props map[string]apiextensionsv1.JSONSchemaProps, field string) (map[string]apiextensionsv1.JSONSchemaProps, error) {
93+
// GetSpecPropertiesFor takes the properties value of a given field of a kind's properties set
94+
func GetSpecPropertiesFor(kind string, props map[string]apiextensionsv1.JSONSchemaProps, field string) (map[string]apiextensionsv1.JSONSchemaProps, error) {
14495
prop, ok := props[field]
14596
if !ok {
14697
return nil, fmt.Errorf("kind %q spec is missing field %q on", kind, field)

internal/autogen/translate/crd_test.go renamed to internal/autogen/translate/crds/crds_test.go

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2025 Google LLC
1+
// Copyright 2025 MongoDB Inc
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -12,16 +12,41 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package translate
15+
package crds_test
1616

1717
import (
18+
"bufio"
19+
"bytes"
1820
"fmt"
1921
"testing"
2022

23+
"github.com/stretchr/testify/assert"
2124
"github.com/stretchr/testify/require"
2225
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
26+
27+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/autogen/translate/crds"
28+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/autogen/translate/testdata"
29+
)
30+
31+
const (
32+
expectedVersion = "v1"
2333
)
2434

35+
func TestParseAssertAndCompile(t *testing.T) {
36+
scanner := bufio.NewScanner(bytes.NewBuffer(testdata.SampleCRDs))
37+
for range 2 { // CRDs sample file has at least 2 CRDs
38+
def, err := crds.Parse(scanner)
39+
require.NoError(t, err)
40+
assert.NotNil(t, def)
41+
kind := def.Spec.Names.Kind
42+
specVersion := crds.SelectVersion(&def.Spec, expectedVersion)
43+
crds.AssertMajorVersion(specVersion, kind, expectedVersion)
44+
schema, err := crds.CompileCRDSchema(specVersion.Schema.OpenAPIV3Schema)
45+
require.NoError(t, err)
46+
assert.NotNil(t, schema)
47+
}
48+
}
49+
2550
func TestSelectVersion(t *testing.T) {
2651
// Define some sample versions to be reused in the tests
2752
v1 := apiextensionsv1.CustomResourceDefinitionVersion{Name: "v1", Served: true}
@@ -78,7 +103,7 @@ func TestSelectVersion(t *testing.T) {
78103

79104
for _, tc := range testCases {
80105
t.Run(tc.name, func(t *testing.T) {
81-
got := selectVersion(tc.spec, tc.version)
106+
got := crds.SelectVersion(tc.spec, tc.version)
82107
require.Equal(t, tc.want, got)
83108
})
84109
}
@@ -158,7 +183,7 @@ func TestGetOpenAPIProperties(t *testing.T) {
158183

159184
for _, tc := range testCases {
160185
t.Run(tc.name, func(t *testing.T) {
161-
gotProperties, err := getOpenAPIProperties(kind, tc.version)
186+
gotProperties, err := crds.GetOpenAPIProperties(kind, tc.version)
162187
if tc.wantErrMsg != "" {
163188
require.Error(t, err)
164189
require.EqualError(t, err, tc.wantErrMsg)
@@ -237,7 +262,7 @@ func TestGetSpecPropertiesFor(t *testing.T) {
237262
for _, tc := range testCases {
238263
t.Run(tc.name, func(t *testing.T) {
239264
// Act
240-
gotProperties, err := getSpecPropertiesFor(kind, tc.props, tc.field)
265+
gotProperties, err := crds.GetSpecPropertiesFor(kind, tc.props, tc.field)
241266

242267
// Assert
243268
if tc.wantErrMsg != "" {

internal/autogen/translate/crds/parse_test.go

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

internal/autogen/translate/refs/context.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
// Copyright 2025 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
115
package refs
216

317
import "sigs.k8s.io/controller-runtime/pkg/client"

internal/autogen/translate/refs/names.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2025 Google LLC
1+
// Copyright 2025 MongoDB Inc
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.

internal/autogen/translate/refs/names_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2025 Google LLC
1+
// Copyright 2025 MongoDB Inc
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -17,8 +17,9 @@ package refs_test
1717
import (
1818
"testing"
1919

20-
"github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/autogen/translate/refs"
2120
"github.com/stretchr/testify/assert"
21+
22+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/autogen/translate/refs"
2223
)
2324

2425
func TestPrefixedName(t *testing.T) {

internal/autogen/translate/testdata/testdata.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2025 Google LLC
1+
// Copyright 2025 MongoDB Inc
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.

internal/autogen/translate/translate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ import (
2020
"fmt"
2121
"reflect"
2222

23+
"github.com/go-logr/logr"
24+
"github.com/stretchr/testify/assert/yaml"
2325
"sigs.k8s.io/controller-runtime/pkg/client"
2426

25-
"github.com/go-logr/logr"
2627
"github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/autogen/translate/refs"
2728
"github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/autogen/translate/unstructured"
28-
"github.com/stretchr/testify/assert/yaml"
2929
)
3030

3131
const (
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright 2025 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
16+
package translate
17+
18+
import (
19+
"fmt"
20+
21+
"github.com/santhosh-tekuri/jsonschema/v5"
22+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
23+
24+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/autogen/translate/crds"
25+
)
26+
27+
// translator implements Translator to translate from a given CRD to and from
28+
// a given SDK version using the same upstream OpenAPI schema
29+
type translator struct {
30+
majorVersion string
31+
jsonSchema *jsonschema.Schema
32+
annotations map[string]string
33+
}
34+
35+
func (tr *translator) Annotation(annotation string) string {
36+
return tr.annotations[annotation]
37+
}
38+
39+
func (tr *translator) MajorVersion() string {
40+
return tr.majorVersion
41+
}
42+
43+
func (tr *translator) Validate(unstructuredObj map[string]any) error {
44+
if err := tr.jsonSchema.Validate(unstructuredObj); err != nil {
45+
return fmt.Errorf("object validation failed against CRD schema: %w", err)
46+
}
47+
return nil
48+
}
49+
50+
// NewTranslator creates a translator for a particular CRD and major version pairs,
51+
// and with a particular set of known Kubernetes object dependencies.
52+
//
53+
// Given the following example resource:
54+
//
55+
// apiVersion: atlas.generated.mongodb.com/v1
56+
// kind: SearchIndex
57+
// metadata:
58+
// name: search-index
59+
// spec:
60+
// v20250312:
61+
//
62+
// In the above case crdVersion is "v1" and majorVersion is "v20250312".
63+
func NewTranslator(crd *apiextensionsv1.CustomResourceDefinition, crdVersion string, majorVersion string) (Translator, error) {
64+
specVersion := crds.SelectVersion(&crd.Spec, crdVersion)
65+
if err := crds.AssertMajorVersion(specVersion, crd.Spec.Names.Kind, majorVersion); err != nil {
66+
return nil, fmt.Errorf("failed to assert major version %s in CRD: %w", majorVersion, err)
67+
}
68+
schema, err := crds.CompileCRDSchema(specVersion.Schema.OpenAPIV3Schema)
69+
if err != nil {
70+
return nil, fmt.Errorf("failed to compile schema: %w", err)
71+
}
72+
return &translator{
73+
majorVersion: majorVersion,
74+
jsonSchema: schema,
75+
annotations: crd.Annotations,
76+
}, nil
77+
}

0 commit comments

Comments
 (0)