Skip to content
This repository was archived by the owner on Apr 15, 2026. It is now read-only.

Commit 3c66ecb

Browse files
authored
Merge pull request #13 from patrickcping/11-terraform-plan-fails-when-the-davinci_flow-resource-name-in-subflow_link-blocks-does-not-match-the-actual-davinci_flow-resource-definition
Re-map all `subflow_link` flow references based on provided subflows
2 parents deafd59 + f1ef6b3 commit 3c66ecb

44 files changed

Lines changed: 276647 additions & 71 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ testoutput*
22
vendor
33
env*.sh
44
output.log
5-
generated*
5+
generated*
6+
dvtf-pingctl

cmd/root.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ func initConfig() error {
132132
}
133133

134134
func bindParams(cmd *cobra.Command) error {
135+
var err error
135136
cmd.Flags().VisitAll(func(f *pflag.Flag) {
136137
configName := strings.ReplaceAll(f.Name, "-", "")
137138

@@ -151,12 +152,15 @@ func bindParams(cmd *cobra.Command) error {
151152
for _, val := range v {
152153
values = append(values, fmt.Sprintf("%v", val))
153154
}
154-
cmd.Flags().Set(f.Name, strings.Join(values, ","))
155+
err = cmd.Flags().Set(f.Name, strings.Join(values, ","))
155156
default:
156-
cmd.Flags().Set(f.Name, fmt.Sprintf("%v", v))
157+
err = cmd.Flags().Set(f.Name, fmt.Sprintf("%v", v))
158+
}
159+
if err != nil {
160+
return
157161
}
158162
}
159163
})
160164

161-
return nil
165+
return err
162166
}

internal/flow/export.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ func NewFromPath(pathToJson string) (*DaVinciExport, error) {
2525
}
2626

2727
// Get the string from file
28-
dvExport.readJSONFile()
28+
err := dvExport.readJSONFile()
29+
if err != nil {
30+
return nil, err
31+
}
2932

3033
return &dvExport, nil
3134
}

internal/generate/generate.go

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
"github.com/patrickcping/dvtf-pingctl/internal/generate/export"
1414
"github.com/patrickcping/dvtf-pingctl/internal/logger"
15+
"github.com/patrickcping/dvtf-pingctl/internal/output"
1516
"github.com/patrickcping/dvtf-pingctl/internal/terraform"
1617
"github.com/samir-gandhi/davinci-client-go/davinci"
1718
)
@@ -24,13 +25,15 @@ type DaVinciGenerator struct {
2425
connectionsData []connectionData
2526
variablesData []variableData
2627
flowAssets []flowAssetData
28+
flowNames map[string]string
2729
}
2830

2931
func New(exportDefs []export.DaVinciGeneratorExport, resources []terraform.ProviderResource, outputPath string) *DaVinciGenerator {
3032
return &DaVinciGenerator{
3133
exportDefs: exportDefs,
3234
resources: resources,
3335
outputPath: outputPath,
36+
flowNames: map[string]string{},
3437
}
3538
}
3639

@@ -54,6 +57,9 @@ func (d *DaVinciGenerator) Generate(version string, overwrite bool) error {
5457
}
5558
}
5659

60+
// Re-write the subflow names to fix issue https://github.com/patrickcping/dvtf-pingctl/issues/11
61+
d.rewriteSubflowNames()
62+
5763
// Write the HCL configuration
5864
err = d.write(version, overwrite)
5965
if err != nil {
@@ -63,6 +69,34 @@ func (d *DaVinciGenerator) Generate(version string, overwrite bool) error {
6369
return nil
6470
}
6571

72+
func (d *DaVinciGenerator) rewriteSubflowNames() {
73+
updatedFlowsData := make([]flowData, 0)
74+
for _, flowData := range d.flowsData {
75+
updatedSubflowLinks := make([]flowSubflowLink, 0)
76+
for _, subflowLink := range flowData.SubflowLinks {
77+
if subFlowName, ok := d.flowNames[subflowLink.ReplaceSubflowID]; ok {
78+
// update the subflow link name
79+
subflowLink.FlowRefID = d.sanitiseResourceName(subFlowName)
80+
subflowLink.SubFlowName = subFlowName
81+
} else {
82+
// Issue warning that subflow hasn't been included in the `-e` param list
83+
output.Print(output.Opts{
84+
Message: "Warning: Subflow has not been found in any of the JSON files provided in the `-e` param list. Plan errors may occur.",
85+
Fields: map[string]interface{}{
86+
"Missing Subflow ID": subflowLink.ReplaceSubflowID,
87+
},
88+
})
89+
}
90+
updatedSubflowLinks = append(updatedSubflowLinks, subflowLink)
91+
}
92+
flowData.SubflowLinks = updatedSubflowLinks
93+
94+
updatedFlowsData = append(updatedFlowsData, flowData)
95+
}
96+
97+
d.flowsData = updatedFlowsData
98+
}
99+
66100
func (d *DaVinciGenerator) parseFlows() error {
67101

68102
newExportDefs := make([]export.DaVinciGeneratorExport, 0)
@@ -128,6 +162,7 @@ func (d *DaVinciGenerator) buildDataSingleFlow(flow davinci.Flow, parsedIntf map
128162
l.Debug().Msgf("buildDataSingleFlow Command called.")
129163

130164
flowResourceName := d.sanitiseResourceName(flow.Name)
165+
d.flowNames[flow.FlowID] = flow.Name
131166

132167
pathVar := fmt.Sprintf("assets/flows/%s.json", flowResourceName)
133168
createFlow := true
@@ -278,7 +313,7 @@ func (d *DaVinciGenerator) buildDataSingleFlow(flow davinci.Flow, parsedIntf map
278313
DependsOnVarRefs: dependsOnVarRefs,
279314
Name: d.sanitiseStringFieldPtr(&flow.Name),
280315
Description: d.sanitiseStringFieldPtr(flow.Description),
281-
FlowJSONPath: fmt.Sprintf("%s", pathVar),
316+
FlowJSONPath: pathVar,
282317
ConnectionLinks: flowConnectionLinks,
283318
SubflowLinks: subflowLinks,
284319
})
@@ -356,7 +391,7 @@ func (d *DaVinciGenerator) writeVariables(version string, overwrite bool) error
356391
return fmt.Errorf("failed to parse variable HCL template. err: %s", err.Error())
357392
}
358393

359-
fileName := d.outputPath + fmt.Sprintf("/davinci_variables.tf")
394+
fileName := fmt.Sprintf("%s/davinci_variables.tf", d.outputPath)
360395

361396
// Check if the file exists
362397
if _, err := os.Stat(fileName); err == nil {
@@ -368,10 +403,10 @@ func (d *DaVinciGenerator) writeVariables(version string, overwrite bool) error
368403
}
369404

370405
outputFile, err := os.Create(fileName)
371-
defer outputFile.Close()
372406
if err != nil {
373407
return err
374408
}
409+
defer outputFile.Close()
375410

376411
for _, variableData := range d.variablesData {
377412
err = hclTemplate.Execute(outputFile, variableData)
@@ -398,7 +433,7 @@ func (d *DaVinciGenerator) writeConnections(version string, overwrite bool) erro
398433
return fmt.Errorf("failed to parse connection HCL template. err: %s", err.Error())
399434
}
400435

401-
fileName := d.outputPath + fmt.Sprintf("/davinci_connectors.tf")
436+
fileName := fmt.Sprintf("%s/davinci_connectors.tf", d.outputPath)
402437

403438
// Check if the file exists
404439
if _, err := os.Stat(fileName); err == nil {
@@ -410,10 +445,10 @@ func (d *DaVinciGenerator) writeConnections(version string, overwrite bool) erro
410445
}
411446

412447
outputFile, err := os.Create(fileName)
413-
defer outputFile.Close()
414448
if err != nil {
415449
return err
416450
}
451+
defer outputFile.Close()
417452

418453
for _, connectionData := range d.connectionsData {
419454
err = hclTemplate.Execute(outputFile, connectionData)
@@ -439,7 +474,7 @@ func (d *DaVinciGenerator) writeFlows(version string, overwrite bool) error {
439474
return fmt.Errorf("failed to parse flow HCL template. err: %s", err.Error())
440475
}
441476

442-
fileName := d.outputPath + fmt.Sprintf("/davinci_flows.tf")
477+
fileName := fmt.Sprintf("%s/davinci_flows.tf", d.outputPath)
443478

444479
// Check if the file exists
445480
if _, err := os.Stat(fileName); err == nil {
@@ -451,10 +486,10 @@ func (d *DaVinciGenerator) writeFlows(version string, overwrite bool) error {
451486
}
452487

453488
outputFile, err := os.Create(fileName)
454-
defer outputFile.Close()
455489
if err != nil {
456490
return err
457491
}
492+
defer outputFile.Close()
458493

459494
for _, flowData := range d.flowsData {
460495
err = hclTemplate.Execute(outputFile, flowData)
@@ -468,7 +503,7 @@ func (d *DaVinciGenerator) writeFlows(version string, overwrite bool) error {
468503

469504
func (d *DaVinciGenerator) writeAssets() error {
470505

471-
outputDir := d.outputPath + fmt.Sprintf("/assets/flows/")
506+
outputDir := fmt.Sprintf("%s/assets/flows/", d.outputPath)
472507

473508
err := os.MkdirAll(outputDir, os.ModePerm)
474509
if err != nil {
@@ -488,6 +523,9 @@ func (d *DaVinciGenerator) writeAssets() error {
488523
func (d *DaVinciGenerator) writeAsset(flowAsset flowAssetData) error {
489524

490525
fileData, err := json.MarshalIndent(flowAsset.flowMap, "", " ")
526+
if err != nil {
527+
return fmt.Errorf("Cannot marshal asset data: %s", err)
528+
}
491529

492530
err = os.WriteFile(fmt.Sprintf("%s/%s", d.outputPath, flowAsset.path), fileData, 0644)
493531
if err != nil {

internal/validate/validate.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -182,42 +182,42 @@ func (d *DaVinciValidator) OutputValidationResponse(vErr error) (ok, warning boo
182182
case errors.Is(vErr, ErrSubflowsPresent):
183183

184184
outputOpts = output.Opts{
185-
Message: fmt.Sprintf("Subflows are present in the export file. Subflows must be managed as their own independent davinci_flow resources. Please re-export the DaVinci flow without subflows."),
185+
Message: "Subflows are present in the export file. Subflows must be managed as their own independent davinci_flow resources. Please re-export the DaVinci flow without subflows.",
186186
Result: output.ENUM_RESULT_FAILURE,
187187
}
188188

189189
case errors.Is(vErr, davinci.ErrInvalidJson):
190190

191191
outputOpts = output.Opts{
192-
Message: fmt.Sprintf("The DaVinci Flow Export JSON is not valid JSON. Please re-export the DaVinci flow."),
192+
Message: "The DaVinci Flow Export JSON is not valid JSON. Please re-export the DaVinci flow.",
193193
Result: output.ENUM_RESULT_FAILURE,
194194
}
195195

196196
case errors.Is(vErr, davinci.ErrEmptyFlow):
197197

198198
outputOpts = output.Opts{
199-
Message: fmt.Sprintf("The DaVinci Flow Export JSON is empty. Please re-export the DaVinci flow."),
199+
Message: "The DaVinci Flow Export JSON is empty. Please re-export the DaVinci flow.",
200200
Result: output.ENUM_RESULT_FAILURE,
201201
}
202202

203203
case errors.Is(vErr, davinci.ErrNoFlowDefinition):
204204

205205
outputOpts = output.Opts{
206-
Message: fmt.Sprintf("No flow definition found in the DaVinci Flow Export JSON. Expecting exactly one flow definition. Please re-export the DaVinci flow."),
206+
Message: "No flow definition found in the DaVinci Flow Export JSON. Expecting exactly one flow definition. Please re-export the DaVinci flow.",
207207
Result: output.ENUM_RESULT_FAILURE,
208208
}
209209

210210
case errors.Is(vErr, davinci.ErrMissingSaveVariableValues):
211211

212212
outputOpts = output.Opts{
213-
Message: fmt.Sprintf("Save flow variable nodes are present but are missing variable values in the DaVinci Flow Export JSON. Please re-export the DaVinci flow ensuring that variable values are included."),
213+
Message: "Save flow variable nodes are present but are missing variable values in the DaVinci Flow Export JSON. Please re-export the DaVinci flow ensuring that variable values are included.",
214214
Result: output.ENUM_RESULT_FAILURE,
215215
}
216216

217217
case errors.As(vErr, &equatesEmptyError):
218218

219219
outputOpts = output.Opts{
220-
Message: fmt.Sprintf("The JSON does not appear to be a valid DaVinci export file. Please re-export the DaVinci flow."),
220+
Message: "The JSON does not appear to be a valid DaVinci export file. Please re-export the DaVinci flow.",
221221
Result: output.ENUM_RESULT_FAILURE,
222222
Fields: map[string]interface{}{
223223
"Computed Difference (empty is failure)": equatesEmptyError.Diff,
@@ -227,7 +227,7 @@ func (d *DaVinciValidator) OutputValidationResponse(vErr error) (ok, warning boo
227227
case errors.As(vErr, &missingRequiredFlowFieldsError):
228228

229229
outputOpts = output.Opts{
230-
Message: fmt.Sprintf("The DaVinci Flow Export JSON has been evaluated to be missing required fields. Please re-export the DaVinci flow."),
230+
Message: "The DaVinci Flow Export JSON has been evaluated to be missing required fields. Please re-export the DaVinci flow.",
231231
Result: output.ENUM_RESULT_FAILURE,
232232
Fields: map[string]interface{}{
233233
"Computed Difference": missingRequiredFlowFieldsError.Diff,
@@ -237,7 +237,7 @@ func (d *DaVinciValidator) OutputValidationResponse(vErr error) (ok, warning boo
237237
case errors.As(vErr, &unknownAdditionalFieldsError):
238238

239239
outputOpts = output.Opts{
240-
Message: fmt.Sprintf("The DaVinci flow will be accepted by the provider, but the DaVinci Flow Export contains unknown properties that cannot be validated. These parameters will be preserved on import to the DaVinci service, but there may be unpredictable results in difference calculation."),
240+
Message: "The DaVinci flow will be accepted by the provider, but the DaVinci Flow Export contains unknown properties that cannot be validated. These parameters will be preserved on import to the DaVinci service, but there may be unpredictable results in difference calculation.",
241241
Result: output.ENUM_RESULT_NOACTION_WARN,
242242
Fields: map[string]interface{}{
243243
"Computed Difference": unknownAdditionalFieldsError.Diff,
@@ -278,7 +278,7 @@ func (d *DaVinciValidator) OutputValidationResponse(vErr error) (ok, warning boo
278278

279279
case errors.Is(vErr, io.EOF):
280280
outputOpts = output.Opts{
281-
Message: fmt.Sprintf("The export contents cannot be empty"),
281+
Message: "The export contents cannot be empty",
282282
Result: output.ENUM_RESULT_FAILURE,
283283
}
284284

scripts/generate-test.sh

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/bin/bash
2+
3+
go build .
4+
5+
# Directory containing the files
6+
directory=$1
7+
8+
# Directory containing the script
9+
script_dir=$(dirname "$(realpath "$0")")
10+
11+
# Directory to generate HCL in
12+
DIR="$directory/testoutput"
13+
14+
if [ -d "$DIR" ]; then
15+
echo "Directory $DIR exists. Removing it."
16+
rm -r $DIR
17+
fi
18+
19+
# Initialize the command variable
20+
command=""
21+
22+
# Loop through all JSON (export) files in the directory
23+
for file in "$directory"/*.json; do
24+
25+
# ./dvtf-pingctl validate -e "$file"
26+
# validateResponse=$?
27+
# # Check if the command failed
28+
# if [ $validateResponse -eq 1 ]; then
29+
# echo "Validation failed"
30+
# exit 1
31+
# fi
32+
# if [ $validateResponse -eq 2 ]; then
33+
# echo "Validation warning"
34+
# fi
35+
36+
# Add each file as a parameter
37+
command+=" -e \"$file\""
38+
done
39+
40+
set -e
41+
42+
# Execute the command
43+
./dvtf-pingctl generate -o $DIR $command
44+
45+
if [ -d "$DIR" ]; then
46+
echo "Directory $DIR exists. Bootstrapping it."
47+
48+
cp $script_dir/../testing/bootstrap-hcl/* $DIR
49+
cp $directory/*.tftest.hcl $DIR
50+
51+
pushd $DIR
52+
terraform init
53+
terraform validate
54+
terraform test
55+
popd
56+
else
57+
echo "Directory $DIR does not exist."
58+
exit 1
59+
fi

scripts/test-bootstrap-hcl/main.tf

Lines changed: 0 additions & 19 deletions
This file was deleted.

scripts/test-bootstrap-hcl/variables.tf

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)