terraform-provider-libvirt/libvirt/utils_xslt.go

111 lines
2.9 KiB
Go

package libvirt
import (
"log"
"os"
"os/exec"
"strings"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
identitySpaceStripXSLT = `
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
`
)
// This is the function we use to detect if the XSLT attribute itself changed
// As we don't want to recreate resources when the XSLT is changed with whitespace,
// we specify the diff suppress function as the result of applying the identity
// transform to the xslt, stripping whitespace
// See https://www.terraform.io/docs/extend/schemas/schema-behaviors.html#diffsuppressfunc
func xsltDiffSupressFunc(_, old, new string, _ *schema.ResourceData) bool {
oldStrip, err := transformXML(old, identitySpaceStripXSLT)
if err != nil {
// fail, just use normal equality
log.Printf("[ERROR] Couldn't normalize XSLT stylesheet")
return old == new
}
newStrip, err := transformXML(new, identitySpaceStripXSLT)
if err != nil {
// fail, just use normal equality
log.Printf("[ERROR] Couldn't normalize XSLT stylesheet")
return old == new
}
return oldStrip == newStrip
}
// this function applies a XSLT transform to the xml data.
func transformXML(xmlS string, xsltS string) (string, error) {
// empty xslt is a no-op
if strings.TrimSpace(xsltS) == "" {
return xmlS, nil
}
xsltFile, err := os.CreateTemp("", "terraform-provider-libvirt-xslt")
if err != nil {
return "", err
}
defer os.Remove(xsltFile.Name()) // clean up
// we trim the xslt as it may contain space before the xml declaration
// because of HCL heredoc
if _, err := xsltFile.WriteString(strings.TrimSpace(xsltS)); err != nil {
return "", err
}
if err := xsltFile.Close(); err != nil {
return "", err
}
xmlFile, err := os.CreateTemp("", "terraform-provider-libvirt-xml")
if err != nil {
return "", err
}
defer os.Remove(xmlFile.Name()) // clean up
if _, err := xmlFile.WriteString(xmlS); err != nil {
return "", err
}
if err := xmlFile.Close(); err != nil {
return "", err
}
//nolint:gosec // G204 not sure why gosec complains
cmd := exec.Command("xsltproc",
"--nomkdir",
"--nonet",
"--nowrite",
xsltFile.Name(),
xmlFile.Name())
transformedXML, err := cmd.Output()
if err != nil {
log.Printf("[ERROR] Failed to run xsltproc (is it installed?)")
return xmlS, err
}
log.Printf("[DEBUG] Transformed XML with user specified XSLT:\n%s", transformedXML)
return string(transformedXML), err
}
// this function applies a XSLT transform to the xml data
// and is to be reused in all resource types
// your resource need to have a xml.xslt element in the schema.
func transformResourceXML(xml string, d *schema.ResourceData) (string, error) {
xslt, ok := d.GetOk("xml.0.xslt")
if !ok {
return xml, nil
}
return transformXML(xml, xslt.(string))
}