Skip to content

Commit

Permalink
add mpdev tf overwrite for metadata display (#75)
Browse files Browse the repository at this point in the history
* add mpdev tf overwrite for metadata display

* add OverwriteDisplay to overwritecmd

* change yaml example format to standard array/object
  • Loading branch information
rzy-goog committed Aug 30, 2023
1 parent 869bd0f commit b3f4fde
Show file tree
Hide file tree
Showing 3 changed files with 281 additions and 5 deletions.
12 changes: 9 additions & 3 deletions mpdev/cmd/tf/overwritecmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
package tf

import (
"io"
"os"

"github.com/GoogleCloudPlatform/marketplace-tools/mpdev/internal/docs"
"github.com/GoogleCloudPlatform/marketplace-tools/mpdev/internal/tf"
"github.com/spf13/cobra"
"io"
"os"
)

// GetOverwriteCommand returns `overwrite` command used to create mpdev resources.
Expand Down Expand Up @@ -57,5 +58,10 @@ func overwriteRunE(_ *cobra.Command, _ []string) (err error) {
return err
}

return tf.OverwriteMetadata(config, dir)
err = tf.OverwriteMetadata(config, dir)
if err != nil {
return err
}

return tf.OverwriteDisplay(config, dir)
}
76 changes: 76 additions & 0 deletions mpdev/internal/tf/overwrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package tf
import (
"encoding/json"
"fmt"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/hcl/v2/hclwrite"
Expand All @@ -30,12 +31,18 @@ import (
)

const metadataFile = "metadata.yaml"
const metadataDisplayFile = "metadata.display.yaml"

type overwriteConfig struct {
Variables []string
Replacements map[string]string
}

type EnumValueLabel struct {
Label string `json:"label"`
Value string `json:"value"`
}

// OverwriteTf replaces default variable values in Terraform modules
func OverwriteTf(config *overwriteConfig, dir string) error {
fmt.Printf("Replacing the default values of the variables: %s\n", config.Variables)
Expand Down Expand Up @@ -208,3 +215,72 @@ func OverwriteMetadata(config *overwriteConfig, dir string) error {
fmt.Printf("Successfully replaced default values in %s\n", metadataFile)
return nil
}

// OverwriteDisplay replaces variable values in Blueprint metadata display file.
func OverwriteDisplay(config *overwriteConfig, dir string) error {
fmt.Printf("Replacing the values of the display variables: %s in %s\n",
config.Variables, metadataDisplayFile)

data, err := os.ReadFile(path.Join(dir, metadataDisplayFile))
if err != nil {
// CLI only modules will not have a metadata display file. Ignore file not found errors
if os.IsNotExist(err) {
return nil
}
return err
}

json, err := yaml.YAMLToJSON(data)
if err != nil {
return fmt.Errorf("failure parsing %s error: %w", metadataDisplayFile, err)
}

for _, variable := range config.Variables {
variableQuery := fmt.Sprintf(`spec.ui.input.variables.%s`, variable)
variableInfo := gjson.GetBytes(json, variableQuery).String()
if variableInfo == "" {
return fmt.Errorf("missing valid display info for variable: %s in %s",
variable, metadataDisplayFile)
}

enumValueLabels := gjson.Get(variableInfo, "enumValueLabels").Array()
if len(enumValueLabels) == 0 {
fmt.Printf("No enum value labels for display variable: %s in %s\n",
variable, metadataDisplayFile)
continue
}

var replacementEnumValueLabels []EnumValueLabel
for _, enumValueLabel := range enumValueLabels {
currValue := enumValueLabel.Get("value").String()
currLabel := enumValueLabel.Get("label").String()
replaceVal, ok := config.Replacements[currValue]
if !ok {
return fmt.Errorf("enum value: %s of variable: %s in %s not found"+
" in replacements", currValue, variable, metadataDisplayFile)
}
replacementEnumValueLabels = append(replacementEnumValueLabels, EnumValueLabel{Label: currLabel, Value: replaceVal})
}

enumQuery := fmt.Sprintf(`spec.ui.input.variables.%s.enumValueLabels`, variable)
json, err = sjson.SetBytes(json, enumQuery, replacementEnumValueLabels)
if err != nil {
return fmt.Errorf("error setting default value of variable: %s. error: %w",
variable, err)
}

}

modifiedYaml, err := yaml.JSONToYAML([]byte(json))
if err != nil {
return err
}

err = os.WriteFile(path.Join(dir, metadataDisplayFile), modifiedYaml, 0644)
if err != nil {
return err
}

fmt.Printf("Successfully replaced display values in %s\n", metadataDisplayFile)
return nil
}
198 changes: 196 additions & 2 deletions mpdev/internal/tf/overwrite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
package tf

import (
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"os"
"path"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)

func TestOverwriteTf(t *testing.T) {
Expand Down Expand Up @@ -308,6 +309,107 @@ func getDirContents(dir string) (map[string]string, error) {
return fileContents, err
}

func TestOverwriteDisplay(t *testing.T) {
testcases := []struct {
name string
originalMetadataDisplay string
expectedMetadataDisplay string
overwriteConfig overwriteConfig
errorContains string
}{
{
name: "Overwrite single display variable enum values",
originalMetadataDisplay: metadataDisplayWithEnumsSingle,
expectedMetadataDisplay: metadataDisplayWithEnumsSingleReplaced,
overwriteConfig: overwriteConfig{
Variables: []string{"source_image"},
Replacements: map[string]string{
"projects/click-to-deploy-images/global/images/wordpress-1": "projects/replacement/global/images/wordpress-1-new",
},
},
},
{
name: "Overwrite multiple display variable enum values",
originalMetadataDisplay: metadataDisplayWithEnumsDouble,
expectedMetadataDisplay: metadataDisplayWithEnumsDoubleReplaced,
overwriteConfig: overwriteConfig{
Variables: []string{"source_image", "another_image"},
Replacements: map[string]string{
"projects/click-to-deploy-images/global/images/wordpress-1": "projects/replacement/global/images/wordpress-1-new",
"projects/click-to-deploy-images/global/images/wordpress-2": "projects/replacement/global/images/wordpress-2-new",
"projects/click-to-deploy-images/global/images/wordpress-3": "projects/replacement/global/images/wordpress-3-new",
},
},
},
{
name: "No changes if no display variable enum value labels",
originalMetadataDisplay: metadataDisplayNoEnums,
expectedMetadataDisplay: metadataDisplayNoEnums,
overwriteConfig: overwriteConfig{
Variables: []string{"source_image"},
Replacements: map[string]string{},
},
}, {
name: "Fail when metadata display is invalid yaml",
originalMetadataDisplay: "- not validyaml\ninvalid-",
errorContains: "failure parsing metadata.display.yaml",
}, {
name: "Fail when display variable not present in Metadata display",
originalMetadataDisplay: metadata,
overwriteConfig: overwriteConfig{
Variables: []string{"missing_variable"},
Replacements: map[string]string{
"original-value": "new-value",
},
},
errorContains: "missing valid display info for variable: missing_variable",
}, {
name: "Fail when display variable enum value is not in replacements",
originalMetadataDisplay: metadataDisplayWithEnumsSingle,
overwriteConfig: overwriteConfig{
Variables: []string{"source_image"},
Replacements: map[string]string{
"non-existent": "new-value",
},
},
errorContains: "enum value: projects/click-to-deploy-images/global/images/wordpress-1 of variable: source_image in metadata.display.yaml not found in replacements",
}}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "tftest")
assert.NoError(t, err)
defer os.RemoveAll(tmpDir)

err = os.WriteFile(path.Join(tmpDir, "metadata.display.yaml"),
[]byte(tc.originalMetadataDisplay), 0600)
assert.NoError(t, err)

err = OverwriteDisplay(&tc.overwriteConfig, tmpDir)

if tc.errorContains == "" {
assert.NoError(t, err)

metadataDisplayBytes, err := os.ReadFile(path.Join(tmpDir, "metadata.display.yaml"))
assert.NoError(t, err)
actualMetadataDisplay := make(map[interface{}]interface{})
expectedMetadataDisplay := make(map[interface{}]interface{})

err = yaml.Unmarshal(metadataDisplayBytes, &actualMetadataDisplay)
assert.NoError(t, err)

err = yaml.Unmarshal([]byte(tc.expectedMetadataDisplay), expectedMetadataDisplay)
assert.NoError(t, err)

assert.Equal(t, expectedMetadataDisplay, actualMetadataDisplay)
} else {
assert.Error(t, err)
assert.ErrorContains(t, err, tc.errorContains)
}
})
}
}

var mainTf string = `
resource "google_compute_instance_template" "template" {
name = "template"
Expand Down Expand Up @@ -406,3 +508,95 @@ spec:
description: The image name for the disk for the VM instance.
varType: string
`

var metadataDisplayWithEnumsSingle string = `
spec:
ui:
input:
variables:
source_image:
name: source_image
title: Source Image
enumValueLabels:
- label: wordpress-1
value: projects/click-to-deploy-images/global/images/wordpress-1
xGoogleProperty:
type: ET_GCE_DISK_IMAGE
`

var metadataDisplayWithEnumsSingleReplaced string = `
spec:
ui:
input:
variables:
source_image:
name: source_image
title: Source Image
enumValueLabels:
- label: wordpress-1
value: projects/replacement/global/images/wordpress-1-new
xGoogleProperty:
type: ET_GCE_DISK_IMAGE
`

var metadataDisplayWithEnumsDouble string = `
spec:
ui:
input:
variables:
source_image:
name: source_image
title: Source Image
enumValueLabels:
- label: wordpress-1
value: projects/click-to-deploy-images/global/images/wordpress-1
- label: wordpress-2
value: projects/click-to-deploy-images/global/images/wordpress-2
xGoogleProperty:
type: ET_GCE_DISK_IMAGE
another_image:
name: another_image
title: Another Image
enumValueLabels:
- label: wordpress-3
value: projects/click-to-deploy-images/global/images/wordpress-3
xGoogleProperty:
type: ET_GCE_DISK_IMAGE
`

var metadataDisplayWithEnumsDoubleReplaced string = `
spec:
ui:
input:
variables:
source_image:
name: source_image
title: Source Image
enumValueLabels:
- label: wordpress-1
value: projects/replacement/global/images/wordpress-1-new
- label: wordpress-2
value: projects/replacement/global/images/wordpress-2-new
xGoogleProperty:
type: ET_GCE_DISK_IMAGE
another_image:
name: another_image
title: Another Image
enumValueLabels:
- label: wordpress-3
value: projects/replacement/global/images/wordpress-3-new
xGoogleProperty:
type: ET_GCE_DISK_IMAGE
`

var metadataDisplayNoEnums string = `
spec:
ui:
input:
variables:
source_image:
name: source_image
title: Source Image
xGoogleProperty:
type: ET_GCE_DISK_IMAGE
`

0 comments on commit b3f4fde

Please sign in to comment.