From d5872d93a82c6983dd8c0a2a3c4f1f1ff75cf02b Mon Sep 17 00:00:00 2001 From: Jieyu Tian Date: Thu, 17 Oct 2024 10:43:39 -0400 Subject: [PATCH] feat: Preserve output types in bpmetadata interface (#2647) --- cli/bpmetadata/cmd.go | 3 + cli/bpmetadata/tfconfig.go | 22 +++++++ cli/bpmetadata/tfconfig_test.go | 58 ++++++++++++++++++- ...faces_with_full_output_types_metadata.yaml | 17 ++++++ ...rfaces_with_new_output_types_metadata.yaml | 16 +++++ ...es_with_partial_output_types_metadata.yaml | 16 +++++ ...rfaces_without_output_types_metadata.yaml} | 2 +- 7 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 cli/testdata/bpmetadata/metadata/interfaces_with_full_output_types_metadata.yaml create mode 100644 cli/testdata/bpmetadata/metadata/interfaces_with_new_output_types_metadata.yaml create mode 100644 cli/testdata/bpmetadata/metadata/interfaces_with_partial_output_types_metadata.yaml rename cli/testdata/bpmetadata/metadata/{interfaces_without_types_metadata.yaml => interfaces_without_output_types_metadata.yaml} (86%) diff --git a/cli/bpmetadata/cmd.go b/cli/bpmetadata/cmd.go index 1a4958d9690..ab43dab1625 100644 --- a/cli/bpmetadata/cmd.go +++ b/cli/bpmetadata/cmd.go @@ -256,6 +256,9 @@ func CreateBlueprintMetadata(bpPath string, bpMetadataObj *BlueprintMetadata) (* // Merge existing connections (if any) into the newly generated interfaces mergeExistingConnections(bpMetadataObj.Spec.Interfaces, existingInterfaces) + // Merge existing output types (if any) into the newly generated interfaces + mergeExistingOutputTypes(bpMetadataObj.Spec.Interfaces, existingInterfaces) + // get blueprint requirements rolesCfgPath := path.Join(repoDetails.Source.BlueprintRootPath, tfRolesFileName) svcsCfgPath := path.Join(repoDetails.Source.BlueprintRootPath, tfServicesFileName) diff --git a/cli/bpmetadata/tfconfig.go b/cli/bpmetadata/tfconfig.go index 819feda47cd..e589bdc8558 100644 --- a/cli/bpmetadata/tfconfig.go +++ b/cli/bpmetadata/tfconfig.go @@ -522,6 +522,28 @@ func mergeExistingConnections(newInterfaces, existingInterfaces *BlueprintInterf } } +// mergeExistingOutputTypes merges existing output types from an old BlueprintInterface into a new one, +// preserving manually authored types. +func mergeExistingOutputTypes(newInterfaces, existingInterfaces *BlueprintInterface) { + if existingInterfaces == nil { + return // Nothing to merge if existingInterfaces is nil + } + + existingOutputs := make(map[string]*BlueprintOutput) + for _, output := range existingInterfaces.Outputs { + existingOutputs[output.Name] = output + } + + for i, output := range newInterfaces.Outputs { + if output.Type != nil { + continue + } + if existingOutput, ok := existingOutputs[output.Name]; ok && existingOutput.Type != nil { + newInterfaces.Outputs[i].Type = existingOutput.Type + } + } +} + // UpdateOutputTypes generates the terraform.tfstate file, extracts output types from it, // and updates the output types in the provided BlueprintInterface. func updateOutputTypes(bpPath string, bpInterfaces *BlueprintInterface) error { diff --git a/cli/bpmetadata/tfconfig_test.go b/cli/bpmetadata/tfconfig_test.go index 123d86a95d3..b65a44dbef7 100644 --- a/cli/bpmetadata/tfconfig_test.go +++ b/cli/bpmetadata/tfconfig_test.go @@ -346,6 +346,62 @@ func TestMergeExistingConnections(t *testing.T) { } } +func TestMergeExistingOutputTypes(t *testing.T) { + tests := []struct { + name string + newInterfacesFile string + existingInterfacesFile string + expectedInterfacesFile string + }{ + { + name: "No existing types", + newInterfacesFile: "interfaces_without_output_types_metadata.yaml", + existingInterfacesFile: "interfaces_without_output_types_metadata.yaml", + expectedInterfacesFile: "interfaces_without_output_types_metadata.yaml", + }, + { + name: "One complex existing type is preserved", + newInterfacesFile: "interfaces_without_output_types_metadata.yaml", + existingInterfacesFile: "interfaces_with_partial_output_types_metadata.yaml", + expectedInterfacesFile: "interfaces_with_partial_output_types_metadata.yaml", + }, + { + name: "All existing types (both simple and complex) are preserved", + newInterfacesFile: "interfaces_without_output_types_metadata.yaml", + existingInterfacesFile: "interfaces_with_full_output_types_metadata.yaml", + expectedInterfacesFile: "interfaces_with_full_output_types_metadata.yaml", + }, + { + name: "Previous types are not overwriting newly generated types", + newInterfacesFile: "interfaces_with_new_output_types_metadata.yaml", + existingInterfacesFile: "interfaces_with_partial_output_types_metadata.yaml", + expectedInterfacesFile: "interfaces_with_new_output_types_metadata.yaml", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Load new interfaces from file + newInterfaces, err := UnmarshalMetadata(metadataTestdataPath, tt.newInterfacesFile) + require.NoError(t, err) + + // Load existing interfaces from file + existingInterfaces, err := UnmarshalMetadata(metadataTestdataPath, tt.existingInterfacesFile) + require.NoError(t, err) + + // Perform the merge + mergeExistingOutputTypes(newInterfaces.Spec.Interfaces, existingInterfaces.Spec.Interfaces) + + // Load expected interfaces from file + expectedInterfaces, err := UnmarshalMetadata(metadataTestdataPath, tt.expectedInterfacesFile) + require.NoError(t, err) + + // Assert that the merged interfaces match the expected outcome + assert.Equal(t, expectedInterfaces.Spec.Interfaces, newInterfaces.Spec.Interfaces) + }) + } +} + func TestTFIncompleteProviderVersions(t *testing.T) { tests := []struct { name string @@ -429,7 +485,7 @@ func TestUpdateOutputTypes(t *testing.T) { { name: "Update output types from state", bpPath: "sample-module", - interfacesFile: "interfaces_without_types_metadata.yaml", + interfacesFile: "interfaces_without_output_types_metadata.yaml", stateFile: "terraform.tfstate", expectedOutputs: []*BlueprintOutput{ { diff --git a/cli/testdata/bpmetadata/metadata/interfaces_with_full_output_types_metadata.yaml b/cli/testdata/bpmetadata/metadata/interfaces_with_full_output_types_metadata.yaml new file mode 100644 index 00000000000..2359bc181a1 --- /dev/null +++ b/cli/testdata/bpmetadata/metadata/interfaces_with_full_output_types_metadata.yaml @@ -0,0 +1,17 @@ +apiVersion: blueprints.cloud.google.com/v1alpha1 +kind: BlueprintMetadata +metadata: + name: terraform-google-network +spec: + interfaces: + outputs: + - name: cluster_id + description: Cluster ID + type: string + - name: endpoint + description: Cluster endpoint + type: + - list + - - object: + - host: string + port: number \ No newline at end of file diff --git a/cli/testdata/bpmetadata/metadata/interfaces_with_new_output_types_metadata.yaml b/cli/testdata/bpmetadata/metadata/interfaces_with_new_output_types_metadata.yaml new file mode 100644 index 00000000000..424365c833c --- /dev/null +++ b/cli/testdata/bpmetadata/metadata/interfaces_with_new_output_types_metadata.yaml @@ -0,0 +1,16 @@ +apiVersion: blueprints.cloud.google.com/v1alpha1 +kind: BlueprintMetadata +metadata: + name: terraform-google-network +spec: + interfaces: + outputs: + - name: cluster_id + description: Cluster ID + type: string + - name: endpoint + description: Cluster endpoint + type: + - tuple + - string + number \ No newline at end of file diff --git a/cli/testdata/bpmetadata/metadata/interfaces_with_partial_output_types_metadata.yaml b/cli/testdata/bpmetadata/metadata/interfaces_with_partial_output_types_metadata.yaml new file mode 100644 index 00000000000..fc83b3dc8fe --- /dev/null +++ b/cli/testdata/bpmetadata/metadata/interfaces_with_partial_output_types_metadata.yaml @@ -0,0 +1,16 @@ +apiVersion: blueprints.cloud.google.com/v1alpha1 +kind: BlueprintMetadata +metadata: + name: terraform-google-network +spec: + interfaces: + outputs: + - name: cluster_id + description: Cluster ID + - name: endpoint + description: Cluster endpoint + type: + - list + - - object: + - host: string + port: number \ No newline at end of file diff --git a/cli/testdata/bpmetadata/metadata/interfaces_without_types_metadata.yaml b/cli/testdata/bpmetadata/metadata/interfaces_without_output_types_metadata.yaml similarity index 86% rename from cli/testdata/bpmetadata/metadata/interfaces_without_types_metadata.yaml rename to cli/testdata/bpmetadata/metadata/interfaces_without_output_types_metadata.yaml index 50320f98b65..35e68206506 100644 --- a/cli/testdata/bpmetadata/metadata/interfaces_without_types_metadata.yaml +++ b/cli/testdata/bpmetadata/metadata/interfaces_without_output_types_metadata.yaml @@ -1,4 +1,4 @@ -# interfaces_without_types_metadata.yaml +# interfaces_without_output_types_metadata.yaml apiVersion: blueprints.cloud.google.com/v1alpha1 kind: BlueprintMetadata metadata: