dmacvicar / terraform-provider-libvirt

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

version 0.7.4 breaks qemu+ssh (libvirt-sock) connection #1040

Closed n-able-consulting closed 10 months ago

n-able-consulting commented 11 months ago

System Information

amd64

Linux distribution

Ubuntu22.04

Terraform version

v1.6.1

Provider and libvirt versions

0.7.4


Description of Issue/Question

I use the plugin to provision vm's on rack within my network. I always implement an install with the latest Terraform and plugin versions. With version 0.7.4. I get the following error when executing my code: Error: failed to connect: dial unix /var/run/libvirt/libvirt-sock: connect: permission denied │ │ with provider["registry.terraform.io/dmacvicar/libvirt"], │ on connection.tf line 14, in provider "libvirt": │ 14: provider "libvirt" {

Falling back to version 0.7.1 (since it is the latest version that is useable): it works again.

Setup

my connection.tf not working

terraform {
 required_version = ">= 1.6.1"
  required_providers {
    libvirt = {
      version  = "0.7.4"
      source  = "dmacvicar/libvirt"
    }
  }
}

# instance the provider
provider "libvirt" {
  uri = "qemu+ssh://user@xxx.xxx.hosts/system?keyfile=/home/xxx/.ssh/xxx&sshauth=privkey"
}

my connection.tf working

terraform {
 required_version = ">= 1.6.1"
  required_providers {
    libvirt = {
      version  = "0.7.1"
      source  = "dmacvicar/libvirt"
    }
  }
}

# instance the provider
provider "libvirt" {
  uri = "qemu+ssh://user@xxx.xxx.hosts/system?keyfile=/home/xxx/.ssh/xxx&sshauth=privkey"
}
niklas-truesec commented 11 months ago

This also seems to break usage of user session URIs

provider "libvirt" { uri = "qemu:///session?socket=/run/user/1000/libvirt/virtqemud-sock" }

nerzhul commented 11 months ago

same issue with bhyve+ssh://

X-Cli commented 11 months ago

The issue is that the connectionURI is modified by the call to RemoteName() done during the call to Dial by go-libvirt. As it seems, the copy of the URI is shallow and thus, calling Dial during a ConnectToURI on the RemoteName() causes the Dial call to contact the RemoteName instead of the configured URI.

I managed to fix the bug by replacing the copy with the following initialization:

    newURI := url.URL{
        Scheme: u.driver(),
        Path:   u.Path,
    }

The issue was not present before because the dial was done prior to passing the connection to go-libvirt and prior to the call of the RemoteName() function.

michaelbeaumont commented 11 months ago

@X-Cli Good catch. It feels to me like the real fix is not having a pointer to a URL inside ConnectionURI, which is what makes the shallow copy useless.

type ConnectionURI struct {
    url.URL
}

Can you try that out/open a PR?

elasmo commented 11 months ago

Could be related to what @X-Cli is mentioning. I'm having issues connecting using qemu+tls.

main.tf:

terraform {
  required_providers {
    libvirt = {
      source  = "dmacvicar/libvirt"
      version = "0.7.4"
    }
  }
}

provider "libvirt" {
  uri = "qemu+tls://libvirt.example.tld/system"
}

resource "libvirt_domain" "test" {
  name = "Test"
}
$ TF_LOG=DEBUG terraform plan
2023-11-03T12:37:30.115+0100 [INFO]  Terraform version: 1.6.2
2023-11-03T12:37:30.115+0100 [DEBUG] using github.com/hashicorp/go-tfe v1.36.0
2023-11-03T12:37:30.115+0100 [DEBUG] using github.com/hashicorp/hcl/v2 v2.19.1
2023-11-03T12:37:30.115+0100 [DEBUG] using github.com/hashicorp/terraform-svchost v0.1.1
2023-11-03T12:37:30.115+0100 [DEBUG] using github.com/zclconf/go-cty v1.14.1
2023-11-03T12:37:30.115+0100 [INFO]  Go runtime version: go1.21.1
2023-11-03T12:37:30.115+0100 [INFO]  CLI args: []string{"terraform", "plan"}
2023-11-03T12:37:30.115+0100 [DEBUG] Attempting to open CLI config file: /home/user/.terraformrc
2023-11-03T12:37:30.115+0100 [DEBUG] File doesn't exist, but doesn't need to. Ignoring.
2023-11-03T12:37:30.116+0100 [DEBUG] ignoring non-existing provider search directory terraform.d/plugins
2023-11-03T12:37:30.116+0100 [DEBUG] ignoring non-existing provider search directory /home/user/.terraform.d/plugins
2023-11-03T12:37:30.116+0100 [DEBUG] ignoring non-existing provider search directory /home/user/.local/share/terraform/plugins
2023-11-03T12:37:30.116+0100 [DEBUG] ignoring non-existing provider search directory /usr/local/share/terraform/plugins
2023-11-03T12:37:30.116+0100 [DEBUG] ignoring non-existing provider search directory /usr/share/terraform/plugins
2023-11-03T12:37:30.116+0100 [INFO]  CLI command args: []string{"plan"}
2023-11-03T12:37:30.117+0100 [DEBUG] New state was assigned lineage "180c2c10-411f-61f4-2b91-0112ae838318"
2023-11-03T12:37:30.128+0100 [DEBUG] checking for provisioner in "."
2023-11-03T12:37:30.129+0100 [DEBUG] checking for provisioner in "/usr/bin"
2023-11-03T12:37:30.130+0100 [INFO]  backend/local: starting Plan operation
2023-11-03T12:37:30.131+0100 [DEBUG] created provider logger: level=debug
2023-11-03T12:37:30.131+0100 [INFO]  provider: configuring client automatic mTLS
2023-11-03T12:37:30.136+0100 [DEBUG] provider: starting plugin: path=.terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4 args=[".terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4"]
2023-11-03T12:37:30.136+0100 [DEBUG] provider: plugin started: path=.terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4 pid=18656
2023-11-03T12:37:30.136+0100 [DEBUG] provider: waiting for RPC address: path=.terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4
2023-11-03T12:37:30.142+0100 [INFO]  provider.terraform-provider-libvirt_v0.7.4: configuring server automatic mTLS: timestamp="2023-11-03T12:37:30.142+0100"
2023-11-03T12:37:30.155+0100 [DEBUG] provider: using plugin: version=5
2023-11-03T12:37:30.155+0100 [DEBUG] provider.terraform-provider-libvirt_v0.7.4: plugin address: address=/tmp/plugin2606746144 network=unix timestamp="2023-11-03T12:37:30.155+0100"
2023-11-03T12:37:30.175+0100 [DEBUG] provider.stdio: received EOF, stopping recv loop: err="rpc error: code = Unavailable desc = error reading from server: EOF"
2023-11-03T12:37:30.179+0100 [DEBUG] provider: plugin process exited: path=.terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4 pid=18656
2023-11-03T12:37:30.179+0100 [DEBUG] provider: plugin exited
2023-11-03T12:37:30.179+0100 [DEBUG] Building and walking validate graph
2023-11-03T12:37:30.180+0100 [DEBUG] ProviderTransformer: "libvirt_domain.test" (*terraform.NodeValidatableResource) needs provider["registry.terraform.io/dmacvicar/libvirt"]
2023-11-03T12:37:30.180+0100 [DEBUG] ReferenceTransformer: "libvirt_domain.test" references: []
2023-11-03T12:37:30.180+0100 [DEBUG] ReferenceTransformer: "provider[\"registry.terraform.io/dmacvicar/libvirt\"]" references: []
2023-11-03T12:37:30.181+0100 [DEBUG] Starting graph walk: walkValidate
2023-11-03T12:37:30.181+0100 [DEBUG] created provider logger: level=debug
2023-11-03T12:37:30.181+0100 [INFO]  provider: configuring client automatic mTLS
2023-11-03T12:37:30.185+0100 [DEBUG] provider: starting plugin: path=.terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4 args=[".terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4"]
2023-11-03T12:37:30.186+0100 [DEBUG] provider: plugin started: path=.terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4 pid=18670
2023-11-03T12:37:30.186+0100 [DEBUG] provider: waiting for RPC address: path=.terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4
2023-11-03T12:37:30.191+0100 [INFO]  provider.terraform-provider-libvirt_v0.7.4: configuring server automatic mTLS: timestamp="2023-11-03T12:37:30.191+0100"
2023-11-03T12:37:30.201+0100 [DEBUG] provider: using plugin: version=5
2023-11-03T12:37:30.201+0100 [DEBUG] provider.terraform-provider-libvirt_v0.7.4: plugin address: address=/tmp/plugin181566249 network=unix timestamp="2023-11-03T12:37:30.200+0100"
2023-11-03T12:37:30.217+0100 [DEBUG] provider.stdio: received EOF, stopping recv loop: err="rpc error: code = Unavailable desc = error reading from server: EOF"
2023-11-03T12:37:30.217+0100 [DEBUG] provider: plugin process exited: path=.terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4 pid=18670
2023-11-03T12:37:30.217+0100 [DEBUG] provider: plugin exited
2023-11-03T12:37:30.218+0100 [INFO]  backend/local: plan calling Plan
2023-11-03T12:37:30.218+0100 [DEBUG] Building and walking plan graph for NormalMode
2023-11-03T12:37:30.218+0100 [DEBUG] ProviderTransformer: "libvirt_domain.test (expand)" (*terraform.nodeExpandPlannableResource) needs provider["registry.terraform.io/dmacvicar/libvirt"]
2023-11-03T12:37:30.218+0100 [DEBUG] ReferenceTransformer: "libvirt_domain.test (expand)" references: []
2023-11-03T12:37:30.218+0100 [DEBUG] ReferenceTransformer: "provider[\"registry.terraform.io/dmacvicar/libvirt\"]" references: []
2023-11-03T12:37:30.218+0100 [DEBUG] Starting graph walk: walkPlan
2023-11-03T12:37:30.218+0100 [DEBUG] created provider logger: level=debug
2023-11-03T12:37:30.218+0100 [INFO]  provider: configuring client automatic mTLS
2023-11-03T12:37:30.221+0100 [DEBUG] provider: starting plugin: path=.terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4 args=[".terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4"]
2023-11-03T12:37:30.222+0100 [DEBUG] provider: plugin started: path=.terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4 pid=18684
2023-11-03T12:37:30.222+0100 [DEBUG] provider: waiting for RPC address: path=.terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4
2023-11-03T12:37:30.226+0100 [INFO]  provider.terraform-provider-libvirt_v0.7.4: configuring server automatic mTLS: timestamp="2023-11-03T12:37:30.226+0100"
2023-11-03T12:37:30.234+0100 [DEBUG] provider: using plugin: version=5
2023-11-03T12:37:30.234+0100 [DEBUG] provider.terraform-provider-libvirt_v0.7.4: plugin address: address=/tmp/plugin1548367601 network=unix timestamp="2023-11-03T12:37:30.234+0100"
2023-11-03T12:37:30.253+0100 [INFO]  provider.terraform-provider-libvirt_v0.7.4: 2023/11/03 12:37:30 [DEBUG] Configuring provider for 'qemu+tls://libvirt.example.tld/system': &{map[uri:0xc0003ea500] <nil> <nil> 0xc00034c680 map[] <nil> {{<nil>} <nil>} 0xc000120160 0xc000132450 0xc000291ad0 false {1 {0 0}} false false}: timestamp="2023-11-03T12:37:30.252+0100"
2023-11-03T12:37:30.253+0100 [ERROR] provider.terraform-provider-libvirt_v0.7.4: Response contains error diagnostic: @caller=github.com/hashicorp/terraform-plugin-go@v0.14.2/tfprotov5/internal/diag/diagnostics.go:55 diagnostic_detail="" diagnostic_severity=ERROR tf_proto_version=5.3 tf_req_id=c3645f64-4cbb-70af-f2d3-8256215c7cc2 @module=sdk.proto diagnostic_summary="failed to connect: dial unix /var/run/libvirt/libvirt-sock: connect: no such file or directory" tf_provider_addr=provider tf_rpc=Configure timestamp="2023-11-03T12:37:30.253+0100"
2023-11-03T12:37:30.253+0100 [ERROR] vertex "provider[\"registry.terraform.io/dmacvicar/libvirt\"]" error: failed to connect: dial unix /var/run/libvirt/libvirt-sock: connect: no such file or directory
2023-11-03T12:37:30.253+0100 [INFO]  backend/local: plan operation completed

Planning failed. Terraform encountered an error while generating this plan.

╷
│ Error: failed to connect: dial unix /var/run/libvirt/libvirt-sock: connect: no such file or directory
│ 
│   with provider["registry.terraform.io/dmacvicar/libvirt"],
│   on main.tf line 10, in provider "libvirt":
│   10: provider "libvirt" {
│ 
╵
2023-11-03T12:37:30.254+0100 [DEBUG] provider.stdio: received EOF, stopping recv loop: err="rpc error: code = Unavailable desc = error reading from server: EOF"
2023-11-03T12:37:30.255+0100 [DEBUG] provider: plugin process exited: path=.terraform/providers/registry.terraform.io/dmacvicar/libvirt/0.7.4/linux_amd64/terraform-provider-libvirt_v0.7.4 pid=18684
2023-11-03T12:37:30.255+0100 [DEBUG] provider: plugin exited
X-Cli commented 11 months ago

@X-Cli Good catch. It feels to me like the real fix is not having a pointer to a URL inside ConnectionURI, which is what makes the shallow copy useless.

type ConnectionURI struct {
  url.URL
}

As far as I know Go, this syntax is not exactly a pointer; it is a syntactic sugar for ConnectionURI to have the same attributes, current and future, as a url.URL. The type is only used as a proxy for url.URL augmented with the Dialer implementation, and a few private functions. However, you are correct that if RemoteName receiver was not a pointer, the ConnectionURI instance would not have been modified either.

I have modified my code to do both, just in case :)

michaelbeaumont commented 11 months ago

As far as I know Go, this syntax is not exactly a pointer;

In fact that's what *url.URL ensures. There's implicitly a URL: *url.URL field, so that on a shallow copy of ConnectionURI, the pointer itself is copied, not the value pointed to by the pointer. And so the subsequent statements like newURI.User = nil modify the original value u.URL.User via the newURI.URL *url.URL via the pointer receiver u *ConnectionURI when we call u.RemoteName().

The change from:

type ConnectionURI struct {
    *url.URL
}

to

type ConnectionURI struct {
    url.URL
}

would make it so that ConnectionURI wraps a value, not a pointer. Preventing unintentional mutation of the URL contained in ConnectionURI. It seems to me very unlikely that a shallow copy of a ConnectionURI is meant to preserve mutability of the contained URL, though I can't be sure without a deeper look at the code.

So the pointer receiver change is actually unnecessary, since the shallow copy of ConnectionURI still points to the same URL.