dmacvicar / terraform-provider-libvirt

Terraform provider to provision infrastructure with Linux's KVM using libvirt
Apache License 2.0
1.54k stars 457 forks source link

New Feature: allow specifying of target host machine on every individual resource/data source #1071

Open memetb opened 4 months ago

memetb commented 4 months ago

System Information

Linux distribution

any

Terraform version

terraform -v

Terraform v1.5.7
on linux_amd64

Provider and libvirt versions

git describe --always --abbrev=40 --dirty

c8facd868b69360f4542f8907eb1cf4c7b2fffa8-dirty

Description of Issue/Question

The current recommendation for connecting to multiple hosts is to declare multiple providers and assign them aliases. Indeed, this is how hashicorp themselves recommend using multiple providers with AWS.

However, AWS is a service, where as libvirt is connecting directly to machines and therefore the use case is not exactly the same. Let's take for instance an enterprise which collocates multiple racks in multiple geographic locations (TOR,NYC,SFC). Each rack has an id (rackN), and in each rack there are 5 general purpose compute machines (generalN), 3 GPU enabled machines (gpuN), and 3 storage servers (storageN) with dedicated connectivity between. This gives each server a fqdn slug like tor-rack1-general2.evilcorp.com.

Now let's say an IaC engineer wants to create a module for an application that will allocate allocate a CPU frontend and a GPU backend, and allocate a storage device on the storage server.

The desired use-case for such a module would be something like so:

terraform {
  required_version = ">= 0.13"
  required_providers {
    libvirt = {
      source = "dmacvicar/libvirt"
      version = "1.0.0"
    }
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "~> 4.0"
    }
    digitalocean = {
      source  = "digitalocean/digitalocean"
      version = "~> 2.0"
    }
  }
}

provider "digitalocean" {
  token = var.do_token
}

provider "cloudflare" {
  api_token = var.cf_token
}

provider "libvirt" {
  uri = "qemu+ssh://${var.target}/system?sshauth=privkey&no_verify=true"
}

module "dev" {
  source = "./foobar"

  region   = "TOR"
  hostname = "development"
  //...
}

module "uat" {
  source = "./foobar"

  region   = "TOR"
  hostname = "user-acceptance"
  //...
}

module "production" {
  source = "./foobar"

  region   = "TOR"
  hostname = "production"
  //...
}

Under the hood, the module foobar/main.tf would connect to an unknown number of host machines to accomplish this goal, something like:

resource "libvirt_volume" "local-data" {
  host = "${var.region}-rack1-storage.evilcorp.com"
  // ...
}

resource "libvirt_domain" "frontend" {
  host = "${var.region}-rack1-general2.evilcorp.com"
  // ...
}

resource "libvirt_domain" "compute" {
  host = "${var.region}-rack1-gpu2.evilcorp.com"
  // ...
}

Note how doing this with duplicated providers forces an abstraction leak: the IaC engineer working on the backend stuff now has to inform the application programmers that they need 3 providers to be able to call this module. Any changes to metal implementations cause application programmers to need to change their code, or IaC engineers to muck with code that isn't theirs.

I have implemented a full solution to this feature request here: https://github.com/memetb/terraform-provider-libvirt/compare/main...memetb:terraform-provider-libvirt:multi-connection?expand=1 Associated PR #1072

I have not submitted it as a PR because I used a currently outstanding PR as my branch base (I can rebase if there's interest for this to be pushed through).

Implementation Notes

A couple of improvements have been made which are of note: