Skip to content

Commit 539749d

Browse files
sagarp337Maxrovr
authored andcommitted
Added - Improve Resource discovery error messaging
1 parent 2e21bef commit 539749d

File tree

6 files changed

+146
-9
lines changed

6 files changed

+146
-9
lines changed

internal/commonexport/commonexport_functions.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,14 +399,18 @@ func (ctx *ResourceDiscoveryContext) PrintSummary() {
399399
utils.Logln(utils.Green(fmt.Sprintf("Total time taken by entire export: %v", ctx.TimeTakenForEntireExport)))
400400
}
401401

402-
func (ctx *ResourceDiscoveryContext) PrintErrors() {
402+
func (ctx *ResourceDiscoveryContext) PrintErrors() ([]string, []string) {
403403
utils.Logln(utils.Yellow("\n\n[WARN] Resource discovery finished with errors listed below:\n"))
404+
var notDiscoveredParentResources []string
405+
var notDiscoveredChildResources []string
404406
for _, resourceDiscoveryError := range ctx.ErrorList.Errors {
405407
if resourceDiscoveryError.ResourceType == "" || ctx.TargetSpecificResources {
406408
utils.Logln(utils.Yellow(resourceDiscoveryError.Error.Error()))
407409

408410
} else if resourceDiscoveryError.ParentResource == "export" {
409411
utils.Logln(utils.Yellow(fmt.Sprintf("Error discovering `%s` resources: %s", resourceDiscoveryError.ResourceType, resourceDiscoveryError.Error.Error())))
412+
partiallyResourcesDiscovered := "Error discovering " + resourceDiscoveryError.ResourceType + " resources: " + resourceDiscoveryError.Error.Error()
413+
notDiscoveredParentResources = append(notDiscoveredParentResources, partiallyResourcesDiscovered)
410414

411415
} else {
412416
utils.Logln(utils.Yellow(fmt.Sprintf("Error discovering `%s` resources for %s: %s", resourceDiscoveryError.ResourceType, resourceDiscoveryError.ParentResource, resourceDiscoveryError.Error.Error())))
@@ -417,9 +421,13 @@ func (ctx *ResourceDiscoveryContext) PrintErrors() {
417421
getNotFoundChildren(resourceDiscoveryError.ResourceType, resourceDiscoveryError.ResourceGraph, &notFoundChildren)
418422
if len(notFoundChildren) > 0 {
419423
utils.Logln(utils.Yellow(fmt.Sprintf("\tFollowing child resources were also not discovered due to parent error: %v", strings.Join(notFoundChildren, ", "))))
424+
notFoundChildResources := "\tFollowing child resources were also not discovered due to parent error: " + strings.Join(notFoundChildren, ", ")
425+
notDiscoveredChildResources = append(notDiscoveredChildResources, notFoundChildResources)
420426
}
421427
}
422428
}
429+
430+
return notDiscoveredParentResources, notDiscoveredChildResources
423431
}
424432

425433
func (h *TerraformResourceHints) DiscoversWithSingularDatasource() bool {

internal/commonexport/commonexport_types.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,52 @@ type ResourceDiscoveryError struct {
138138
Error error
139139
ResourceGraph *TerraformResourceGraph
140140
}
141+
type ResourceDiscoveryCustomError struct {
142+
TypeOfError ErrorTypeEnum
143+
Message error
144+
Suggestion string
145+
}
146+
147+
const (
148+
// RD Error Message
149+
WriteTmpStateError ErrorTypeEnum = "WriteTmpStateError"
150+
IdsNotFoundError ErrorTypeEnum = "IdsNotFoundError"
151+
PartiallyResourcesDiscoveredError ErrorTypeEnum = "PartiallyResourcesDiscoveredError"
152+
153+
WriteTmpStateErrorMessage = "[ERROR] error writing temp state for resources found:"
154+
IdsNotFoundErrorMessage = "[ERROR] one or more expected resource ids were not found"
155+
PartiallyResourcesDiscoveredSuggestion = `This error could happen if:
156+
1. GET/LIST api failed to get the resource.
157+
2. If GET request requires more attributes than the OCID of a resource. If the resource supports composite id, make sure correct composite Id is getting set in resource data using getIdFn resource hint.
158+
3. If resource is dependent on another resource(parent), the field for the parent resource should be a required field in API.
159+
4. If a resource is tenancy specific i.e. it can only be created in the tenancy then the resource should be present under tenancyResourcesGraphs.`
160+
WriteTmpStateErrorSuggestion = "Unsupported Terraform version! Please install supported terraform v.0.12 - https://releases.hashicorp.com/terraform/0.12.31/"
161+
IdsNotFoundSuggestion = "SetData() method should not contains parameters are not supported by schema\""
162+
)
163+
164+
func (tfE ResourceDiscoveryCustomError) Error() error {
165+
switch tfE.TypeOfError {
166+
case WriteTmpStateError:
167+
return fmt.Errorf(
168+
"Error Message: %s \n"+
169+
"Suggestion: %s\n",
170+
tfE.Message.Error(), tfE.Suggestion)
171+
case IdsNotFoundError:
172+
return fmt.Errorf(
173+
"Error Message: %s \n"+
174+
"Suggestion: %s\n",
175+
tfE.Message.Error(), tfE.Suggestion)
176+
case PartiallyResourcesDiscoveredError:
177+
return fmt.Errorf(
178+
"Error Message: %s \n"+
179+
"Suggestion: %s\n",
180+
tfE.Message.Error(), tfE.Suggestion)
181+
default:
182+
return fmt.Errorf(tfE.Message.Error())
183+
}
184+
}
185+
186+
type ErrorTypeEnum string
141187

142188
var TfHclVersionvar TfHclVersion
143189
var GetHclStringFromGenericMap = func(builder *strings.Builder, ociRes *OCIResource, interpolationMap map[string]string) error {

internal/resourcediscovery/export_compartment.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -314,12 +314,28 @@ func RunExportCommand(args *tf_export.ExportCommandArgs) (err error, status Stat
314314
}
315315
if len(ctx.ErrorList.Errors) > 0 {
316316
// If the errors were from discovery of resources return partial success status
317-
ctx.PrintErrors()
318-
return nil, StatusPartialSuccess
317+
error, status := getListOfNotDiscoveredResources(ctx)
318+
319+
return error, status
319320
}
320321
return nil, StatusSuccess
321322
}
322323

324+
func getListOfNotDiscoveredResources(ctx *tf_export.ResourceDiscoveryContext) (error, Status) {
325+
notDiscoveredParentResources, notDiscoveredChildResources := ctx.PrintErrors()
326+
var notDiscoveredResources []string
327+
var notDiscoveredError tf_export.ResourceDiscoveryCustomError
328+
329+
notDiscoveredResources = append(notDiscoveredParentResources, notDiscoveredChildResources...) // Not discovered resources eg.Parent resources + Child Resources
330+
331+
notDiscoveredError = tf_export.ResourceDiscoveryCustomError{
332+
TypeOfError: tf_export.PartiallyResourcesDiscoveredError,
333+
Message: errors.New(strings.Join(notDiscoveredResources, ",")),
334+
}
335+
336+
return notDiscoveredError.Error(), StatusPartialSuccess
337+
}
338+
323339
func getExportConfig(d *schema.ResourceData) (interface{}, error) {
324340
clients := &tf_client.OracleClients{
325341
SdkClientMap: make(map[string]interface{}, len(tf_client.OracleClientRegistrationsVar.RegisteredClients)),
@@ -600,10 +616,15 @@ func generateStateParallel(ctx *tf_export.ResourceDiscoveryContext, steps []reso
600616

601617
// Write temp state file for each service, this step will import resources into a separate state file for each service in parallel
602618
// If writing temp configuration thrown error, won't attempt to write temp state
603-
if err == nil {
604-
if err = step.writeTmpState(); err != nil {
605-
errorChannel <- fmt.Errorf("[ERROR] error while writing temp state for resources found in step %d: %s", i, err.Error())
619+
// Write temp state file for each service, this step will import resources into a separate state file for each service in parallel
620+
if err := step.writeTmpState(); err != nil {
621+
var tfError tf_export.ResourceDiscoveryCustomError
622+
tfError = tf_export.ResourceDiscoveryCustomError{
623+
TypeOfError: tf_export.WriteTmpStateError,
624+
Message: errors.New(tf_export.WriteTmpStateErrorMessage),
625+
Suggestion: tf_export.WriteTmpStateErrorSuggestion,
606626
}
627+
errorChannel <- tfError.Error()
607628
}
608629

609630
utils.Debugf("writing temp config and state: Completed step %d", i)

internal/resourcediscovery/export_compartment_test.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1046,7 +1046,7 @@ func TestUnitRunExportCommand_exitStatusForPartialSuccess(t *testing.T) {
10461046
return getTestClients(), nil
10471047
}
10481048
exportConfigProvider = acctest.MockConfigurationProvider{}
1049-
if err, status := RunExportCommand(args); err != nil {
1049+
if err, status := RunExportCommand(args); err != nil && status == StatusFail {
10501050
t.Logf("(TF version %s) export command failed due to err: %v", tf_export.TfHclVersionvar.ToString(), err)
10511051
t.Fail()
10521052
} else if status != StatusPartialSuccess {
@@ -1066,6 +1066,50 @@ func TestUnitRunExportCommand_exitStatusForPartialSuccess(t *testing.T) {
10661066
os.RemoveAll(outputDir)
10671067
}
10681068

1069+
// Test exit status in case of partial success
1070+
// issue-routing-tag: terraform/default
1071+
func TestUnitRunExportCommand_errorSuggestionForPartialSuccess(t *testing.T) {
1072+
type testFormat struct {
1073+
name string
1074+
errorLength int
1075+
ctx *tf_export.ResourceDiscoveryContext
1076+
}
1077+
errors := []*tf_export.ResourceDiscoveryError{}
1078+
parentResource := "oci_test_parent:ocid1.parent.abcdefghiklmnop.0"
1079+
1080+
resourceDiscoveryContext := &tf_export.ResourceDiscoveryContext{
1081+
ResourceHintsLookup: map[string]*tf_export.TerraformResourceHints{"oci_test_parent": exportParentDefinition},
1082+
ExpectedResourceIds: map[string]bool{"oci_test_parent:ocid1.parent.abcdefghiklmnop.0": false},
1083+
ErrorList: tf_export.ErrorList{
1084+
Errors: append(errors, &tf_export.ResourceDiscoveryError{
1085+
ResourceType: "load_balancer",
1086+
ParentResource: "export",
1087+
Error: fmt.Errorf("Error Message: [ERROR] Error while discovering below resources:\n" + parentResource),
1088+
ResourceGraph: nil,
1089+
}),
1090+
},
1091+
TargetSpecificResources: false,
1092+
}
1093+
tests := []testFormat{
1094+
{
1095+
name: "Test error message for partially not discovered resources",
1096+
ctx: resourceDiscoveryContext,
1097+
errorLength: 1,
1098+
},
1099+
}
1100+
for _, test := range tests {
1101+
t.Logf("Running %s", test.name)
1102+
if error, _ := getListOfNotDiscoveredResources(test.ctx); error != nil {
1103+
expectedErrorMessage := error.Error()
1104+
fmt.Println("\n", expectedErrorMessage)
1105+
if !strings.Contains(expectedErrorMessage, parentResource) {
1106+
t.Errorf("Output error - %s which is not equal to expected error - %s", expectedErrorMessage, test.ctx.ErrorList.Errors[0].Error.Error())
1107+
}
1108+
1109+
}
1110+
}
1111+
}
1112+
10691113
// Test that resources can be found using a resource dependency graph
10701114
// issue-routing-tag: terraform/default
10711115
func TestUnitFindResources_basic(t *testing.T) {

internal/resourcediscovery/export_resource_helpers_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,11 +505,23 @@ func TestUnitpostValidate(t *testing.T) {
505505
name: "Test one missing resource",
506506
errorLength: 1,
507507
},
508+
{
509+
name: "Missing resource id check",
510+
errorLength: 2,
511+
},
508512
}
509513
for _, test := range tests {
510514
t.Logf("Running %s", test.name)
511515
ctx.PostValidate()
516+
// Validate encapsulated error message: one or more expected resource ids were not found
517+
if len(ctx.ErrorList.Errors) == 2 {
518+
for _, resourceDiscoveryError := range ctx.ErrorList.Errors {
519+
// element is the element from someSlice for where we are
520+
fmt.Println("Error", resourceDiscoveryError.Error)
521+
}
522+
}
512523
if len(ctx.ErrorList.Errors) != test.errorLength {
524+
513525
t.Errorf("Output error length - %d which is not equal to expected error - %d", len(ctx.ErrorList.Errors), test.errorLength)
514526
}
515527
}

internal/resourcediscovery/export_test_helpers.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,14 @@ func testExportCompartment(compartmentId *string, exportCommandArgs *tf_export.E
133133
}
134134
// For generated tests, RD will only return this error if one of the `ids` was not found
135135
// (which in case of tests is the id for the resource RD is looking for)
136-
if status == StatusPartialSuccess {
137-
return fmt.Errorf("[ERROR] expected resource was not found")
136+
if errExport != nil && status == StatusPartialSuccess {
137+
var idsNotFoundError tf_export.ResourceDiscoveryCustomError
138+
idsNotFoundError = tf_export.ResourceDiscoveryCustomError{
139+
TypeOfError: tf_export.PartiallyResourcesDiscoveredError,
140+
Message: errExport,
141+
Suggestion: tf_export.PartiallyResourcesDiscoveredSuggestion,
142+
}
143+
return idsNotFoundError.Error()
138144
}
139145
}
140146

0 commit comments

Comments
 (0)