diff options
-rw-r--r-- | docs/providers/libvirt/r/domain.html.markdown | 24 | ||||
-rw-r--r-- | libvirt/domain_def.go | 6 | ||||
-rw-r--r-- | libvirt/provider_test.go | 2 | ||||
-rw-r--r-- | libvirt/resource_libvirt_domain.go | 67 | ||||
-rw-r--r-- | libvirt/resource_libvirt_domain_test.go | 74 | ||||
-rw-r--r-- | vendor.yml | 2 |
6 files changed, 165 insertions, 10 deletions
diff --git a/docs/providers/libvirt/r/domain.html.markdown b/docs/providers/libvirt/r/domain.html.markdown index 995420da..b9a5500e 100644 --- a/docs/providers/libvirt/r/domain.html.markdown +++ b/docs/providers/libvirt/r/domain.html.markdown @@ -43,7 +43,29 @@ The following arguments are supported: The following extra argument is provided for CoreOS images: -* `coreos_ignition` - (Optional) The name of a CoreOS Ignition file. +* `coreos_ignition` - (Optional) This can be set to the name of an existing ignition +file or alternatively can be set to the rendered value of a Terraform ignition provider object. + +An example where a Terraform ignition provider object is used: +``` +# Systemd unit resource containing the unit definition +resource "ignition_systemd_unit" "example" { + name = "example.service" + content = "[Service]\nType=oneshot\nExecStart=/usr/bin/echo Hello World\n\n[Install]\nWantedBy=multi-user.target" +} + +# Ignition config include the previous defined systemd unit resource +resource "ignition_config" "example" { + systemd = [ + "${ignition_systemd_unit.example.id}", + ] +} + +resource "libvirt_domain" "my_machine" { + coreos_ignition = "${ignition_config.example.rendered}" + ... +} +``` Note that to make use of Ignition files with CoreOS the host must be running QEMU v2.6 or greater. diff --git a/libvirt/domain_def.go b/libvirt/domain_def.go index e6952d79..46dadaaf 100644 --- a/libvirt/domain_def.go +++ b/libvirt/domain_def.go @@ -57,8 +57,9 @@ type defDomain struct { } type defMetadata struct { - XMLName xml.Name `xml:"http://github.com/dmacvicar/terraform-provider-libvirt/ user_data"` - Xml string `xml:",cdata"` + XMLName xml.Name `xml:"http://github.com/dmacvicar/terraform-provider-libvirt/ user_data"` + Xml string `xml:",cdata"` + IgnitionFile string `xml:",ignition_file,omitempty"` } type defOs struct { @@ -143,3 +144,4 @@ func newDomainDef() defDomain { return domainDef } + diff --git a/libvirt/provider_test.go b/libvirt/provider_test.go index 662b1185..c130f86b 100644 --- a/libvirt/provider_test.go +++ b/libvirt/provider_test.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" + ignition "github.com/hashicorp/terraform/builtin/providers/ignition" ) var testAccProviders map[string]terraform.ResourceProvider @@ -15,6 +16,7 @@ func init() { testAccProvider = Provider().(*schema.Provider) testAccProviders = map[string]terraform.ResourceProvider{ "libvirt": testAccProvider, + "ignition": ignition.Provider(), } } diff --git a/libvirt/resource_libvirt_domain.go b/libvirt/resource_libvirt_domain.go index 26a121ff..00bbaf19 100644 --- a/libvirt/resource_libvirt_domain.go +++ b/libvirt/resource_libvirt_domain.go @@ -1,6 +1,7 @@ package libvirt import ( + "encoding/json" "encoding/xml" "fmt" "log" @@ -9,6 +10,8 @@ import ( "strings" "time" + "crypto/sha256" + "encoding/hex" "github.com/davecgh/go-spew/spew" libvirt "github.com/dmacvicar/libvirt-go" "github.com/hashicorp/terraform/helper/schema" @@ -20,6 +23,11 @@ func init() { spew.Config.Indent = "\t" } +func hash(s string) string { + sha := sha256.Sum256([]byte(s)) + return hex.EncodeToString(sha[:]) +} + func resourceLibvirtDomain() *schema.Resource { return &schema.Resource{ Create: resourceLibvirtDomainCreate, @@ -145,12 +153,38 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error } if ignition, ok := d.GetOk("coreos_ignition"); ok { - ignitionFile := ignition.(string) - if _, err := os.Stat(ignitionFile); os.IsNotExist(err) { - return fmt.Errorf("Could not find ignition file '%s'", ignitionFile) + var file bool + file = true + ignitionString := ignition.(string) + if _, err := os.Stat(ignitionString); err != nil { + var js map[string]interface{} + if err_conf := json.Unmarshal([]byte(ignitionString), &js); err_conf != nil { + return fmt.Errorf("coreos_ignition parameter is neither a file "+ + "nor a valid json object %s", ignition) + } + log.Printf("[DEBUG] about to set file to false") + file = false } + log.Printf("[DEBUG] file %s", file) var fw_cfg []defCmd - ign_str := fmt.Sprintf("name=opt/com.coreos/config,file=%s", ignitionFile) + var ign_str string + if !file { + ignitionHash := hash(ignitionString) + tempFileName := fmt.Sprint("/tmp/", ignitionHash, ".ign") + tempFile, err := os.Create(tempFileName) + defer tempFile.Close() + if err != nil { + return fmt.Errorf("Cannot create temporary ignition file %s", tempFileName) + } + if _, err := tempFile.WriteString(ignitionString); err != nil { + return fmt.Errorf("Cannot write Ignition object to temporary "+ + "ignition file %s", tempFileName) + } + domainDef.Metadata.TerraformLibvirt.IgnitionFile = tempFileName + ign_str = fmt.Sprintf("name=opt/com.coreos/config,file=%s", tempFileName) + } else if file { + ign_str = fmt.Sprintf("name=opt/com.coreos/config,file=%s", ignitionString) + } fw_cfg = append(fw_cfg, defCmd{"-fw_cfg"}) fw_cfg = append(fw_cfg, defCmd{ign_str}) domainDef.CmdLine.Cmd = fw_cfg @@ -197,12 +231,14 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error if consoleCount, ok := d.GetOk("console.#"); ok { var consoles []defConsole - for i :=0; i < consoleCount.(int); i++ { + for i := 0; i < consoleCount.(int); i++ { console := defConsole{} consolePrefix := fmt.Sprintf("console.%d", i) console.Type = d.Get(consolePrefix + ".type").(string) - console.Source.Path = d.Get(consolePrefix + ".source_path").(string) console.Target.Port = d.Get(consolePrefix + ".target_port").(string) + if source_path, ok := d.GetOk(consolePrefix + ".source_path"); ok { + console.Source.Path = source_path.(string) + } if target_type, ok := d.GetOk(consolePrefix + ".target_type"); ok { console.Target.Type = target_type.(string) } @@ -756,6 +792,25 @@ func resourceLibvirtDomainDelete(d *schema.ResourceData, meta interface{}) error } defer domain.Free() + xmlDesc, err := domain.GetXMLDesc(0) + if err != nil { + return fmt.Errorf("Error retrieving libvirt domain XML description: %s", err) + } + + domainDef := newDomainDef() + err = xml.Unmarshal([]byte(xmlDesc), &domainDef) + if err != nil { + return fmt.Errorf("Error reading libvirt domain XML description: %s", err) + } + + if ignitionFile := domainDef.Metadata.TerraformLibvirt.IgnitionFile; ignitionFile != "" { + log.Printf("[DEBUG] deleting ignition file") + err = os.Remove(ignitionFile) + if err != nil && !os.IsNotExist(err) { + return fmt.Errorf("Error removing Ignition file %s: %s", ignitionFile, err) + } + } + state, err := domain.GetState() if err != nil { return fmt.Errorf("Couldn't get info about domain: %s", err) diff --git a/libvirt/resource_libvirt_domain_test.go b/libvirt/resource_libvirt_domain_test.go index 73feefe5..6749283c 100644 --- a/libvirt/resource_libvirt_domain_test.go +++ b/libvirt/resource_libvirt_domain_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" //"gopkg.in/alexzorin/libvirt-go.v2" + "encoding/xml" libvirt "github.com/dmacvicar/libvirt-go" ) @@ -208,6 +209,44 @@ func TestAccLibvirtDomain_NetworkInterface(t *testing.T) { }) } +func TestAccLibvirtDomain_IgnitionObject(t *testing.T) { + var domain libvirt.VirDomain + + var config = fmt.Sprintf(` + resource "ignition_systemd_unit" "acceptance-test-systemd" { + name = "example.service" + content = "[Service]\nType=oneshot\nExecStart=/usr/bin/echo Hello World\n\n[Install]\nWantedBy=multi-user.target" + } + + resource "ignition_config" "acceptance-test-config" { + systemd = [ + "${ignition_systemd_unit.acceptance-test-systemd.id}", + ] + } + + resource "libvirt_domain" "acceptance-test-domain" { + name = "terraform-test-domain" + coreos_ignition = "${ignition_config.acceptance-test-config.rendered}" + } + `) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLibvirtDomainDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: config, + ExpectNonEmptyPlan: true, + Check: resource.ComposeTestCheckFunc( + testAccCheckLibvirtDomainExists("libvirt_domain.acceptance-test-domain", &domain), + testAccCheckIgnitionFileNameExists(&domain), + ), + }, + }, + }) +} + func testAccCheckLibvirtDomainDestroy(s *terraform.State) error { virtConn := testAccProvider.Meta().(*Client).libvirt @@ -263,3 +302,38 @@ func testAccCheckLibvirtDomainExists(n string, domain *libvirt.VirDomain) resour return nil } } + +func testAccCheckIgnitionFileNameExists(domain *libvirt.VirDomain) resource.TestCheckFunc { + return func(s *terraform.State) error { + var ignStr string + for _, rs := range s.RootModule().Resources { + if rs.Type != "libvirt_domain" { + continue + } + ignStr = rs.Primary.Attributes["coreos_ignition"] + } + + xmlDesc, err := domain.GetXMLDesc(0) + if err != nil { + return fmt.Errorf("Error retrieving libvirt domain XML description: %s", err) + } + + domainDef := newDomainDef() + err = xml.Unmarshal([]byte(xmlDesc), &domainDef) + if err != nil { + return fmt.Errorf("Error reading libvirt domain XML description: %s", err) + } + + ignitionFile := domainDef.Metadata.TerraformLibvirt.IgnitionFile + if ignitionFile == "" { + return fmt.Errorf("No ignition file meta-data") + } + + hashStr := hash(ignStr) + hashFile := fmt.Sprint("/tmp/", hashStr, ".ign") + if ignitionFile != hashFile { + return fmt.Errorf("Igntion file metadata incorrect %s %s", ignitionFile, hashFile) + } + return nil + } +} @@ -23,7 +23,7 @@ vendors: - path: github.com/hashicorp/logutils rev: 0dc08b1671f34c4250ce212759ebd880f743d883 - path: github.com/hashicorp/terraform - rev: v0.8.0 + rev: v0.8.5 - path: github.com/hooklift/assert rev: c7786599453421cddf9aa8a3a7b537f567d1ac1b - path: github.com/hooklift/iso9660 |