Skip to content

Commit c5d4381

Browse files
authored
Simplify translate API (#2763)
* Simplify translate API Signed-off-by: jose.vazquez <[email protected]> * Do not cloud test v2 changes --------- Signed-off-by: jose.vazquez <[email protected]>
1 parent 72912d9 commit c5d4381

File tree

3 files changed

+35
-42
lines changed

3 files changed

+35
-42
lines changed

.github/workflows/cloud-tests-filter.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
- 'Dockerfile'
3535
- 'test/e2e/**'
3636
- 'test/int/**'
37-
- 'internal/!v3/**'
37+
- '!internal/v3/**'
3838
# run only if 'production-code' files were changed
3939
- name: Production code changed
4040
if: steps.paths-filter.outputs.production-code-changed == 'true'

internal/v3/translate/translator.go

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,13 @@ type PtrClientObj[T any] interface {
3131
client.Object
3232
}
3333

34-
// Translator allows to translate back and forth between a CRD schema version
35-
// and SDK API structures of a certain version
34+
// Translator allows to translate back and forth between a CRD schema
35+
// and SDK API structures of a certain version.
36+
// A translator is an immutable configuration object, it can be safely shared
37+
// across threads
3638
type Translator struct {
3739
crd CRDInfo
38-
majorVersion MajorVersion
39-
deps DependencyRepo
40-
}
41-
42-
// MajorVersion holds the SDK version information
43-
type MajorVersion struct {
44-
version string
40+
majorVersion string
4541
}
4642

4743
// APIImporter can translate itself into Kubernetes Objects.
@@ -69,16 +65,15 @@ type APIExporter[T any] interface {
6965
// v20250312:
7066
//
7167
// In the above case crdVersion is "v1" and majorVersion is "v20250312".
72-
func NewTranslator(crd *apiextensionsv1.CustomResourceDefinition, crdVersion string, majorVersion string, deps DependencyRepo) *Translator {
68+
func NewTranslator(crd *apiextensionsv1.CustomResourceDefinition, crdVersion string, majorVersion string) *Translator {
7369
return &Translator{
7470
crd: CRDInfo{definition: crd, version: crdVersion},
75-
majorVersion: MajorVersion{version: majorVersion},
76-
deps: deps,
71+
majorVersion: majorVersion,
7772
}
7873
}
7974

80-
// FromAPI translaters a source API structure into a Kubernetes object
81-
func FromAPI[S any, T any, P PtrClientObj[T]](t *Translator, target P, source *S) ([]client.Object, error) {
75+
// FromAPI translaters a source API structure into a Kubernetes object.
76+
func FromAPI[S any, T any, P PtrClientObj[T]](t *Translator, target P, source *S, objs ...client.Object) ([]client.Object, error) {
8277
importer, ok := any(source).(APIImporter[T, P])
8378
if ok {
8479
return importer.FromAPI(t, target)
@@ -92,7 +87,7 @@ func FromAPI[S any, T any, P PtrClientObj[T]](t *Translator, target P, source *S
9287

9388
versionedSpec := map[string]any{}
9489
copyFields(versionedSpec, sourceUnstructured)
95-
if err := createField(targetUnstructured, versionedSpec, "spec", t.majorVersion.version); err != nil {
90+
if err := createField(targetUnstructured, versionedSpec, "spec", t.majorVersion); err != nil {
9691
return nil, fmt.Errorf("failed to create versioned spec object in unstructured target: %w", err)
9792
}
9893
versionedSpecEntry := map[string]any{}
@@ -101,11 +96,12 @@ func FromAPI[S any, T any, P PtrClientObj[T]](t *Translator, target P, source *S
10196

10297
versionedStatus := map[string]any{}
10398
copyFields(versionedStatus, sourceUnstructured)
104-
if err := createField(targetUnstructured, versionedStatus, "status", t.majorVersion.version); err != nil {
99+
if err := createField(targetUnstructured, versionedStatus, "status", t.majorVersion); err != nil {
105100
return nil, fmt.Errorf("failed to create versioned status object in unsstructured target: %w", err)
106101
}
107102

108-
extraObjects, err := t.expandMappings(targetUnstructured)
103+
deps := NewDependencies(target, objs...)
104+
extraObjects, err := t.expandMappings(deps, targetUnstructured)
109105
if err != nil {
110106
return nil, fmt.Errorf("failed to process API mappings: %w", err)
111107
}
@@ -116,7 +112,7 @@ func FromAPI[S any, T any, P PtrClientObj[T]](t *Translator, target P, source *S
116112
}
117113

118114
// ToAPI translates a source Kubernetes spec into a target API structure
119-
func ToAPI[T any](t *Translator, target *T, source client.Object) error {
115+
func ToAPI[T any](t *Translator, target *T, source client.Object, objs ...client.Object) error {
120116
exporter, ok := (source).(APIExporter[T])
121117
if ok {
122118
return exporter.ToAPI(t, target)
@@ -131,20 +127,21 @@ func ToAPI[T any](t *Translator, target *T, source client.Object) error {
131127
if err != nil {
132128
return fmt.Errorf("failed to enumerate CRD spec properties: %w", err)
133129
}
134-
if _, ok := specProps[t.majorVersion.version]; !ok {
135-
return fmt.Errorf("failed to match the CRD spec version %q in schema", t.majorVersion.version)
130+
if _, ok := specProps[t.majorVersion]; !ok {
131+
return fmt.Errorf("failed to match the CRD spec version %q in schema", t.majorVersion)
136132
}
137133
unstructuredSrc, err := toUnstructured(source)
138134
if err != nil {
139135
return fmt.Errorf("failed to convert k8s source value to unstructured: %w", err)
140136
}
141137
targetUnstructured := map[string]any{}
142-
value, err := accessField[map[string]any](unstructuredSrc, "spec", t.majorVersion.version)
138+
value, err := accessField[map[string]any](unstructuredSrc, "spec", t.majorVersion)
143139
if err != nil {
144140
return fmt.Errorf("failed to access source spec value: %w", err)
145141
}
146142

147-
if err := t.collapseMappings(value); err != nil {
143+
deps := NewDependencies(source, objs...)
144+
if err := t.collapseMappings(deps, value); err != nil {
148145
return fmt.Errorf("failed to process API mappings: %w", err)
149146
}
150147

@@ -176,7 +173,7 @@ func ToAPI[T any](t *Translator, target *T, source client.Object) error {
176173
return nil
177174
}
178175

179-
func (t *Translator) expandMappings(obj map[string]any) ([]client.Object, error) {
176+
func (t *Translator) expandMappings(deps DependencyRepo, obj map[string]any) ([]client.Object, error) {
180177
mappingsYML := t.crd.definition.Annotations[APIMAppingsAnnotation]
181178
if mappingsYML == "" {
182179
return []client.Object{}, nil
@@ -186,19 +183,19 @@ func (t *Translator) expandMappings(obj map[string]any) ([]client.Object, error)
186183
return nil, fmt.Errorf("failed to unmarshal mappings YAML: %w", err)
187184
}
188185

189-
if err := t.expandMappingsAt(obj, mappings, "spec", t.majorVersion.version); err != nil {
186+
if err := t.expandMappingsAt(deps, obj, mappings, "spec", t.majorVersion); err != nil {
190187
return nil, fmt.Errorf("failed to map properties of spec from API to Kubernetes: %w", err)
191188
}
192-
if err := t.expandMappingsAt(obj, mappings, "spec", t.majorVersion.version, "entry"); err != nil {
189+
if err := t.expandMappingsAt(deps, obj, mappings, "spec", t.majorVersion, "entry"); err != nil {
193190
return nil, fmt.Errorf("failed to map properties of spec from API to Kubernetes: %w", err)
194191
}
195-
if err := t.expandMappingsAt(obj, mappings, "status", t.majorVersion.version); err != nil {
192+
if err := t.expandMappingsAt(deps, obj, mappings, "status", t.majorVersion); err != nil {
196193
return nil, fmt.Errorf("failed to map properties of status from API to Kubernetes: %w", err)
197194
}
198-
return t.deps.Added(), nil
195+
return deps.Added(), nil
199196
}
200197

201-
func (t *Translator) expandMappingsAt(obj, mappings map[string]any, fields ...string) error {
198+
func (t *Translator) expandMappingsAt(deps DependencyRepo, obj, mappings map[string]any, fields ...string) error {
202199
expandedPath := []string{"properties"}
203200
for _, field := range fields {
204201
expandedPath = append(expandedPath, field, "properties")
@@ -214,14 +211,14 @@ func (t *Translator) expandMappingsAt(obj, mappings map[string]any, fields ...st
214211
if err != nil {
215212
return fmt.Errorf("failed to access object's %v: %w", fields, err)
216213
}
217-
mapper := Mapper{deps: t.deps, expand: true}
214+
mapper := Mapper{deps: deps, expand: true}
218215
if err := mapper.mapProperties([]string{}, props, field); err != nil {
219216
return fmt.Errorf("failed to process properties from API into %v: %w", fields, err)
220217
}
221218
return nil
222219
}
223220

224-
func (t *Translator) collapseMappings(spec map[string]any) error {
221+
func (t *Translator) collapseMappings(deps DependencyRepo, spec map[string]any) error {
225222
mappingsYML := t.crd.definition.Annotations[APIMAppingsAnnotation]
226223
if mappingsYML == "" {
227224
return nil
@@ -231,14 +228,14 @@ func (t *Translator) collapseMappings(spec map[string]any) error {
231228
return fmt.Errorf("failed to unmarshal mappings YAML: %w", err)
232229
}
233230
props, err := accessField[map[string]any](mappings,
234-
"properties", "spec", "properties", t.majorVersion.version, "properties")
231+
"properties", "spec", "properties", t.majorVersion, "properties")
235232
if errors.Is(err, ErrNotFound) {
236233
return nil
237234
}
238235
if err != nil {
239236
return fmt.Errorf("failed to access the API mapping properties for the spec: %w", err)
240237
}
241-
mapper := Mapper{deps: t.deps, expand: false}
238+
mapper := Mapper{deps: deps, expand: false}
242239
return mapper.mapProperties([]string{}, props, spec)
243240
}
244241

internal/v3/translate/translator_test.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -409,8 +409,7 @@ func testFromAPI[S any, T any, P translate.PtrClientObj[T]](t *testing.T, kind s
409409
crdsYML := bytes.NewBuffer(crdsYAMLBytes)
410410
crd, err := extractCRD(kind, bufio.NewScanner(crdsYML))
411411
require.NoError(t, err)
412-
deps := translate.NewDependencies(target)
413-
translator := translate.NewTranslator(crd, version, sdkVersion, deps)
412+
translator := translate.NewTranslator(crd, version, sdkVersion)
414413
results, err := translate.FromAPI(translator, target, input)
415414
require.NoError(t, err)
416415
assert.Equal(t, want, results)
@@ -712,10 +711,8 @@ func TestToAPIAllRefs(t *testing.T) {
712711
crdsYML := bytes.NewBuffer(crdsYAMLBytes)
713712
crd, err := extractCRD(tc.crd, bufio.NewScanner(crdsYML))
714713
require.NoError(t, err)
715-
deps := translate.NewDependencies(tc.input, tc.deps...)
716-
translator := translate.NewTranslator(crd, version, sdkVersion, deps)
717-
// , reflect.TypeOf(tc.target)
718-
require.NoError(t, translate.ToAPI(translator, &tc.target, tc.input))
714+
translator := translate.NewTranslator(crd, version, sdkVersion)
715+
require.NoError(t, translate.ToAPI(translator, &tc.target, tc.input, tc.deps...))
719716
assert.Equal(t, tc.want, tc.target)
720717
})
721718
}
@@ -2155,9 +2152,8 @@ func testToAPI[T any](t *testing.T, kind string, input client.Object, objs []cli
21552152
crdsYML := bytes.NewBuffer(crdsYAMLBytes)
21562153
crd, err := extractCRD(kind, bufio.NewScanner(crdsYML))
21572154
require.NoError(t, err)
2158-
deps := translate.NewDependencies(input, objs...)
2159-
translator := translate.NewTranslator(crd, version, sdkVersion, deps)
2160-
require.NoError(t, translate.ToAPI(translator, target, input))
2155+
translator := translate.NewTranslator(crd, version, sdkVersion)
2156+
require.NoError(t, translate.ToAPI(translator, target, input, objs...))
21612157
assert.Equal(t, want, target)
21622158
}
21632159

0 commit comments

Comments
 (0)