Skip to content

Commit 1d9076d

Browse files
authored
feat: return error on missing required fields during adoption (#613)
fixes [#2557](aws-controllers-k8s/community#2557) Description of changes: During resource Adoption, the ACK controllers only returned an error when there was a missing PrimaryKey. If a Resource requires more than 1 field to describe a Resource (eg. Lambda alias requires Name and FunctionName), the controller only verifies the Primary identifier (ARN, ID, Name) and nothing else. With this change, we look into the SDK model to retrieve the required fields and return an error if all are not defined during `PopulateResourceFromAnnotation` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 1045a5e commit 1d9076d

File tree

2 files changed

+50
-42
lines changed

2 files changed

+50
-42
lines changed

pkg/generate/code/set_resource.go

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -843,10 +843,14 @@ func identifierNameOrIDGuardConstructor(
843843
// the required field for a read, or returning an error here
844844
// and returns a `MissingNameIdentifier` error:
845845
//
846-
// if fields[${requiredField}] == "" {
847-
// return ackerrors.MissingNameIdentifier
846+
// f0, ok := fields[${requiredField}]
847+
// if !ok {
848+
// return ackerrors.NewTerminalError(fmt.Errorf("required field missing: ${requiredField}"))
848849
// }
849850
func requiredFieldGuardContructor(
851+
// requiredFieldVarName is the variable where the requiredField value
852+
// will be stored
853+
requiredFieldVarName string,
850854
// String representing the fields map that contains the required
851855
// fields for adoption
852856
sourceVarName string,
@@ -856,7 +860,7 @@ func requiredFieldGuardContructor(
856860
indentLevel int,
857861
) string {
858862
indent := strings.Repeat("\t", indentLevel)
859-
out := fmt.Sprintf("%stmp, ok := %s[\"%s\"]\n", indent, sourceVarName, requiredField)
863+
out := fmt.Sprintf("%s%s, ok := %s[\"%s\"]\n", indent, requiredFieldVarName, sourceVarName, requiredField)
860864
out += fmt.Sprintf("%sif !ok {\n", indent)
861865
out += fmt.Sprintf("%s\treturn ackerrors.NewTerminalError(fmt.Errorf(\"required field missing: %s\"))\n", indent, requiredField)
862866
out += fmt.Sprintf("%s}\n", indent)
@@ -1258,30 +1262,30 @@ func SetResourceIdentifiers(
12581262
//
12591263
// ```
12601264
//
1261-
// tmp, ok := field["brokerID"]
1265+
// primaryKey, ok := field["brokerID"]
12621266
// if !ok {
1263-
// return ackerrors.MissingNameIdentifier
1267+
// return ackerrors.NewTerminalError(fmt.Errorf("required field missing: brokerID"))
12641268
// }
1265-
// r.ko.Status.BrokerID = &tmp
1269+
// r.ko.Status.BrokerID = &primaryKey
12661270
//
12671271
// ```
12681272
//
12691273
// An example of code with additional keys:
12701274
//
12711275
// ```
12721276
//
1273-
// tmp, ok := field["resourceID"]
1274-
// if !ok {
1275-
// return ackerrors.MissingNameIdentifier
1276-
// }
1277-
//
1278-
// r.ko.Spec.ResourceID = &tmp
1277+
// primaryKey, ok := field["resourceID"]
1278+
// if !ok {
1279+
// return ackerrors.NewTerminalError(fmt.Errorf("required field missing: resourceID"))
1280+
// }
12791281
//
1280-
// f0, f0ok := fields["scalableDimension"]
1282+
// r.ko.Spec.ResourceID = &primaryKey
12811283
//
1282-
// if f0ok {
1283-
// r.ko.Spec.ScalableDimension = &f0
1284-
// }
1284+
// f0, ok := fields["scalableDimension"]
1285+
// if !ok {
1286+
// return ackerrors.NewTerminalError(fmt.Errorf("required field missing: scalableDimension"))
1287+
// }
1288+
// r.ko.Spec.ScalableDimension = &f0
12851289
//
12861290
// f1, f1ok := fields["serviceNamespace"]
12871291
//
@@ -1295,17 +1299,17 @@ func SetResourceIdentifiers(
12951299
// ```
12961300
//
12971301
// tmpArn, ok := field["arn"]
1298-
// if !ok {
1299-
// return ackerrors.MissingNameIdentifier
1302+
// if !ok {
1303+
// return ackerrors.NewTerminalError(fmt.Errorf("required field missing: arn"))
13001304
// }
13011305
// if r.ko.Status.ACKResourceMetadata == nil {
13021306
// r.ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{}
13031307
// }
13041308
// arn := ackv1alpha1.AWSResourceName(tmp)
13051309
//
1306-
// r.ko.Status.ACKResourceMetadata.ARN = &arn
1310+
// r.ko.Status.ACKResourceMetadata.ARN = &arn
13071311
//
1308-
// f0, f0ok := fields["modelPackageName"]
1312+
// f0, f0ok := fields["modelPackageName"]
13091313
//
13101314
// if f0ok {
13111315
// r.ko.Spec.ModelPackageName = &f0
@@ -1355,10 +1359,10 @@ func PopulateResourceFromAnnotation(
13551359
out := "\n"
13561360
// Check if the CRD defines the primary keys
13571361
primaryKeyConditionalOut := "\n"
1358-
primaryKeyConditionalOut += requiredFieldGuardContructor(sourceVarName, "arn", indentLevel)
1362+
primaryKeyConditionalOut += requiredFieldGuardContructor("resourceARN", sourceVarName, "arn", indentLevel)
13591363
arnOut += ackResourceMetadataGuardConstructor(fmt.Sprintf("%s.Status", targetVarName), indentLevel)
13601364
arnOut += fmt.Sprintf(
1361-
"%sarn := ackv1alpha1.AWSResourceName(tmp)\n",
1365+
"%sarn := ackv1alpha1.AWSResourceName(resourceARN)\n",
13621366
indent,
13631367
)
13641368
arnOut += fmt.Sprintf(
@@ -1377,9 +1381,10 @@ func PopulateResourceFromAnnotation(
13771381
isPrimarySet := primaryField != nil
13781382
if isPrimarySet {
13791383
memberPath, _ := findFieldInCR(cfg, r, primaryField.Names.Original)
1380-
primaryKeyOut += requiredFieldGuardContructor(sourceVarName, primaryField.Names.CamelLower, indentLevel)
1384+
primaryKeyOut += requiredFieldGuardContructor("primaryKey", sourceVarName, primaryField.Names.CamelLower, indentLevel)
13811385
targetVarPath := fmt.Sprintf("%s%s", targetVarName, memberPath)
13821386
primaryKeyOut += setResourceIdentifierPrimaryIdentifierAnn(cfg, r,
1387+
"&primaryKey",
13831388
primaryField,
13841389
targetVarPath,
13851390
sourceVarName,
@@ -1451,18 +1456,19 @@ func PopulateResourceFromAnnotation(
14511456
switch targetField.ShapeRef.Shape.Type {
14521457
case "list", "structure", "map":
14531458
panic("primary identifier '" + targetField.Path + "' must be a scalar type since NameOrID is a string")
1454-
default:
1455-
break
14561459
}
14571460

14581461
targetVarPath := fmt.Sprintf("%s%s", targetVarName, memberPath)
1459-
if isPrimaryIdentifier {
1460-
primaryKeyOut += requiredFieldGuardContructor(sourceVarName, targetField.Names.CamelLower, indentLevel)
1462+
if inputShape.IsRequired(memberName) || isPrimaryIdentifier {
1463+
requiredFieldVarName := fmt.Sprintf("f%d", memberIndex)
1464+
primaryKeyOut += requiredFieldGuardContructor(requiredFieldVarName, sourceVarName, targetField.Names.CamelLower, indentLevel)
14611465
primaryKeyOut += setResourceIdentifierPrimaryIdentifierAnn(cfg, r,
1466+
fmt.Sprintf("&%s", requiredFieldVarName),
14621467
targetField,
14631468
targetVarPath,
14641469
sourceVarName,
1465-
indentLevel)
1470+
indentLevel,
1471+
)
14661472
} else {
14671473
additionalKeyOut += setResourceIdentifierAdditionalKeyAnn(
14681474
cfg, r,
@@ -1471,7 +1477,8 @@ func PopulateResourceFromAnnotation(
14711477
targetVarPath,
14721478
sourceVarName,
14731479
names.New(fieldName).CamelLower,
1474-
indentLevel)
1480+
indentLevel,
1481+
)
14751482
}
14761483
}
14771484

@@ -1539,6 +1546,8 @@ func setResourceIdentifierPrimaryIdentifier(
15391546
func setResourceIdentifierPrimaryIdentifierAnn(
15401547
cfg *ackgenconfig.Config,
15411548
r *model.CRD,
1549+
// The variable used for required key
1550+
requiredFieldVarName string,
15421551
// The field that will be set on the target variable
15431552
targetField *model.Field,
15441553
// The variable name that we want to set a value to
@@ -1548,12 +1557,11 @@ func setResourceIdentifierPrimaryIdentifierAnn(
15481557
// Number of levels of indentation to use
15491558
indentLevel int,
15501559
) string {
1551-
adaptedMemberPath := fmt.Sprintf("&tmp")
15521560
qualifiedTargetVar := fmt.Sprintf("%s.%s", targetVarName, targetField.Path)
15531561

15541562
return setResourceForScalar(
15551563
qualifiedTargetVar,
1556-
adaptedMemberPath,
1564+
requiredFieldVarName,
15571565
targetField.ShapeRef,
15581566
indentLevel,
15591567
false,

pkg/generate/code/set_resource_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ func TestSetResource_APIGWv2_Route_ReadOne(t *testing.T) {
204204
)
205205
}
206206

207-
208207
func TestSetResource_SageMaker_Domain_ReadOne(t *testing.T) {
209208
assert := assert.New(t)
210209
require := require.New(t)
@@ -1800,11 +1799,11 @@ func TestSetResource_EKS_Cluster_PopulateResourceFromAnnotation(t *testing.T) {
18001799
require.NotNil(crd)
18011800

18021801
expected := `
1803-
tmp, ok := fields["name"]
1802+
f0, ok := fields["name"]
18041803
if !ok {
18051804
return ackerrors.NewTerminalError(fmt.Errorf("required field missing: name"))
18061805
}
1807-
r.ko.Spec.Name = &tmp
1806+
r.ko.Spec.Name = &f0
18081807
18091808
`
18101809
assert.Equal(
@@ -1823,15 +1822,15 @@ func TestSetResource_SageMaker_ModelPackage_PopulateResourceFromAnnotation(t *te
18231822
require.NotNil(crd)
18241823

18251824
expected := `
1826-
tmp, ok := identifier["arn"]
1825+
resourceARN, ok := identifier["arn"]
18271826
if !ok {
18281827
return ackerrors.NewTerminalError(fmt.Errorf("required field missing: arn"))
18291828
}
18301829
18311830
if r.ko.Status.ACKResourceMetadata == nil {
18321831
r.ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{}
18331832
}
1834-
arn := ackv1alpha1.AWSResourceName(tmp)
1833+
arn := ackv1alpha1.AWSResourceName(resourceARN)
18351834
r.ko.Status.ACKResourceMetadata.ARN = &arn
18361835
`
18371836
assert.Equal(
@@ -1850,16 +1849,17 @@ func TestSetResource_APIGWV2_ApiMapping_PopulateResourceFromAnnotation(t *testin
18501849
require.NotNil(crd)
18511850

18521851
expected := `
1853-
tmp, ok := fields["apiMappingID"]
1852+
f0, ok := fields["apiMappingID"]
18541853
if !ok {
18551854
return ackerrors.NewTerminalError(fmt.Errorf("required field missing: apiMappingID"))
18561855
}
1857-
r.ko.Status.APIMappingID = &tmp
1858-
1859-
f1, f1ok := fields["domainName"]
1860-
if f1ok {
1861-
r.ko.Spec.DomainName = aws.String(f1)
1856+
r.ko.Status.APIMappingID = &f0
1857+
f1, ok := fields["domainName"]
1858+
if !ok {
1859+
return ackerrors.NewTerminalError(fmt.Errorf("required field missing: domainName"))
18621860
}
1861+
r.ko.Spec.DomainName = &f1
1862+
18631863
`
18641864
assert.Equal(
18651865
expected,

0 commit comments

Comments
 (0)