From 21f9bfabc1531eab317ed68bd14483ca738f6907 Mon Sep 17 00:00:00 2001 From: Duncan Mac-Vicar P Date: Sun, 23 Sep 2018 21:53:53 +0200 Subject: [PATCH] Initial prototype of a XML transformation hook based on XSLT --- examples/xslt/main.tf | 16 ++++ examples/xslt/nicmodel.xsl | 17 ++++ libvirt/resource_libvirt_domain.go | 20 ++++- libvirt/resource_libvirt_domain_test.go | 115 ++++++++++++++++++++++++ libvirt/utils_xslt.go | 59 ++++++++++++ 5 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 examples/xslt/main.tf create mode 100644 examples/xslt/nicmodel.xsl create mode 100644 libvirt/utils_xslt.go diff --git a/examples/xslt/main.tf b/examples/xslt/main.tf new file mode 100644 index 000000000..2247c8392 --- /dev/null +++ b/examples/xslt/main.tf @@ -0,0 +1,16 @@ +provider "libvirt" { + uri = "qemu:///system" +} + +resource "libvirt_domain" "xslt-demo-domain" { + name = "xslt-demo-domain" + memory = "512" + + network_interface { + network_name = "default" + } + + xml { + xslt = "${file("nicmodel.xsl")}" + } +} diff --git a/examples/xslt/nicmodel.xsl b/examples/xslt/nicmodel.xsl new file mode 100644 index 000000000..b72b22863 --- /dev/null +++ b/examples/xslt/nicmodel.xsl @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/libvirt/resource_libvirt_domain.go b/libvirt/resource_libvirt_domain.go index 3b7a66062..ad4caab4e 100644 --- a/libvirt/resource_libvirt_domain.go +++ b/libvirt/resource_libvirt_domain.go @@ -348,6 +348,20 @@ func resourceLibvirtDomain() *schema.Resource { Default: false, ForceNew: false, }, + "xml": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "xslt": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + }, + }, + }, }, } } @@ -456,8 +470,12 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error if err != nil { return fmt.Errorf("Error serializing libvirt domain: %s", err) } + log.Printf("[DEBUG] Generated XML for libvirt domain:\n%s", data) - log.Printf("[DEBUG] Creating libvirt domain with XML:\n%s", data) + data, err = transformResourceXML(data, d) + if err != nil { + return fmt.Errorf("Error applying XSLT stylesheet: %s", err) + } domain, err := virConn.DomainDefineXML(data) if err != nil { diff --git a/libvirt/resource_libvirt_domain_test.go b/libvirt/resource_libvirt_domain_test.go index 7f3389a45..4d527c600 100644 --- a/libvirt/resource_libvirt_domain_test.go +++ b/libvirt/resource_libvirt_domain_test.go @@ -1271,3 +1271,118 @@ func TestAccLibvirtDomain_Import(t *testing.T) { }, }) } + +func TestAccLibvirtDomain_XSLT_UnsupportedAttribute(t *testing.T) { + var domain libvirt.Domain + randomDomainName := acctest.RandString(10) + randomNetworkName := acctest.RandString(10) + + var config = fmt.Sprintf(` + resource "libvirt_network" "%s" { + name = "%s" + addresses = ["10.17.3.0/24"] + } + + resource "libvirt_domain" "%s" { + name = "%s" + network_interface = { + network_name = "default" + } + xml { + xslt = < + + + + + + + + + + + + + + + +EOF + } + }`, randomNetworkName, randomNetworkName, randomDomainName, randomDomainName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLibvirtDomainDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testAccCheckLibvirtDomainExists("libvirt_domain."+randomDomainName, &domain), + testAccCheckLibvirtDomainDescription(&domain, func(domainDef libvirtxml.Domain) error { + if domainDef.Devices.Interfaces[0].Model.Type != "e1000" { + return fmt.Errorf("Expecting XSLT to tranform network model to e1000") + } + return nil + }), + ), + }, + }, + }) +} + +func TestAccLibvirtDomain_XSLT_SupportedAttribute(t *testing.T) { + var domain libvirt.Domain + randomDomainName := acctest.RandString(10) + randomNetworkName := acctest.RandString(10) + + var config = fmt.Sprintf(` + resource "libvirt_network" "%s" { + name = "%s" + addresses = ["10.17.3.0/24"] + } + + resource "libvirt_domain" "%s" { + name = "%s" + network_interface = { + network_name = "default" + } + xml { + xslt = < + + + + + + + + + + + + + + +EOF + } + }`, randomNetworkName, randomNetworkName, randomDomainName, randomDomainName, randomNetworkName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLibvirtDomainDestroy, + Steps: []resource.TestStep{ + { + Config: config, + ExpectNonEmptyPlan: true, + Check: resource.ComposeTestCheckFunc( + testAccCheckLibvirtDomainExists("libvirt_domain."+randomDomainName, &domain), + resource.TestCheckResourceAttr( + "libvirt_domain."+randomDomainName, "network_interface.0.network_name", randomNetworkName), + ), + }, + }, + }) +} diff --git a/libvirt/utils_xslt.go b/libvirt/utils_xslt.go new file mode 100644 index 000000000..879e54edf --- /dev/null +++ b/libvirt/utils_xslt.go @@ -0,0 +1,59 @@ +package libvirt + +import ( + "io/ioutil" + "log" + "os" + "os/exec" + "strings" + + "github.com/hashicorp/terraform/helper/schema" +) + +// 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 + } + + xsltFile, err := ioutil.TempFile("", "terraform-provider-libvirt-xslt") + if err != nil { + log.Fatal(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.Write([]byte(strings.TrimSpace(xslt.(string)))); err != nil { + log.Fatal(err) + } + + if err := xsltFile.Close(); err != nil { + log.Fatal(err) + } + + xmlFile, err := ioutil.TempFile("", "terraform-provider-libvirt-xml") + if err != nil { + log.Fatal(err) + } + defer os.Remove(xmlFile.Name()) // clean up + + if _, err := xmlFile.Write([]byte(xml)); err != nil { + log.Fatal(err) + } + + if err := xmlFile.Close(); err != nil { + log.Fatal(err) + } + + cmd := exec.Command("xsltproc", xsltFile.Name(), xmlFile.Name()) + transformedXML, err := cmd.Output() + if err != nil { + return xml, err + } + log.Printf("[DEBUG] Transformed XML with user specified XSLT:\n%s", transformedXML) + return string(transformedXML), nil +}