1616package crapi
1717
1818import (
19- "fmt"
20- "reflect"
21-
22- "github.com/getkin/kin-openapi/openapi3"
2319 "sigs.k8s.io/controller-runtime/pkg/client"
2420
2521 "github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/crapi/refs"
26- "github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/crapi/unstructured"
2722)
2823
2924// Translator allows to translate back and forth between a CRD schema
@@ -36,140 +31,33 @@ type Translator interface {
3631
3732 // Mappings returns all the OpenAPi custom reference extensions, or an error
3833 Mappings () ([]* refs.Mapping , error )
39- }
40-
41- // APIImporter can translate itself into Kubernetes Objects.
42- // Use to customize or accelerate translations ad-hoc
43- type APIImporter [T any , P refs.PtrClientObj [T ]] interface {
44- FromAPI (tr Translator , target P , objs ... client.Object ) ([]client.Object , error )
45- }
46-
47- // APIExporter can translate itself to an API Object.
48- // Use to customize or accelerate translations ad-hoc
49- type APIExporter [T any ] interface {
50- ToAPI (tr Translator , target * T , objs ... client.Object ) error
51- }
52-
53- // ToAPI translates a source Kubernetes spec into a target API structure.
54- // It uses the spec only to populate ethe API request, nothing from the status.
55- // The target is set to a API request struct to be filled.
56- // The source is set to the Kubernetes CR value. Only the spec data is used here.
57- // The request includes the translator and the dependencies associated with the
58- // source CR, usually Kubernetes secrets.
59- func ToAPI [T any ](tr Translator , target * T , source client.Object , objs ... client.Object ) error {
60- exporter , ok := (source ).(APIExporter [T ])
61- if ok {
62- return exporter .ToAPI (tr , target )
63- }
64- unstructuredSrc , err := unstructured .ToUnstructured (source )
65- if err != nil {
66- return fmt .Errorf ("failed to convert k8s source value to unstructured: %w" , err )
67- }
68- targetUnstructured := map [string ]any {}
69-
70- if err := collapseReferences (tr , unstructuredSrc , source , objs ); err != nil {
71- return fmt .Errorf ("failed to process API mappings: %w" , err )
72- }
73-
74- targetType := reflect .TypeOf (target ).Elem ()
75- if targetType .Kind () != reflect .Struct {
76- return fmt .Errorf ("target must be a struct but got %v" , targetType .Kind ())
77- }
78-
79- value , err := unstructured .GetField [map [string ]any ](unstructuredSrc , "spec" , tr .MajorVersion ())
80- if err != nil {
81- return fmt .Errorf ("failed to access source spec value: %w" , err )
82- }
83- unstructured .CopyFields (targetUnstructured , value )
84- rawEntry := value ["entry" ]
85- if entry , ok := rawEntry .(map [string ]any ); ok {
86- unstructured .CopyFields (targetUnstructured , entry )
87- }
88- if err := unstructured .FromUnstructured (target , targetUnstructured ); err != nil {
89- return fmt .Errorf ("failed to set structured value from unstructured: %w" , err )
90- }
91- return nil
92- }
9334
94- // FromAPI translates a source API structure into a Kubernetes object.
95- // The API source is used to populate the Kubernetes spec, including the
96- // spec.entry and status as well.
97- // The target is set to CR value to be filled. Both spec and stuatus are filled.
98- // The source is set to API response.
99- // The request includes the translator and any dependencies associated with the
100- // source CR.
101- // Returns any extra objects extracted from the response as separate Kubernetes
102- // objects, such as Kubernetes secrets, for instance. This list does not include
103- // the mutated target, and will be empty if nothing else was extracted off the ç
104- // response.
105- func FromAPI [S any , T any , P refs.PtrClientObj [T ]](tr Translator , target P , source * S , objs ... client.Object ) ([]client.Object , error ) {
106- importer , ok := any (source ).(APIImporter [T , P ])
107- if ok {
108- return importer .FromAPI (tr , target , objs ... )
109- }
110- sourceUnstructured , err := unstructured .ToUnstructured (source )
111- if err != nil {
112- return nil , fmt .Errorf ("failed to convert API source value to unstructured: %w" , err )
113- }
114-
115- targetUnstructured , err := unstructured .ToUnstructured (target )
116- if err != nil {
117- return nil , fmt .Errorf ("failed to convert target value to unstructured: %w" , err )
118- }
119-
120- versionedSpec , err := unstructured .GetOrCreateField (
121- targetUnstructured , map [string ]any {}, "spec" , tr .MajorVersion ())
122- if err != nil {
123- return nil , fmt .Errorf ("failed to ensure versioned spec object in unstructured target: %w" , err )
124- }
125- versionedStatus , err := unstructured .GetOrCreateField (
126- targetUnstructured , map [string ]any {}, "status" , tr .MajorVersion ())
127- if err != nil {
128- return nil , fmt .Errorf ("failed to create versioned status object in unstructured target: %w" , err )
129- }
130-
131- unstructured .CopyFields (versionedSpec , sourceUnstructured )
132- versionedSpecEntry := map [string ]any {}
133- unstructured .CopyFields (versionedSpecEntry , sourceUnstructured )
134- versionedSpec ["entry" ] = versionedSpecEntry
135- unstructured .CopyFields (versionedStatus , sourceUnstructured )
136-
137- extraObjects , err := expandReferences (tr , targetUnstructured , target , objs )
138- if err != nil {
139- return nil , fmt .Errorf ("failed to process API mappings: %w" , err )
140- }
141- if err := unstructured .FromUnstructured (target , targetUnstructured ); err != nil {
142- return nil , fmt .Errorf ("failed set structured kubernetes object from unstructured: %w" , err )
143- }
144- return extraObjects , nil
145- }
146-
147- // collapseReferences finds all Kubernetes references, solves them and collapses
148- // them by replacing their values from the reference (e.g Kubernetes secret or
149- // group), into the corresponding API request value
150- func collapseReferences (tr Translator , req map [string ]any , main client.Object , objs []client.Object ) error {
151- mappings , err := tr .Mappings ()
152- if err != nil {
153- return fmt .Errorf ("failed to extract mappings to collapse: %w" , err )
154- }
155- return refs .CollapseAll (mappings , main , objs , req )
156- }
157-
158- // expandReferences finds all API fields that must be referenced, and expand
159- // such reference, moving the value (e.g. sensitive field or group id) to a
160- // referenced Kubernetes object (e.g. Kubernetes secret or Atlas Group)
161- func expandReferences (tr Translator , cr map [string ]any , main client.Object , objs []client.Object ) ([]client.Object , error ) {
162- mappings , err := tr .Mappings ()
163- if err != nil {
164- return nil , fmt .Errorf ("failed to extract mappings to expand: %w" , err )
165- }
166- return refs .ExpandAll (mappings , main , objs , cr )
167- }
168-
169- func propertyValueOrNil (schema * openapi3.Schema , propertyName string ) * openapi3.Schema {
170- if schema .Properties != nil &&
171- schema .Properties [propertyName ] != nil && schema .Properties [propertyName ].Value != nil {
172- return schema .Properties [propertyName ].Value
173- }
174- return nil
35+ // ToAPI translates a source Kubernetes object into a target API structure.
36+ // It uses the spec only to populate ethe API request, nothing from the status.
37+ // The target is set to a API request struct to be filled.
38+ // The source is set to the Kubernetes CR value. Only the spec data is used here.
39+ // The request includes the translator and the dependencies associated with the
40+ // source CR, usually Kubernetes secrets.
41+ ToAPI (target any , source client.Object , objs ... client.Object ) error
42+
43+ // FromAPI translates a source API structure into a Kubernetes object.
44+ // The API source is used to populate the Kubernetes spec, including the
45+ // spec.entry and status as well.
46+ // The target is set to CR value to be filled. Both spec and status are filled.
47+ // The source is set to API response.
48+ // The request includes the translator and any dependencies associated with the
49+ // source CR.
50+ // Returns any extra objects extracted from the response as separate Kubernetes
51+ // objects, such as Kubernetes secrets, for instance. This list does not include
52+ // the mutated target, and will be empty if nothing else was extracted off the ç
53+ // response.
54+ FromAPI (target client.Object , source any , objs ... client.Object ) ([]client.Object , error )
55+ }
56+
57+ // Request is deprecated do not use
58+ //
59+ // Deprecated: request is no longer used in the ToAPI and FromAPI calls
60+ type Request struct {
61+ Translator Translator
62+ Dependencies []client.Object
17563}
0 commit comments