Related to #727: restore default Proxmox ports
This commit is contained in:
parent
823508dc48
commit
044dadcdf1
4 changed files with 153 additions and 37 deletions
|
|
@ -1097,16 +1097,15 @@ func defaultPortForNodeType(nodeType string) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalizeNodeHost ensures hosts always include a scheme and only adds a default port
|
// normalizeNodeHost ensures hosts always include a scheme and default port when one
|
||||||
// when the user did not supply a scheme (e.g., bare hostname/IP inputs). If the user
|
// isn't provided. Defaults align with Proxmox APIs (PVE/PMG: 8006, PBS: 8007) while
|
||||||
// explicitly provides http/https, we respect their choice and avoid forcing a port.
|
// preserving any explicit scheme/port the user supplies.
|
||||||
func normalizeNodeHost(rawHost, nodeType string) (string, error) {
|
func normalizeNodeHost(rawHost, nodeType string) (string, error) {
|
||||||
host := strings.TrimSpace(rawHost)
|
host := strings.TrimSpace(rawHost)
|
||||||
if host == "" {
|
if host == "" {
|
||||||
return "", fmt.Errorf("host is required")
|
return "", fmt.Errorf("host is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
userProvidedScheme := strings.HasPrefix(host, "http://") || strings.HasPrefix(host, "https://")
|
|
||||||
scheme := "https"
|
scheme := "https"
|
||||||
if strings.HasPrefix(host, "http://") {
|
if strings.HasPrefix(host, "http://") {
|
||||||
scheme = "http"
|
scheme = "http"
|
||||||
|
|
@ -1137,7 +1136,7 @@ func normalizeNodeHost(rawHost, nodeType string) (string, error) {
|
||||||
parsed.RawQuery = ""
|
parsed.RawQuery = ""
|
||||||
parsed.Fragment = ""
|
parsed.Fragment = ""
|
||||||
|
|
||||||
if parsed.Port() == "" && !userProvidedScheme {
|
if parsed.Port() == "" {
|
||||||
defaultPort := defaultPortForNodeType(nodeType)
|
defaultPort := defaultPortForNodeType(nodeType)
|
||||||
if defaultPort != "" {
|
if defaultPort != "" {
|
||||||
parsed.Host = net.JoinHostPort(parsed.Hostname(), defaultPort)
|
parsed.Host = net.JoinHostPort(parsed.Hostname(), defaultPort)
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,19 @@ func TestNormalizeNodeHost(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
rawHost string
|
rawHost string
|
||||||
nodeType string
|
nodeType string
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "keeps explicit https without port",
|
name: "adds default port to explicit https without port",
|
||||||
rawHost: "https://example.com",
|
rawHost: "https://example.com",
|
||||||
nodeType: "pve",
|
nodeType: "pve",
|
||||||
want: "https://example.com",
|
want: "https://example.com:8006",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "adds default port for bare pve host",
|
name: "adds default port for bare pve host",
|
||||||
rawHost: "pve.lan",
|
rawHost: "pve.lan",
|
||||||
nodeType: "pve",
|
nodeType: "pve",
|
||||||
want: "https://pve.lan:8006",
|
want: "https://pve.lan:8006",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -39,19 +39,19 @@ func TestNormalizeNodeHost(t *testing.T) {
|
||||||
nodeType: "pmg",
|
nodeType: "pmg",
|
||||||
want: "https://[2001:db8::1]:8006",
|
want: "https://[2001:db8::1]:8006",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "drops path segments",
|
name: "drops path segments",
|
||||||
rawHost: "https://example.com/api",
|
rawHost: "https://example.com/api",
|
||||||
nodeType: "pve",
|
nodeType: "pve",
|
||||||
want: "https://example.com",
|
want: "https://example.com:8006",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "respects explicit http without forcing a port",
|
name: "adds default port to explicit http scheme",
|
||||||
rawHost: "http://example.com",
|
rawHost: "http://example.com",
|
||||||
nodeType: "pve",
|
nodeType: "pve",
|
||||||
want: "http://example.com",
|
want: "http://example.com:8006",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/rcourtman/pulse-go-rewrite/pkg/pbs"
|
"github.com/rcourtman/pulse-go-rewrite/pkg/pbs"
|
||||||
|
|
@ -8,6 +10,42 @@ import (
|
||||||
"github.com/rcourtman/pulse-go-rewrite/pkg/proxmox"
|
"github.com/rcourtman/pulse-go-rewrite/pkg/proxmox"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultPVEPort = "8006"
|
||||||
|
defaultPBSPort = "8007"
|
||||||
|
)
|
||||||
|
|
||||||
|
// normalizeHostPort ensures we always have a scheme and explicit port when talking to
|
||||||
|
// Proxmox APIs. It preserves existing ports/schemes and strips any path/query segments.
|
||||||
|
func normalizeHostPort(host, defaultPort string) string {
|
||||||
|
trimmed := strings.TrimSpace(host)
|
||||||
|
if trimmed == "" || defaultPort == "" {
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate := trimmed
|
||||||
|
if !strings.HasPrefix(candidate, "http://") && !strings.HasPrefix(candidate, "https://") {
|
||||||
|
candidate = "https://" + candidate
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed, err := url.Parse(candidate)
|
||||||
|
if err != nil || parsed.Host == "" {
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop any path fragments so we only persist host:port
|
||||||
|
parsed.Path = ""
|
||||||
|
parsed.RawPath = ""
|
||||||
|
parsed.RawQuery = ""
|
||||||
|
parsed.Fragment = ""
|
||||||
|
|
||||||
|
if parsed.Port() == "" {
|
||||||
|
parsed.Host = net.JoinHostPort(parsed.Hostname(), defaultPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed.Scheme + "://" + parsed.Host
|
||||||
|
}
|
||||||
|
|
||||||
// CreateProxmoxConfig creates a proxmox.ClientConfig from a PVEInstance
|
// CreateProxmoxConfig creates a proxmox.ClientConfig from a PVEInstance
|
||||||
func CreateProxmoxConfig(node *PVEInstance) proxmox.ClientConfig {
|
func CreateProxmoxConfig(node *PVEInstance) proxmox.ClientConfig {
|
||||||
user := node.User
|
user := node.User
|
||||||
|
|
@ -16,7 +54,7 @@ func CreateProxmoxConfig(node *PVEInstance) proxmox.ClientConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
return proxmox.ClientConfig{
|
return proxmox.ClientConfig{
|
||||||
Host: node.Host,
|
Host: normalizeHostPort(node.Host, defaultPVEPort),
|
||||||
User: user,
|
User: user,
|
||||||
Password: node.Password,
|
Password: node.Password,
|
||||||
TokenName: node.TokenName,
|
TokenName: node.TokenName,
|
||||||
|
|
@ -29,7 +67,7 @@ func CreateProxmoxConfig(node *PVEInstance) proxmox.ClientConfig {
|
||||||
// CreatePBSConfig creates a pbs.ClientConfig from a PBSInstance
|
// CreatePBSConfig creates a pbs.ClientConfig from a PBSInstance
|
||||||
func CreatePBSConfig(node *PBSInstance) pbs.ClientConfig {
|
func CreatePBSConfig(node *PBSInstance) pbs.ClientConfig {
|
||||||
return pbs.ClientConfig{
|
return pbs.ClientConfig{
|
||||||
Host: node.Host,
|
Host: normalizeHostPort(node.Host, defaultPBSPort),
|
||||||
User: node.User,
|
User: node.User,
|
||||||
Password: node.Password,
|
Password: node.Password,
|
||||||
TokenName: node.TokenName,
|
TokenName: node.TokenName,
|
||||||
|
|
@ -42,7 +80,7 @@ func CreatePBSConfig(node *PBSInstance) pbs.ClientConfig {
|
||||||
// CreatePMGConfig creates a pmg.ClientConfig from a PMGInstance
|
// CreatePMGConfig creates a pmg.ClientConfig from a PMGInstance
|
||||||
func CreatePMGConfig(node *PMGInstance) pmg.ClientConfig {
|
func CreatePMGConfig(node *PMGInstance) pmg.ClientConfig {
|
||||||
return pmg.ClientConfig{
|
return pmg.ClientConfig{
|
||||||
Host: node.Host,
|
Host: normalizeHostPort(node.Host, defaultPVEPort),
|
||||||
User: node.User,
|
User: node.User,
|
||||||
Password: node.Password,
|
Password: node.Password,
|
||||||
TokenName: node.TokenName,
|
TokenName: node.TokenName,
|
||||||
|
|
@ -59,7 +97,7 @@ func CreateProxmoxConfigFromFields(host, user, password, tokenName, tokenValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
return proxmox.ClientConfig{
|
return proxmox.ClientConfig{
|
||||||
Host: host,
|
Host: normalizeHostPort(host, defaultPVEPort),
|
||||||
User: user,
|
User: user,
|
||||||
Password: password,
|
Password: password,
|
||||||
TokenName: tokenName,
|
TokenName: tokenName,
|
||||||
|
|
@ -72,7 +110,7 @@ func CreateProxmoxConfigFromFields(host, user, password, tokenName, tokenValue,
|
||||||
// CreatePBSConfigFromFields creates a pbs.ClientConfig from individual fields
|
// CreatePBSConfigFromFields creates a pbs.ClientConfig from individual fields
|
||||||
func CreatePBSConfigFromFields(host, user, password, tokenName, tokenValue, fingerprint string, verifySSL bool) pbs.ClientConfig {
|
func CreatePBSConfigFromFields(host, user, password, tokenName, tokenValue, fingerprint string, verifySSL bool) pbs.ClientConfig {
|
||||||
return pbs.ClientConfig{
|
return pbs.ClientConfig{
|
||||||
Host: host,
|
Host: normalizeHostPort(host, defaultPBSPort),
|
||||||
User: user,
|
User: user,
|
||||||
Password: password,
|
Password: password,
|
||||||
TokenName: tokenName,
|
TokenName: tokenName,
|
||||||
|
|
@ -85,7 +123,7 @@ func CreatePBSConfigFromFields(host, user, password, tokenName, tokenValue, fing
|
||||||
// CreatePMGConfigFromFields creates a pmg.ClientConfig from individual fields
|
// CreatePMGConfigFromFields creates a pmg.ClientConfig from individual fields
|
||||||
func CreatePMGConfigFromFields(host, user, password, tokenName, tokenValue, fingerprint string, verifySSL bool) pmg.ClientConfig {
|
func CreatePMGConfigFromFields(host, user, password, tokenName, tokenValue, fingerprint string, verifySSL bool) pmg.ClientConfig {
|
||||||
return pmg.ClientConfig{
|
return pmg.ClientConfig{
|
||||||
Host: host,
|
Host: normalizeHostPort(host, defaultPVEPort),
|
||||||
User: user,
|
User: user,
|
||||||
Password: password,
|
Password: password,
|
||||||
TokenName: tokenName,
|
TokenName: tokenName,
|
||||||
|
|
|
||||||
79
internal/config/client_helpers_test.go
Normal file
79
internal/config/client_helpers_test.go
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNormalizeHostPort(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
host string
|
||||||
|
defaultPort string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "adds default port to https host",
|
||||||
|
host: "https://example.com",
|
||||||
|
defaultPort: defaultPVEPort,
|
||||||
|
want: "https://example.com:8006",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "adds default port when scheme missing",
|
||||||
|
host: "pbs.lan",
|
||||||
|
defaultPort: defaultPBSPort,
|
||||||
|
want: "https://pbs.lan:8007",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "preserves existing port and scheme",
|
||||||
|
host: "http://example.com:8443",
|
||||||
|
defaultPort: defaultPVEPort,
|
||||||
|
want: "http://example.com:8443",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "drops path segments before applying port",
|
||||||
|
host: "https://example.com/api",
|
||||||
|
defaultPort: defaultPVEPort,
|
||||||
|
want: "https://example.com:8006",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := normalizeHostPort(tt.host, tt.defaultPort); got != tt.want {
|
||||||
|
t.Fatalf("normalizeHostPort(%q, %q) = %q, want %q", tt.host, tt.defaultPort, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateConfigHelpersNormalizeHosts(t *testing.T) {
|
||||||
|
pveCfg := PVEInstance{
|
||||||
|
Host: "https://pve.local",
|
||||||
|
User: "root@pam",
|
||||||
|
}
|
||||||
|
pveClientCfg := CreateProxmoxConfig(&pveCfg)
|
||||||
|
if pveClientCfg.Host != "https://pve.local:8006" {
|
||||||
|
t.Fatalf("expected default port on PVE host, got %q", pveClientCfg.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
pbsCfg := PBSInstance{
|
||||||
|
Host: "pbs.local",
|
||||||
|
User: "root@pam",
|
||||||
|
}
|
||||||
|
pbsClientCfg := CreatePBSConfig(&pbsCfg)
|
||||||
|
if pbsClientCfg.Host != "https://pbs.local:8007" {
|
||||||
|
t.Fatalf("expected default PBS port on host, got %q", pbsClientCfg.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
pmgCfg := PMGInstance{
|
||||||
|
Host: "https://pmg.local/api",
|
||||||
|
User: "root@pam",
|
||||||
|
}
|
||||||
|
pmgClientCfg := CreatePMGConfig(&pmgCfg)
|
||||||
|
if pmgClientCfg.Host != "https://pmg.local:8006" {
|
||||||
|
t.Fatalf("expected default PMG port on host, got %q", pmgClientCfg.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
fromFields := CreateProxmoxConfigFromFields("https://cluster.local", "root@pam", "", "token!name", "value", "", true)
|
||||||
|
if fromFields.Host != "https://cluster.local:8006" {
|
||||||
|
t.Fatalf("expected default port on host built from fields, got %q", fromFields.Host)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue