Skip to content

Commit 233ab75

Browse files
authored
Add NewPerVersionTraslators function (#2951)
* Add NewPerVersionTraslators function * Clarify To&FromAPI interfaces and fix FromAPI returns * Remove the Request
1 parent 7802573 commit 233ab75

File tree

3 files changed

+231
-185
lines changed

3 files changed

+231
-185
lines changed

internal/crapi/crapi.go

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"reflect"
2121

2222
"github.com/getkin/kin-openapi/openapi3"
23-
"github.com/go-logr/logr"
2423
"sigs.k8s.io/controller-runtime/pkg/client"
2524

2625
"github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/crapi/refs"
@@ -39,39 +38,44 @@ type Translator interface {
3938
Mappings() ([]*refs.Mapping, error)
4039
}
4140

42-
// Request holds common parameters for all translation request
41+
// Request is deprecated do not use
42+
//
43+
// Deprecated: request is no longer used in the ToAPI and FromAPI calls
4344
type Request struct {
4445
Translator Translator
45-
Log logr.Logger
4646
Dependencies []client.Object
4747
}
4848

4949
// APIImporter can translate itself into Kubernetes Objects.
5050
// Use to customize or accelerate translations ad-hoc
5151
type APIImporter[T any, P refs.PtrClientObj[T]] interface {
52-
FromAPI(tr *Request, target P) ([]client.Object, error)
52+
FromAPI(tr Translator, target P, objs ...client.Object) ([]client.Object, error)
5353
}
5454

5555
// APIExporter can translate itself to an API Object.
5656
// Use to customize or accelerate translations ad-hoc
5757
type APIExporter[T any] interface {
58-
ToAPI(tr *Request, target *T) error
58+
ToAPI(tr Translator, target *T, objs ...client.Object) error
5959
}
6060

6161
// ToAPI translates a source Kubernetes spec into a target API structure.
6262
// It uses the spec only to populate ethe API request, nothing from the status.
63-
func ToAPI[T any](r *Request, target *T, source client.Object) error {
63+
// The target is set to a API request struct to be filled.
64+
// The source is set to the Kubernetes CR value. Only the spec data is used here.
65+
// The request includes the translator and the dependencies associated with the
66+
// source CR, usually Kubernetes secrets.
67+
func ToAPI[T any](tr Translator, target *T, source client.Object, objs ...client.Object) error {
6468
exporter, ok := (source).(APIExporter[T])
6569
if ok {
66-
return exporter.ToAPI(r, target)
70+
return exporter.ToAPI(tr, target)
6771
}
6872
unstructuredSrc, err := unstructured.ToUnstructured(source)
6973
if err != nil {
7074
return fmt.Errorf("failed to convert k8s source value to unstructured: %w", err)
7175
}
7276
targetUnstructured := map[string]any{}
7377

74-
if err := collapseReferences(r, unstructuredSrc, source); err != nil {
78+
if err := collapseReferences(tr, unstructuredSrc, source, objs); err != nil {
7579
return fmt.Errorf("failed to process API mappings: %w", err)
7680
}
7781

@@ -80,7 +84,7 @@ func ToAPI[T any](r *Request, target *T, source client.Object) error {
8084
return fmt.Errorf("target must be a struct but got %v", targetType.Kind())
8185
}
8286

83-
value, err := unstructured.GetField[map[string]any](unstructuredSrc, "spec", r.Translator.MajorVersion())
87+
value, err := unstructured.GetField[map[string]any](unstructuredSrc, "spec", tr.MajorVersion())
8488
if err != nil {
8589
return fmt.Errorf("failed to access source spec value: %w", err)
8690
}
@@ -98,10 +102,18 @@ func ToAPI[T any](r *Request, target *T, source client.Object) error {
98102
// FromAPI translates a source API structure into a Kubernetes object.
99103
// The API source is used to populate the Kubernetes spec, including the
100104
// spec.entry and status as well.
101-
func FromAPI[S any, T any, P refs.PtrClientObj[T]](r *Request, target P, source *S) ([]client.Object, error) {
105+
// The target is set to CR value to be filled. Both spec and stuatus are filled.
106+
// The source is set to API response.
107+
// The request includes the translator and any dependencies associated with the
108+
// source CR.
109+
// Returns any extra objects extracted from the response as separate Kubernetes
110+
// objects, such as Kubernetes secrets, for instance. This list does not include
111+
// the mutated target, and will be empty if nothing else was extracted off the ç
112+
// response.
113+
func FromAPI[S any, T any, P refs.PtrClientObj[T]](tr Translator, target P, source *S, objs ...client.Object) ([]client.Object, error) {
102114
importer, ok := any(source).(APIImporter[T, P])
103115
if ok {
104-
return importer.FromAPI(r, target)
116+
return importer.FromAPI(tr, target, objs...)
105117
}
106118
sourceUnstructured, err := unstructured.ToUnstructured(source)
107119
if err != nil {
@@ -114,12 +126,12 @@ func FromAPI[S any, T any, P refs.PtrClientObj[T]](r *Request, target P, source
114126
}
115127

116128
versionedSpec, err := unstructured.GetOrCreateField(
117-
targetUnstructured, map[string]any{}, "spec", r.Translator.MajorVersion())
129+
targetUnstructured, map[string]any{}, "spec", tr.MajorVersion())
118130
if err != nil {
119131
return nil, fmt.Errorf("failed to ensure versioned spec object in unstructured target: %w", err)
120132
}
121133
versionedStatus, err := unstructured.GetOrCreateField(
122-
targetUnstructured, map[string]any{}, "status", r.Translator.MajorVersion())
134+
targetUnstructured, map[string]any{}, "status", tr.MajorVersion())
123135
if err != nil {
124136
return nil, fmt.Errorf("failed to create versioned status object in unstructured target: %w", err)
125137
}
@@ -130,36 +142,36 @@ func FromAPI[S any, T any, P refs.PtrClientObj[T]](r *Request, target P, source
130142
versionedSpec["entry"] = versionedSpecEntry
131143
unstructured.CopyFields(versionedStatus, sourceUnstructured)
132144

133-
extraObjects, err := expandReferences(r, targetUnstructured, target)
145+
extraObjects, err := expandReferences(tr, targetUnstructured, target, objs)
134146
if err != nil {
135147
return nil, fmt.Errorf("failed to process API mappings: %w", err)
136148
}
137149
if err := unstructured.FromUnstructured(target, targetUnstructured); err != nil {
138150
return nil, fmt.Errorf("failed set structured kubernetes object from unstructured: %w", err)
139151
}
140-
return append([]client.Object{target}, extraObjects...), nil
152+
return extraObjects, nil
141153
}
142154

143155
// collapseReferences finds all Kubernetes references, solves them and collapses
144156
// them by replacing their values from the reference (e.g Kubernetes secret or
145157
// group), into the corresponding API request value
146-
func collapseReferences(r *Request, req map[string]any, main client.Object) error {
147-
mappings, err := r.Translator.Mappings()
158+
func collapseReferences(tr Translator, req map[string]any, main client.Object, objs []client.Object) error {
159+
mappings, err := tr.Mappings()
148160
if err != nil {
149161
return fmt.Errorf("failed to extract mappings to collapse: %w", err)
150162
}
151-
return refs.CollapseAll(mappings, main, r.Dependencies, req)
163+
return refs.CollapseAll(mappings, main, objs, req)
152164
}
153165

154166
// expandReferences finds all API fields that must be referenced, and expand
155167
// such reference, moving the value (e.g. sensitive field or group id) to a
156168
// referenced Kubernetes object (e.g. Kubernetes secret or Atlas Group)
157-
func expandReferences(r *Request, cr map[string]any, main client.Object) ([]client.Object, error) {
158-
mappings, err := r.Translator.Mappings()
169+
func expandReferences(tr Translator, cr map[string]any, main client.Object, objs []client.Object) ([]client.Object, error) {
170+
mappings, err := tr.Mappings()
159171
if err != nil {
160172
return nil, fmt.Errorf("failed to extract mappings to expand: %w", err)
161173
}
162-
return refs.ExpandAll(mappings, main, r.Dependencies, cr)
174+
return refs.ExpandAll(mappings, main, objs, cr)
163175
}
164176

165177
func propertyValueOrNil(schema *openapi3.Schema, propertyName string) *openapi3.Schema {

0 commit comments

Comments
 (0)