Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions libvirt/data_source_libvirt_network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package libvirt

import (
"fmt"
"net"
"strconv"

"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
)

// a libvirt network DNS host template datasource
//
// Datasource example:
//
// data "libvirt_network_dns_host_template" "k8smasters" {
// count = "${var.master_count}"
// ip = "${var.master_ips[count.index]}"
// hostname = "master-${count.index}"
// }
//
// resource "libvirt_network" "k8snet" {
// ...
// dns = [{
// hosts = [ "${flatten(data.libvirt_network_dns_host_template.k8smasters.*.rendered)}" ]
// }]
// ...
// }
//
func datasourceLibvirtNetworkDNSHostTemplate() *schema.Resource {
return &schema.Resource{
Read: resourceLibvirtNetworkDNSHostRead,
Schema: map[string]*schema.Schema{
"ip": {
Type: schema.TypeString,
Required: true,
},
"hostname": {
Type: schema.TypeString,
Required: true,
},
"rendered": {
Type: schema.TypeMap,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Computed: true,
},
},
}
}

func resourceLibvirtNetworkDNSHostRead(d *schema.ResourceData, meta interface{}) error {
dnsHost := map[string]interface{}{}
if address, ok := d.GetOk("ip"); ok {
ip := net.ParseIP(address.(string))
if ip == nil {
return fmt.Errorf("Could not parse address '%s'", address)
}
dnsHost["ip"] = ip.String()
}
if hostname, ok := d.GetOk("hostname"); ok {
dnsHost["hostname"] = hostname.(string)
}
d.Set("rendered", dnsHost)
d.SetId(strconv.Itoa(hashcode.String(fmt.Sprintf("%v", dnsHost))))

return nil
}
53 changes: 53 additions & 0 deletions libvirt/data_source_libvirt_network_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package libvirt

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccLibvirtNetworkDataSource_DNSHostTemplate(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLibvirtNetworkDestroy,
Steps: []resource.TestStep{

{
Config: `data "libvirt_network_dns_host_template" "bootstrap" {
count = 2
ip = "1.1.1.${count.index}"
hostname = "myhost${count.index}"
}`,
Check: resource.ComposeTestCheckFunc(
checkDNSHostTemplate("data.libvirt_network_dns_host_template.bootstrap.0", "ip", "1.1.1.0"),
checkDNSHostTemplate("data.libvirt_network_dns_host_template.bootstrap.0", "hostname", "myhost0"),
checkDNSHostTemplate("data.libvirt_network_dns_host_template.bootstrap.1", "ip", "1.1.1.1"),
checkDNSHostTemplate("data.libvirt_network_dns_host_template.bootstrap.1", "hostname", "myhost1"),
),
},
},
})
}

func checkDNSHostTemplate(id, name, value string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[id]
if !ok {
return fmt.Errorf("Not found: %s", id)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}

v := rs.Primary.Attributes[name]
if v != value {
return fmt.Errorf(
"Value for %s is %s, not %s", name, v, value)
}

return nil
}
}
4 changes: 4 additions & 0 deletions libvirt/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ func Provider() terraform.ResourceProvider {
"libvirt_ignition": resourceIgnition(),
},

DataSourcesMap: map[string]*schema.Resource{
"libvirt_network_dns_host_template": datasourceLibvirtNetworkDNSHostTemplate(),
},

ConfigureFunc: providerConfigure,
}
}
Expand Down
65 changes: 60 additions & 5 deletions libvirt/resource_libvirt_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,31 @@ func resourceLibvirtNetwork() *schema.Resource {
},
},
},
"hosts": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"ip": {
Type: schema.TypeString,
// This should be required, but Terraform does validation too early
// and therefore doesn't recognize that this is set when assigning from
// a rendered dns_host template.
Optional: true,
ForceNew: true,
},
"hostname": {
Type: schema.TypeString,
// This should be required, but Terraform does validation too early
// and therefore doesn't recognize that this is set when assigning from
// a rendered dns_host template.
Optional: true,
ForceNew: true,
},
},
},
},
},
},
},
Expand Down Expand Up @@ -243,11 +268,8 @@ func resourceLibvirtNetworkCreate(d *schema.ResourceData, meta interface{}) erro
if err != nil {
return fmt.Errorf("Could not set DHCP from adresses '%s'", err)
}
var dnsForwarders []libvirtxml.NetworkDNSForwarder
if dnsForwardCount, ok := d.GetOk(dnsPrefix + ".forwarders.#"); ok {
dns := libvirtxml.NetworkDNS{
Forwarders: []libvirtxml.NetworkDNSForwarder{},
}

for i := 0; i < dnsForwardCount.(int); i++ {
forward := libvirtxml.NetworkDNSForwarder{}
forwardPrefix := fmt.Sprintf(dnsPrefix+".forwarders.%d", i)
Expand All @@ -261,7 +283,40 @@ func resourceLibvirtNetworkCreate(d *schema.ResourceData, meta interface{}) erro
if domain, ok := d.GetOk(forwardPrefix + ".domain"); ok {
forward.Domain = domain.(string)
}
dns.Forwarders = append(dns.Forwarders, forward)
dnsForwarders = append(dnsForwarders, forward)
}
}

dnsHostsMap := map[string][]string{}
if dnsHostCount, ok := d.GetOk(dnsPrefix + ".hosts.#"); ok {
for i := 0; i < dnsHostCount.(int); i++ {
hostPrefix := fmt.Sprintf(dnsPrefix+".hosts.%d", i)

address := d.Get(hostPrefix + ".ip").(string)
if net.ParseIP(address) == nil {
return fmt.Errorf("Could not parse address '%s'", address)
}

dnsHostsMap[address] = append(dnsHostsMap[address], d.Get(hostPrefix+".hostname").(string))
}
}

var dnsHosts []libvirtxml.NetworkDNSHost
for ip, hostnames := range dnsHostsMap {
dnsHostnames := []libvirtxml.NetworkDNSHostHostname{}
for _, hostname := range hostnames {
dnsHostnames = append(dnsHostnames, libvirtxml.NetworkDNSHostHostname{Hostname: hostname})
}
dnsHosts = append(dnsHosts, libvirtxml.NetworkDNSHost{
IP: ip,
Hostnames: dnsHostnames,
})
}

if len(dnsForwarders) > 0 || len(dnsHosts) > 0 {
dns := libvirtxml.NetworkDNS{
Forwarders: dnsForwarders,
Host: dnsHosts,
}
networkDef.DNS = &dns
}
Expand Down
86 changes: 86 additions & 0 deletions libvirt/resource_libvirt_network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,92 @@ func checkDNSForwarders(name string, expected []libvirtxml.NetworkDNSForwarder)
}
}

func TestAccLibvirtNetwork_DNSHosts(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLibvirtNetworkDestroy,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
resource "libvirt_network" "test_net" {
name = "networktest"
domain = "k8s.local"
addresses = ["10.17.3.0/24"]
dns {
hosts = [
{
hostname = "myhost1",
ip = "1.1.1.1",
},
{
hostname = "myhost1",
ip = "1.1.1.2",
},
{
hostname = "myhost2",
ip = "1.1.1.1",
},
]
}
}`),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.hosts.0.hostname", "myhost1"),
resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.hosts.0.ip", "1.1.1.1"),
resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.hosts.1.hostname", "myhost1"),
resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.hosts.1.ip", "1.1.1.2"),
resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.hosts.2.hostname", "myhost2"),
resource.TestCheckResourceAttr("libvirt_network.test_net", "dns.0.hosts.2.ip", "1.1.1.1"),
checkDNSHosts("libvirt_network.test_net", []libvirtxml.NetworkDNSHost{
{
IP: "1.1.1.1",
Hostnames: []libvirtxml.NetworkDNSHostHostname{
{Hostname: "myhost1"},
{Hostname: "myhost2"},
},
},
{
IP: "1.1.1.2",
Hostnames: []libvirtxml.NetworkDNSHostHostname{
{Hostname: "myhost1"},
},
},
}),
),
},
},
})
}

func checkDNSHosts(name string, expected []libvirtxml.NetworkDNSHost) resource.TestCheckFunc {
return func(s *terraform.State) error {
networkDef, err := getNetworkDef(s, name)
if err != nil {
return err
}
if networkDef.DNS == nil {
return fmt.Errorf("DNS block not found in networkDef")
}
actual := networkDef.DNS.Host
if len(expected) != len(actual) {
return fmt.Errorf("len(expected): %d != len(actual): %d", len(expected), len(actual))
}
for _, e := range expected {
found := false
for _, a := range actual {
if reflect.DeepEqual(a.IP, e.IP) && reflect.DeepEqual(a.Hostnames, e.Hostnames) {
found = true
break
}
}
if !found {
return fmt.Errorf("Unable to find:%v in: %v", e, actual)
}
}
return nil
}
}

func networkExists(n string, network *libvirt.Network) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
Expand Down
32 changes: 32 additions & 0 deletions website/docs/r/network.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ resource "libvirt_network" "kube_network" {
# domain = "my domain"
# }
# ]

# (Optional) one or more DNS host entries. Both of
# "ip" and "hostname" must be specified. The format is:
# hosts = [
# {
# hostname = "my_hostname"
# ip = "my.ip.address.1"
# },
# {
# hostname = "my_hostname"
# ip = "my.ip.address.2"
# },
# ]
}
}
```
Expand Down Expand Up @@ -96,7 +109,26 @@ The following arguments are supported:
Inside of `dns` section the following argument are supported:
* `local_only` - (Optional) true/false: true means 'do not forward unresolved requests for this domain to the part DNS server
* `forwarders` - (Optional) Either `address`, `domain`, or both must be set
* `hosts` - (Optional) a DNS host entry block. You can have one or more of these
blocks in your DNS definition. You must specify both `ip` and `hostname`.

An advanced example of round-robin DNS (using DNS host templates) follows:

```hcl
resource "libvirt_network" "my_network" {
...
dns = {
hosts = [ "${flatten(data.libvirt_network_dns_host_template.hosts.*.rendered)}" ]
}
...
}

data "libvirt_network_dns_host_template" "hosts" {
count = "${var.host_count}"
ip = "${var.host_ips[count.index]}"
hostname = "my_host"
}
```

* `dhcp` - (Optional) DHCP configuration.
You need to use it in conjuction with the adresses variable.
Expand Down