Skip to content

Commit d4e9730

Browse files
committed
Move conversion functions to conversion.go
Signed-off-by: Alper Rifat Ulucinar <ulucinar@users.noreply.github.com>
1 parent fc73677 commit d4e9730

File tree

2 files changed

+115
-72
lines changed

2 files changed

+115
-72
lines changed

pkg/controller/conversion.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package controller
6+
7+
import (
8+
"slices"
9+
"sort"
10+
"strings"
11+
12+
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
13+
"github.com/pkg/errors"
14+
)
15+
16+
type conversionMode int
17+
18+
const (
19+
toEmbeddedObject conversionMode = iota
20+
toSingletonList
21+
)
22+
23+
// String returns a string representation of the conversion mode.
24+
func (m conversionMode) String() string {
25+
switch m {
26+
case toSingletonList:
27+
return "toSingletonList"
28+
case toEmbeddedObject:
29+
return "toEmbeddedObject"
30+
default:
31+
return "unknown"
32+
}
33+
}
34+
35+
// setValue sets the value, in pv, to v at the specified path fp.
36+
// It's implemented on top of the fieldpath library by accessing
37+
// the parent map in fp and directly setting v as a value in the
38+
// parent map. We don't use fieldpath.Paved.SetValue because the
39+
// JSON value validation performed by it potentially changes types.
40+
func setValue(pv *fieldpath.Paved, v any, fp string) error {
41+
segments := strings.Split(fp, ".")
42+
p := fp
43+
var pm any = pv.UnstructuredContent()
44+
var err error
45+
if len(segments) > 1 {
46+
p = strings.Join(segments[:len(segments)-1], ".")
47+
pm, err = pv.GetValue(p)
48+
if err != nil {
49+
return errors.Wrapf(err, "cannot get the parent value at field path %s", p)
50+
}
51+
}
52+
parent, ok := pm.(map[string]any)
53+
if !ok {
54+
return errors.Errorf("parent at field path %s must be a map[string]any", p)
55+
}
56+
parent[segments[len(segments)-1]] = v
57+
return nil
58+
}
59+
60+
// convert performs conversion between singleton lists and embedded objects
61+
// while passing the CRD parameters to the Terraform layer and while reading
62+
// state from the Terraform layer at runtime. The paths where the conversion
63+
// will be performed are specified using paths and the conversion mode (whether
64+
// an embedded object will be converted into a singleton list or a singleton
65+
// list will be converted into an embedded object) is determined by the mode
66+
// parameter.
67+
func convert(params map[string]any, paths []string, mode conversionMode) (map[string]any, error) {
68+
switch mode {
69+
case toSingletonList:
70+
slices.Sort(paths)
71+
case toEmbeddedObject:
72+
sort.Slice(paths, func(i, j int) bool {
73+
return paths[i] > paths[j]
74+
})
75+
}
76+
77+
pv := fieldpath.Pave(params)
78+
for _, fp := range paths {
79+
exp, err := pv.ExpandWildcards(fp)
80+
if err != nil {
81+
return nil, errors.Wrapf(err, "cannot expand wildcards for the field path expression %s", fp)
82+
}
83+
switch len(exp) {
84+
case 0:
85+
continue
86+
case 1:
87+
v, err := pv.GetValue(exp[0])
88+
if err != nil {
89+
return nil, errors.Wrapf(err, "cannot get the value at the field path %s with the conversion mode set to %q", exp[0], mode)
90+
}
91+
switch mode {
92+
case toSingletonList:
93+
if err := setValue(pv, []any{v}, exp[0]); err != nil {
94+
return nil, errors.Wrapf(err, "cannot set the singleton list's value at the field path %s", exp[0])
95+
}
96+
case toEmbeddedObject:
97+
s, ok := v.([]any)
98+
if !ok || len(s) > 1 {
99+
// if len(s) is 0, then it's not a slice
100+
return nil, errors.Errorf("singleton list, at the field path %s, must have a length of 1 but it has a length of %d", exp[0], len(s))
101+
}
102+
var newVal any = map[string]any{}
103+
if len(s) > 0 {
104+
newVal = s[0]
105+
}
106+
if err := setValue(pv, newVal, exp[0]); err != nil {
107+
return nil, errors.Wrapf(err, "cannot set the embedded object's value at the field path %s", exp[0])
108+
}
109+
}
110+
default:
111+
return nil, errors.Errorf("unexpected number of expansions (%d) for the wildcard field path expression %s", len(exp), fp)
112+
}
113+
}
114+
return params, nil
115+
}

pkg/controller/external_tfpluginsdk.go

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ package controller
77
import (
88
"context"
99
"fmt"
10-
"slices"
11-
"sort"
1210
"strings"
1311
"time"
1412

@@ -124,76 +122,6 @@ type terraformPluginSDKExternal struct {
124122
opTracker *AsyncTracker
125123
}
126124

127-
func setValue(pv *fieldpath.Paved, v any, fp string) error {
128-
segments := strings.Split(fp, ".")
129-
p := fp
130-
var pm any = pv.UnstructuredContent()
131-
var err error
132-
if len(segments) > 1 {
133-
p = strings.Join(segments[:len(segments)-1], ".")
134-
pm, err = pv.GetValue(p)
135-
if err != nil {
136-
return errors.Wrapf(err, "cannot get the parent value at field path %s", p)
137-
}
138-
}
139-
parent, ok := pm.(map[string]any)
140-
if !ok {
141-
return errors.Errorf("parent at field path %s must be a map[string]any", p)
142-
}
143-
parent[segments[len(segments)-1]] = v
144-
return nil
145-
}
146-
147-
func convert(params map[string]any, paths []string, toList bool) (map[string]any, error) {
148-
pv := fieldpath.Pave(params)
149-
150-
if toList {
151-
slices.Sort(paths)
152-
} else {
153-
sort.Slice(paths, func(i, j int) bool {
154-
return paths[i] > paths[j]
155-
})
156-
}
157-
158-
for _, fp := range paths {
159-
exp, err := pv.ExpandWildcards(fp)
160-
if err != nil {
161-
return nil, errors.Wrapf(err, "cannot expand wildcards for the field path expression %s", fp)
162-
}
163-
switch len(exp) {
164-
case 0:
165-
continue
166-
case 1:
167-
v, err := pv.GetValue(exp[0])
168-
if err != nil {
169-
return nil, errors.Wrapf(err, "cannot get the value at the field path %s with the list mode set to %t", exp[0], toList)
170-
}
171-
switch {
172-
case toList:
173-
if err := setValue(pv, []any{v}, exp[0]); err != nil {
174-
return nil, errors.Wrapf(err, "cannot set the singleton list's value at the field path %s", exp[0])
175-
}
176-
default:
177-
s, ok := v.([]any)
178-
if !ok || len(s) > 1 {
179-
// if len(s) is 0, then it's not a slice
180-
return nil, errors.Errorf("singleton list, at the field path %s, must have a length of 1 but it has a length of %d", exp[0], len(s))
181-
}
182-
var newVal any = map[string]any{}
183-
if len(s) > 0 {
184-
newVal = s[0]
185-
}
186-
if err := setValue(pv, newVal, exp[0]); err != nil {
187-
return nil, errors.Wrapf(err, "cannot set the embedded object's value at the field path %s", exp[0])
188-
}
189-
}
190-
default:
191-
return nil, errors.Errorf("unexpected number of expansions (%d) for the wildcard field path expression %s", len(exp), fp)
192-
}
193-
}
194-
return params, nil
195-
}
196-
197125
func getExtendedParameters(ctx context.Context, tr resource.Terraformed, externalName string, config *config.Resource, ts terraform.Setup, initParamsMerged bool, kube client.Client) (map[string]any, error) {
198126
params, err := tr.GetMergedParameters(initParamsMerged)
199127
if err != nil {

0 commit comments

Comments
 (0)