mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-07-03 20:12:59 +00:00
feat(provider): add optional SSH port param to node in provider ssh block (#520)
* feat: Add optional SSH port param to node in provider ssh block * fix: minor cleanups --------- Co-authored-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
4cccebaa62
commit
124cac247c
@ -208,3 +208,4 @@ Proxmox `provider` block:
|
|||||||
specified multiple times to provide configuration fo multiple nodes.
|
specified multiple times to provide configuration fo multiple nodes.
|
||||||
- `name` - (Required) The name of the node.
|
- `name` - (Required) The name of the node.
|
||||||
- `address` - (Required) The IP address of the node.
|
- `address` - (Required) The IP address of the node.
|
||||||
|
- `port` - (Optional) SSH port of the node. Defaults to 22.
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
|
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
@ -68,6 +69,7 @@ type proxmoxProviderModel struct {
|
|||||||
Nodes []struct {
|
Nodes []struct {
|
||||||
Name types.String `tfsdk:"name"`
|
Name types.String `tfsdk:"name"`
|
||||||
Address types.String `tfsdk:"address"`
|
Address types.String `tfsdk:"address"`
|
||||||
|
Port types.Int64 `tfsdk:"port"`
|
||||||
} `tfsdk:"node"`
|
} `tfsdk:"node"`
|
||||||
} `tfsdk:"ssh"`
|
} `tfsdk:"ssh"`
|
||||||
}
|
}
|
||||||
@ -167,6 +169,11 @@ func (p *proxmoxProvider) Schema(_ context.Context, _ provider.SchemaRequest, re
|
|||||||
Description: "The address of the Proxmox VE node.",
|
Description: "The address of the Proxmox VE node.",
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
|
"port": schema.Int64Attribute{
|
||||||
|
Description: "The port of the Proxmox VE node.",
|
||||||
|
Optional: true,
|
||||||
|
Validators: []validator.Int64{int64validator.Between(1, 65535)},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -291,8 +298,9 @@ func (p *proxmoxProvider) Configure(
|
|||||||
sshPassword := utils.GetAnyStringEnv("PROXMOX_VE_SSH_PASSWORD")
|
sshPassword := utils.GetAnyStringEnv("PROXMOX_VE_SSH_PASSWORD")
|
||||||
sshAgent := utils.GetAnyBoolEnv("PROXMOX_VE_SSH_AGENT")
|
sshAgent := utils.GetAnyBoolEnv("PROXMOX_VE_SSH_AGENT")
|
||||||
sshAgentSocket := utils.GetAnyStringEnv("SSH_AUTH_SOCK", "PROXMOX_VE_SSH_AUTH_SOCK")
|
sshAgentSocket := utils.GetAnyStringEnv("SSH_AUTH_SOCK", "PROXMOX_VE_SSH_AUTH_SOCK")
|
||||||
nodeOverrides := map[string]string{}
|
nodeOverrides := map[string]ssh.ProxmoxNode{}
|
||||||
|
|
||||||
|
//nolint: nestif
|
||||||
if len(config.SSH) > 0 {
|
if len(config.SSH) > 0 {
|
||||||
if !config.SSH[0].Username.IsNull() {
|
if !config.SSH[0].Username.IsNull() {
|
||||||
sshUsername = config.SSH[0].Username.ValueString()
|
sshUsername = config.SSH[0].Username.ValueString()
|
||||||
@ -311,7 +319,15 @@ func (p *proxmoxProvider) Configure(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, n := range config.SSH[0].Nodes {
|
for _, n := range config.SSH[0].Nodes {
|
||||||
nodeOverrides[n.Name.ValueString()] = n.Address.ValueString()
|
nodePort := int32(n.Port.ValueInt64())
|
||||||
|
if nodePort == 0 {
|
||||||
|
nodePort = 22
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeOverrides[n.Name.ValueString()] = ssh.ProxmoxNode{
|
||||||
|
Address: n.Address.ValueString(),
|
||||||
|
Port: nodePort,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,12 +386,12 @@ type apiResolver struct {
|
|||||||
c api.Client
|
c api.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *apiResolver) Resolve(ctx context.Context, nodeName string) (string, error) {
|
func (r *apiResolver) Resolve(ctx context.Context, nodeName string) (ssh.ProxmoxNode, error) {
|
||||||
nc := &nodes.Client{Client: r.c, NodeName: nodeName}
|
nc := &nodes.Client{Client: r.c, NodeName: nodeName}
|
||||||
|
|
||||||
networkDevices, err := nc.ListNetworkInterfaces(ctx)
|
networkDevices, err := nc.ListNetworkInterfaces(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to list network devices of node \"%s\": %w", nc.NodeName, err)
|
return ssh.ProxmoxNode{}, fmt.Errorf("failed to list network devices of node \"%s\": %w", nc.NodeName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeAddress := ""
|
nodeAddress := ""
|
||||||
@ -388,22 +404,23 @@ func (r *apiResolver) Resolve(ctx context.Context, nodeName string) (string, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
if nodeAddress == "" {
|
if nodeAddress == "" {
|
||||||
return "", fmt.Errorf("failed to determine the IP address of node \"%s\"", nc.NodeName)
|
return ssh.ProxmoxNode{}, fmt.Errorf("failed to determine the IP address of node \"%s\"", nc.NodeName)
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeAddressParts := strings.Split(nodeAddress, "/")
|
nodeAddressParts := strings.Split(nodeAddress, "/")
|
||||||
|
node := ssh.ProxmoxNode{Address: nodeAddressParts[0], Port: 22}
|
||||||
|
|
||||||
return nodeAddressParts[0], nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type apiResolverWithOverrides struct {
|
type apiResolverWithOverrides struct {
|
||||||
ar apiResolver
|
ar apiResolver
|
||||||
overrides map[string]string
|
overrides map[string]ssh.ProxmoxNode
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *apiResolverWithOverrides) Resolve(ctx context.Context, nodeName string) (string, error) {
|
func (r *apiResolverWithOverrides) Resolve(ctx context.Context, nodeName string) (ssh.ProxmoxNode, error) {
|
||||||
if ip, ok := r.overrides[nodeName]; ok {
|
if node, ok := r.overrides[nodeName]; ok {
|
||||||
return ip, nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.ar.Resolve(ctx, nodeName)
|
return r.ar.Resolve(ctx, nodeName)
|
||||||
|
@ -74,19 +74,20 @@ func NewClient(
|
|||||||
|
|
||||||
// ExecuteNodeCommands executes commands on a given node.
|
// ExecuteNodeCommands executes commands on a given node.
|
||||||
func (c *client) ExecuteNodeCommands(ctx context.Context, nodeName string, commands []string) error {
|
func (c *client) ExecuteNodeCommands(ctx context.Context, nodeName string, commands []string) error {
|
||||||
ip, err := c.nodeLookup.Resolve(ctx, nodeName)
|
node, err := c.nodeLookup.Resolve(ctx, nodeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to find node endpoint: %w", err)
|
return fmt.Errorf("failed to find node endpoint: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tflog.Debug(ctx, "executing commands on the node using SSH", map[string]interface{}{
|
tflog.Debug(ctx, "executing commands on the node using SSH", map[string]interface{}{
|
||||||
"node_address": ip,
|
"node_address": node.Address,
|
||||||
|
"node_port": node.Port,
|
||||||
"commands": commands,
|
"commands": commands,
|
||||||
})
|
})
|
||||||
|
|
||||||
closeOrLogError := utils.CloseOrLogError(ctx)
|
closeOrLogError := utils.CloseOrLogError(ctx)
|
||||||
|
|
||||||
sshClient, err := c.openNodeShell(ctx, ip)
|
sshClient, err := c.openNodeShell(ctx, node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -212,13 +213,13 @@ func (c *client) NodeUpload(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// openNodeShell establishes a new SSH connection to a node.
|
// openNodeShell establishes a new SSH connection to a node.
|
||||||
func (c *client) openNodeShell(ctx context.Context, nodeAddress string) (*ssh.Client, error) {
|
func (c *client) openNodeShell(ctx context.Context, node ProxmoxNode) (*ssh.Client, error) {
|
||||||
homeDir, err := os.UserHomeDir()
|
homeDir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to determine the home directory: %w", err)
|
return nil, fmt.Errorf("failed to determine the home directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sshHost := fmt.Sprintf("%s:22", nodeAddress)
|
sshHost := fmt.Sprintf("%s:%d", node.Address, node.Port)
|
||||||
|
|
||||||
sshPath := path.Join(homeDir, ".ssh")
|
sshPath := path.Join(homeDir, ".ssh")
|
||||||
if _, err = os.Stat(sshPath); os.IsNotExist(err) {
|
if _, err = os.Stat(sshPath); os.IsNotExist(err) {
|
||||||
|
@ -10,7 +10,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ProxmoxNode represents node address and port for SSH connection.
|
||||||
|
type ProxmoxNode struct {
|
||||||
|
Address string
|
||||||
|
Port int32
|
||||||
|
}
|
||||||
|
|
||||||
// NodeResolver is an interface for resolving node names to IP addresses to use for SSH connection.
|
// NodeResolver is an interface for resolving node names to IP addresses to use for SSH connection.
|
||||||
type NodeResolver interface {
|
type NodeResolver interface {
|
||||||
Resolve(ctx context.Context, nodeName string) (string, error)
|
Resolve(ctx context.Context, nodeName string) (ProxmoxNode, error)
|
||||||
}
|
}
|
||||||
|
@ -129,12 +129,15 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{},
|
|||||||
sshConf[mkProviderSSHAgentSocket] = sshAgentSocket
|
sshConf[mkProviderSSHAgentSocket] = sshAgentSocket
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeOverrides := map[string]string{}
|
nodeOverrides := map[string]ssh.ProxmoxNode{}
|
||||||
|
|
||||||
if ns, ok := sshConf[mkProviderSSHNode]; ok {
|
if ns, ok := sshConf[mkProviderSSHNode]; ok {
|
||||||
for _, n := range ns.([]interface{}) {
|
for _, n := range ns.([]interface{}) {
|
||||||
node := n.(map[string]interface{})
|
node := n.(map[string]interface{})
|
||||||
nodeOverrides[node[mkProviderSSHNodeName].(string)] = node[mkProviderSSHNodeAddress].(string)
|
nodeOverrides[node[mkProviderSSHNodeName].(string)] = ssh.ProxmoxNode{
|
||||||
|
Address: node[mkProviderSSHNodeAddress].(string),
|
||||||
|
Port: node[mkProviderSSHNodePort].(int32),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,12 +164,12 @@ type apiResolver struct {
|
|||||||
c api.Client
|
c api.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *apiResolver) Resolve(ctx context.Context, nodeName string) (string, error) {
|
func (r *apiResolver) Resolve(ctx context.Context, nodeName string) (ssh.ProxmoxNode, error) {
|
||||||
nc := &nodes.Client{Client: r.c, NodeName: nodeName}
|
nc := &nodes.Client{Client: r.c, NodeName: nodeName}
|
||||||
|
|
||||||
networkDevices, err := nc.ListNetworkInterfaces(ctx)
|
networkDevices, err := nc.ListNetworkInterfaces(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to list network devices of node \"%s\": %w", nc.NodeName, err)
|
return ssh.ProxmoxNode{}, fmt.Errorf("failed to list network devices of node \"%s\": %w", nc.NodeName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeAddress := ""
|
nodeAddress := ""
|
||||||
@ -179,22 +182,23 @@ func (r *apiResolver) Resolve(ctx context.Context, nodeName string) (string, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
if nodeAddress == "" {
|
if nodeAddress == "" {
|
||||||
return "", fmt.Errorf("failed to determine the IP address of node \"%s\"", nc.NodeName)
|
return ssh.ProxmoxNode{}, fmt.Errorf("failed to determine the IP address of node \"%s\"", nc.NodeName)
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeAddressParts := strings.Split(nodeAddress, "/")
|
nodeAddressParts := strings.Split(nodeAddress, "/")
|
||||||
|
node := ssh.ProxmoxNode{Address: nodeAddressParts[0], Port: 22}
|
||||||
|
|
||||||
return nodeAddressParts[0], nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type apiResolverWithOverrides struct {
|
type apiResolverWithOverrides struct {
|
||||||
ar apiResolver
|
ar apiResolver
|
||||||
overrides map[string]string
|
overrides map[string]ssh.ProxmoxNode
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *apiResolverWithOverrides) Resolve(ctx context.Context, nodeName string) (string, error) {
|
func (r *apiResolverWithOverrides) Resolve(ctx context.Context, nodeName string) (ssh.ProxmoxNode, error) {
|
||||||
if ip, ok := r.overrides[nodeName]; ok {
|
if node, ok := r.overrides[nodeName]; ok {
|
||||||
return ip, nil
|
return node, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.ar.Resolve(ctx, nodeName)
|
return r.ar.Resolve(ctx, nodeName)
|
||||||
|
@ -31,6 +31,7 @@ const (
|
|||||||
mkProviderSSHNode = "node"
|
mkProviderSSHNode = "node"
|
||||||
mkProviderSSHNodeName = "name"
|
mkProviderSSHNodeName = "name"
|
||||||
mkProviderSSHNodeAddress = "address"
|
mkProviderSSHNodeAddress = "address"
|
||||||
|
mkProviderSSHNodePort = "port"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createSchema() map[string]*schema.Schema {
|
func createSchema() map[string]*schema.Schema {
|
||||||
@ -155,6 +156,13 @@ func createSchema() map[string]*schema.Schema {
|
|||||||
Description: "The address of the Proxmox VE node.",
|
Description: "The address of the Proxmox VE node.",
|
||||||
ValidateFunc: validation.IsIPAddress,
|
ValidateFunc: validation.IsIPAddress,
|
||||||
},
|
},
|
||||||
|
mkProviderSSHNodePort: {
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Description: "The port of the Proxmox VE node.",
|
||||||
|
Default: 22,
|
||||||
|
ValidateFunc: validation.IsPortNumber,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user