Skip to content

Commit f8c0f47

Browse files
authored
Use SetConfig in SetResourceForStruct when target shape has no members (#292)
Issue #, if available: aws-controllers-k8s/community#1146 This is a follow-up to #289. A `targetShape` without members in `SetResourceForStruct` will not be processed today; however, this is **not** desired behavior for all APIs such as EC2's DHCPOptions resource. This patch will look up and consume `SetConfig` in `SetResourceForStruct` when `targetShape` has no members like when DHCPOptions' field, `DHCPConfigurations.Values []*string`, aligns (by name) with aws-sdk's output shape, `DhcpConfigurations.Values []*AttributeValue`. Instead of skipping the processing of `Values []*string` field, it will check if a `SetConfig` exists and if it's valid (referencing a *real* shape in sourceShape), then use that updated field to source the target field's value resulting in using `DhcpConfigurations.Values.Value *string` to populate the resource's `DHCPConfigurations.Values []*string`. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 3e91498 commit f8c0f47

File tree

4 files changed

+159
-6
lines changed

4 files changed

+159
-6
lines changed

pkg/generate/ack/controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ var (
114114
if setCfg != nil && setCfg.Ignore {
115115
return ""
116116
}
117-
return code.SetResourceForStruct(r.Config(), r, targetFieldName, targetVarName, targetShapeRef, setCfg, sourceVarName, sourceShapeRef, indentLevel)
117+
return code.SetResourceForStruct(r.Config(), r, targetFieldName, targetVarName, targetShapeRef, setCfg, sourceVarName, sourceShapeRef, "", model.OpTypeList, indentLevel)
118118
},
119119
"GoCodeCompare": func(r *ackmodel.CRD, deltaVarName string, sourceVarName string, targetVarName string, indentLevel int) string {
120120
return code.CompareResource(r.Config(), r, deltaVarName, sourceVarName, targetVarName, indentLevel)

pkg/generate/code/set_resource.go

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,8 @@ func SetResource(
308308
setCfg,
309309
sourceAdaptedVarName,
310310
sourceMemberShapeRef,
311+
f.Names.Camel,
312+
opType,
311313
indentLevel+1,
312314
)
313315
out += setResourceForScalar(
@@ -578,6 +580,8 @@ func setResourceReadMany(
578580
setCfg,
579581
sourceAdaptedVarName,
580582
sourceMemberShapeRef,
583+
f.Names.Camel,
584+
model.OpTypeList,
581585
indentLevel+2,
582586
)
583587
out += setResourceForScalar(
@@ -1156,6 +1160,9 @@ func setResourceForContainer(
11561160
sourceVarName string,
11571161
// ShapeRef of the source struct field
11581162
sourceShapeRef *awssdkmodel.ShapeRef,
1163+
// targetFieldPath is the field path for the target containing field
1164+
targetFieldPath string,
1165+
op model.OpType,
11591166
indentLevel int,
11601167
) string {
11611168
switch sourceShapeRef.Shape.Type {
@@ -1168,6 +1175,8 @@ func setResourceForContainer(
11681175
targetSetCfg,
11691176
sourceVarName,
11701177
sourceShapeRef,
1178+
targetFieldPath,
1179+
op,
11711180
indentLevel,
11721181
)
11731182
case "list":
@@ -1179,6 +1188,8 @@ func setResourceForContainer(
11791188
targetSetCfg,
11801189
sourceVarName,
11811190
sourceShapeRef,
1191+
targetFieldPath,
1192+
op,
11821193
indentLevel,
11831194
)
11841195
case "map":
@@ -1190,6 +1201,8 @@ func setResourceForContainer(
11901201
targetSetCfg,
11911202
sourceVarName,
11921203
sourceShapeRef,
1204+
targetFieldPath,
1205+
op,
11931206
indentLevel,
11941207
)
11951208
default:
@@ -1219,15 +1232,21 @@ func SetResourceForStruct(
12191232
sourceVarName string,
12201233
// ShapeRef of the source struct field
12211234
sourceShapeRef *awssdkmodel.ShapeRef,
1235+
// targetFieldPath is the field path to targetFieldName
1236+
targetFieldPath string,
1237+
op model.OpType,
12221238
indentLevel int,
12231239
) string {
12241240
out := ""
12251241
indent := strings.Repeat("\t", indentLevel)
12261242
sourceShape := sourceShapeRef.Shape
12271243
targetShape := targetShapeRef.Shape
12281244

1245+
var sourceMemberShapeRef *awssdkmodel.ShapeRef
1246+
var sourceAdaptedVarName, qualifiedTargetVar string
1247+
12291248
for _, targetMemberName := range targetShape.MemberNames() {
1230-
sourceMemberShapeRef := sourceShape.MemberRefs[targetMemberName]
1249+
sourceMemberShapeRef = sourceShape.MemberRefs[targetMemberName]
12311250
if sourceMemberShapeRef == nil {
12321251
continue
12331252
}
@@ -1244,13 +1263,14 @@ func SetResourceForStruct(
12441263
indexedVarName := fmt.Sprintf("%sf%d", targetVarName, sourceMemberIndex)
12451264
sourceMemberShape := sourceMemberShapeRef.Shape
12461265
targetMemberCleanNames := names.New(targetMemberName)
1247-
sourceAdaptedVarName := sourceVarName + "." + targetMemberName
1266+
sourceAdaptedVarName = sourceVarName + "." + targetMemberName
12481267
out += fmt.Sprintf(
12491268
"%sif %s != nil {\n", indent, sourceAdaptedVarName,
12501269
)
1251-
qualifiedTargetVar := fmt.Sprintf(
1270+
qualifiedTargetVar = fmt.Sprintf(
12521271
"%s.%s", targetVarName, targetMemberCleanNames.Camel,
12531272
)
1273+
updatedTargetFieldPath := targetFieldPath + "." + targetMemberCleanNames.Camel
12541274

12551275
switch sourceMemberShape.Type {
12561276
case "list", "structure", "map":
@@ -1269,6 +1289,8 @@ func SetResourceForStruct(
12691289
nil,
12701290
sourceAdaptedVarName,
12711291
sourceMemberShapeRef,
1292+
updatedTargetFieldPath,
1293+
op,
12721294
indentLevel+1,
12731295
)
12741296
out += setResourceForScalar(
@@ -1290,6 +1312,52 @@ func SetResourceForStruct(
12901312
"%s}\n", indent,
12911313
)
12921314
}
1315+
if len(targetShape.MemberNames()) == 0 {
1316+
// This scenario can occur when the targetShape is a primitive, but
1317+
// the sourceShape is a struct. For example, EC2 resource DHCPOptions
1318+
// has a field NewDhcpConfiguration.Values(targetShape = string) whose name
1319+
// aligns with DhcpConfiguration.Values(sourceShape = AttributeValue).
1320+
// Although the names correspond, the shapes/types are different and the intent
1321+
// is to set NewDhcpConfiguration.Values using DhcpConfiguration.Values.Value
1322+
// (AttributeValue.Value) shape instead. This behavior can be configured using
1323+
// SetConfig.
1324+
1325+
// Check if target field has a SetConfig, validate SetConfig.From points
1326+
// to a shape within sourceShape, and generate Go code using
1327+
// said shape. Using the example above, SetConfig is set
1328+
// for NewDhcpConfiguration.Values and Setconfig.From points
1329+
// to AttributeValue.Value (string), which leads to generating Go
1330+
// code referencing DhcpConfiguration.Values.Value instead of 'Values'.
1331+
1332+
if targetField, ok := r.Fields[targetFieldPath]; ok {
1333+
setCfg := targetField.GetSetterConfig(op)
1334+
if setCfg != nil && setCfg.From != nil {
1335+
fp := fieldpath.FromString(*setCfg.From)
1336+
sourceMemberShapeRef = fp.ShapeRef(sourceShapeRef)
1337+
if sourceMemberShapeRef != nil && sourceMemberShapeRef.Shape != nil {
1338+
names := names.New(sourceMemberShapeRef.LocationName)
1339+
sourceAdaptedVarName = sourceVarName + "." + names.Camel
1340+
out += fmt.Sprintf(
1341+
"%sif %s != nil {\n", indent, sourceAdaptedVarName,
1342+
)
1343+
qualifiedTargetVar = targetVarName
1344+
1345+
// Use setResourceForScalar and dereference sourceAdaptedVarName
1346+
// because primitives are being set.
1347+
sourceAdaptedVarName = "*" + sourceAdaptedVarName
1348+
out += setResourceForScalar(
1349+
qualifiedTargetVar,
1350+
sourceAdaptedVarName,
1351+
sourceMemberShapeRef,
1352+
indentLevel+1,
1353+
)
1354+
out += fmt.Sprintf(
1355+
"%s}\n", indent,
1356+
)
1357+
}
1358+
}
1359+
}
1360+
}
12931361
return out
12941362
}
12951363

@@ -1310,6 +1378,9 @@ func setResourceForSlice(
13101378
sourceVarName string,
13111379
// ShapeRef of the source slice field
13121380
sourceShapeRef *awssdkmodel.ShapeRef,
1381+
// targetFieldPath is the field path to targetFieldName
1382+
targetFieldPath string,
1383+
op model.OpType,
13131384
indentLevel int,
13141385
) string {
13151386
out := ""
@@ -1388,6 +1459,8 @@ func setResourceForSlice(
13881459
targetSetCfg,
13891460
iterVarName,
13901461
&sourceShape.MemberRef,
1462+
targetFieldPath,
1463+
op,
13911464
indentLevel+1,
13921465
)
13931466
}
@@ -1421,6 +1494,9 @@ func setResourceForMap(
14211494
sourceVarName string,
14221495
// ShapeRef of the source map field
14231496
sourceShapeRef *awssdkmodel.ShapeRef,
1497+
// targetFieldPath is the field path to targetFieldName
1498+
targetFieldPath string,
1499+
op model.OpType,
14241500
indentLevel int,
14251501
) string {
14261502
out := ""
@@ -1453,6 +1529,8 @@ func setResourceForMap(
14531529
nil,
14541530
valIterVarName,
14551531
&sourceShape.ValueRef,
1532+
targetFieldPath,
1533+
op,
14561534
indentLevel+1,
14571535
)
14581536
addressOfVar := ""

pkg/generate/code/set_resource_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3169,3 +3169,75 @@ func TestSetResource_IAM_Role_NestedSetConfig(t *testing.T) {
31693169
code.SetResource(crd.Config(), crd, model.OpTypeGet, "resp", "ko", 1),
31703170
)
31713171
}
3172+
3173+
func TestSetResource_EC2_DHCPOptions_NestedSetConfig(t *testing.T) {
3174+
assert := assert.New(t)
3175+
require := require.New(t)
3176+
3177+
g := testutil.NewModelForService(t, "ec2")
3178+
3179+
crd := testutil.GetCRDByName(t, g, "DhcpOptions")
3180+
require.NotNil(crd)
3181+
3182+
// The input and output shapes for the DhcpConfigurations.Values are different
3183+
// and we have a custom SetConfig for this field in our generator.yaml file
3184+
// to configure the SetResource function to set the value of the resource's
3185+
// (nested) Spec.DHCPConfigurations.Values to the value of the (nested)
3186+
// GetDhcpOptionsResponse.DhcpOptions.DhcpConfigurations.Values.Value field
3187+
expected := `
3188+
if resp.DhcpOptions.DhcpConfigurations != nil {
3189+
f0 := []*svcapitypes.NewDHCPConfiguration{}
3190+
for _, f0iter := range resp.DhcpOptions.DhcpConfigurations {
3191+
f0elem := &svcapitypes.NewDHCPConfiguration{}
3192+
if f0iter.Key != nil {
3193+
f0elem.Key = f0iter.Key
3194+
}
3195+
if f0iter.Values != nil {
3196+
f0elemf1 := []*string{}
3197+
for _, f0elemf1iter := range f0iter.Values {
3198+
var f0elemf1elem string
3199+
if f0elemf1iter.Value != nil {
3200+
f0elemf1elem = *f0elemf1iter.Value
3201+
}
3202+
f0elemf1 = append(f0elemf1, &f0elemf1elem)
3203+
}
3204+
f0elem.Values = f0elemf1
3205+
}
3206+
f0 = append(f0, f0elem)
3207+
}
3208+
ko.Spec.DHCPConfigurations = f0
3209+
} else {
3210+
ko.Spec.DHCPConfigurations = nil
3211+
}
3212+
if resp.DhcpOptions.DhcpOptionsId != nil {
3213+
ko.Status.DHCPOptionsID = resp.DhcpOptions.DhcpOptionsId
3214+
} else {
3215+
ko.Status.DHCPOptionsID = nil
3216+
}
3217+
if resp.DhcpOptions.OwnerId != nil {
3218+
ko.Status.OwnerID = resp.DhcpOptions.OwnerId
3219+
} else {
3220+
ko.Status.OwnerID = nil
3221+
}
3222+
if resp.DhcpOptions.Tags != nil {
3223+
f3 := []*svcapitypes.Tag{}
3224+
for _, f3iter := range resp.DhcpOptions.Tags {
3225+
f3elem := &svcapitypes.Tag{}
3226+
if f3iter.Key != nil {
3227+
f3elem.Key = f3iter.Key
3228+
}
3229+
if f3iter.Value != nil {
3230+
f3elem.Value = f3iter.Value
3231+
}
3232+
f3 = append(f3, f3elem)
3233+
}
3234+
ko.Status.Tags = f3
3235+
} else {
3236+
ko.Status.Tags = nil
3237+
}
3238+
`
3239+
assert.Equal(
3240+
expected,
3241+
code.SetResource(crd.Config(), crd, model.OpTypeCreate, "resp", "ko", 1),
3242+
)
3243+
}

pkg/testdata/models/apis/ec2/0000-00-00/generator.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,11 @@ operations:
6868
output_wrapper_field_path: VpcEndpoint
6969

7070
resources:
71-
DHCPOptions:
72-
71+
DhcpOptions:
72+
fields:
73+
DHCPConfigurations.Values:
74+
set:
75+
- from: AttributeValue.Value
7376
SecurityGroup:
7477
renames:
7578
operations:

0 commit comments

Comments
 (0)