mirror of
https://github.com/outbackdingo/proxmox-cloud-controller-manager.git
synced 2026-01-27 10:20:13 +00:00
feat: add config options token_id_file & token_secret_file
Adds additional config options to read proxmox-cluster credentials from separate files. Signed-off-by: 3deep5me <manuel.karim5@gmail.com>
This commit is contained in:
@@ -76,8 +76,8 @@ type ClustersConfig struct {
|
||||
var (
|
||||
ErrMissingPVERegion = errors.New("missing PVE region in cloud config")
|
||||
ErrMissingPVEAPIURL = errors.New("missing PVE API URL in cloud config")
|
||||
ErrAuthCredentialsMissing = errors.New("user or token credentials are required")
|
||||
ErrInvalidAuthCredentials = errors.New("must specify one of user or token credentials, not both")
|
||||
ErrAuthCredentialsMissing = errors.New("user, token or file credentials are required")
|
||||
ErrInvalidAuthCredentials = errors.New("must specify one of user, token or file credentials, not multiple")
|
||||
ErrInvalidCloudConfig = errors.New("invalid cloud config")
|
||||
ErrInvalidNetworkMode = fmt.Errorf("invalid network mode, valid modes are %v", ValidNetworkModes)
|
||||
)
|
||||
@@ -93,11 +93,15 @@ func ReadCloudConfig(config io.Reader) (ClustersConfig, error) {
|
||||
}
|
||||
|
||||
for idx, c := range cfg.Clusters {
|
||||
if c.Username != "" && c.Password != "" {
|
||||
if c.TokenID != "" || c.TokenSecret != "" {
|
||||
return ClustersConfig{}, fmt.Errorf("cluster #%d: %w", idx+1, ErrInvalidAuthCredentials)
|
||||
}
|
||||
} else if c.TokenID == "" || c.TokenSecret == "" {
|
||||
hasTokenAuth := c.TokenID != "" || c.TokenSecret != ""
|
||||
hasTokenFileAuth := c.TokenIDFile != "" || c.TokenSecretFile != ""
|
||||
|
||||
hasUserAuth := c.Username != "" && c.Password != ""
|
||||
if (hasTokenAuth && hasUserAuth) || (hasTokenFileAuth && hasUserAuth) || (hasTokenAuth && hasTokenFileAuth) {
|
||||
return ClustersConfig{}, fmt.Errorf("cluster #%d: %w", idx+1, ErrInvalidAuthCredentials)
|
||||
}
|
||||
|
||||
if !hasTokenAuth && !hasTokenFileAuth && !hasUserAuth {
|
||||
return ClustersConfig{}, fmt.Errorf("cluster #%d: %w", idx+1, ErrAuthCredentialsMissing)
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,20 @@ clusters:
|
||||
assert.ErrorIs(t, err, providerconfig.ErrAuthCredentialsMissing)
|
||||
assert.NotNil(t, cfg)
|
||||
|
||||
// Valid config with one cluster and secret_file
|
||||
cfg, err = providerconfig.ReadCloudConfig(strings.NewReader(`
|
||||
clusters:
|
||||
- url: https://example.com
|
||||
insecure: false
|
||||
token_id_file: "/etc/proxmox-secrets/cluster1/token_id"
|
||||
token_secret_file: "/etc/proxmox-secrets/cluster1/token_secret"
|
||||
region: cluster-1
|
||||
`))
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, cfg)
|
||||
assert.Equal(t, 1, len(cfg.Clusters))
|
||||
assert.Equal(t, "/etc/proxmox-secrets/cluster1/token_id", cfg.Clusters[0].TokenIDFile)
|
||||
|
||||
// Valid config with one cluster
|
||||
cfg, err = providerconfig.ReadCloudConfig(strings.NewReader(`
|
||||
clusters:
|
||||
@@ -118,6 +132,21 @@ clusters:
|
||||
assert.Equal(t, 1, len(cfg.Clusters))
|
||||
assert.Equal(t, providerconfig.ProviderCapmox, cfg.Features.Provider)
|
||||
|
||||
// Errors when token_id/token_secret are set with token_id_file/token_secret_file
|
||||
_, err = providerconfig.ReadCloudConfig(strings.NewReader(`
|
||||
features:
|
||||
provider: 'capmox'
|
||||
clusters:
|
||||
- url: https://example.com
|
||||
insecure: false
|
||||
token_id_file: "/etc/proxmox-secrets/cluster1/token_id"
|
||||
token_secret_file: "/etc/proxmox-secrets/cluster1/token_secret"
|
||||
token_id: "ha"
|
||||
token_secret: "secret"
|
||||
region: cluster-1
|
||||
`))
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// Errors when username/password are set with token_id/token_secret
|
||||
_, err = providerconfig.ReadCloudConfig(strings.NewReader(`
|
||||
features:
|
||||
|
||||
@@ -35,13 +35,15 @@ import (
|
||||
|
||||
// ProxmoxCluster defines a Proxmox cluster configuration.
|
||||
type ProxmoxCluster struct {
|
||||
URL string `yaml:"url"`
|
||||
Insecure bool `yaml:"insecure,omitempty"`
|
||||
TokenID string `yaml:"token_id,omitempty"`
|
||||
TokenSecret string `yaml:"token_secret,omitempty"`
|
||||
Username string `yaml:"username,omitempty"`
|
||||
Password string `yaml:"password,omitempty"`
|
||||
Region string `yaml:"region,omitempty"`
|
||||
URL string `yaml:"url"`
|
||||
Insecure bool `yaml:"insecure,omitempty"`
|
||||
TokenIDFile string `yaml:"token_id_file,omitempty"`
|
||||
TokenSecretFile string `yaml:"token_secret_file,omitempty"`
|
||||
TokenID string `yaml:"token_id,omitempty"`
|
||||
TokenSecret string `yaml:"token_secret,omitempty"`
|
||||
Username string `yaml:"username,omitempty"`
|
||||
Password string `yaml:"password,omitempty"`
|
||||
Region string `yaml:"region,omitempty"`
|
||||
}
|
||||
|
||||
// ProxmoxPool is a Proxmox client.
|
||||
@@ -56,6 +58,24 @@ func NewProxmoxPool(config []*ProxmoxCluster, hClient *http.Client) (*ProxmoxPoo
|
||||
clients := make(map[string]*proxmox.Client, clusters)
|
||||
|
||||
for _, cfg := range config {
|
||||
if cfg.TokenID == "" {
|
||||
var err error
|
||||
|
||||
cfg.TokenID, err = readValueFromFile(cfg.TokenIDFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.TokenSecret == "" {
|
||||
var err error
|
||||
|
||||
cfg.TokenSecret, err = readValueFromFile(cfg.TokenSecretFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
tlsconf := &tls.Config{InsecureSkipVerify: true}
|
||||
if !cfg.Insecure {
|
||||
tlsconf = nil
|
||||
@@ -261,3 +281,16 @@ func (c *ProxmoxPool) getSMBSetting(vmInfo map[string]interface{}, name string)
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func readValueFromFile(path string) (string, error) {
|
||||
if path == "" {
|
||||
return "", fmt.Errorf("path cannot be empty")
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read file '%s': %w", path, err)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(content)), nil
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package proxmoxpool_test
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/jarcoal/httpmock"
|
||||
@@ -28,7 +29,6 @@ import (
|
||||
)
|
||||
|
||||
func newClusterEnv() []*pxpool.ProxmoxCluster {
|
||||
// copilot convert the cfg call to an array of []*proxmox_pool.ProxmoxCluster:
|
||||
cfg := []*pxpool.ProxmoxCluster{
|
||||
{
|
||||
URL: "https://127.0.0.1:8006/api2/json",
|
||||
@@ -49,6 +49,20 @@ func newClusterEnv() []*pxpool.ProxmoxCluster {
|
||||
return cfg
|
||||
}
|
||||
|
||||
func newClusterEnvWithFiles(tokenIDPath, tokenSecretPath string) []*pxpool.ProxmoxCluster {
|
||||
cfg := []*pxpool.ProxmoxCluster{
|
||||
{
|
||||
URL: "https://127.0.0.1:8006/api2/json",
|
||||
Insecure: false,
|
||||
TokenIDFile: tokenIDPath,
|
||||
TokenSecretFile: tokenSecretPath,
|
||||
Region: "cluster-1",
|
||||
},
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func TestNewClient(t *testing.T) {
|
||||
cfg := newClusterEnv()
|
||||
assert.NotNil(t, cfg)
|
||||
@@ -62,6 +76,29 @@ func TestNewClient(t *testing.T) {
|
||||
assert.NotNil(t, pClient)
|
||||
}
|
||||
|
||||
func TestNewClientWithCredentialsFromFile(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
tokenIDFile, err := os.CreateTemp(tempDir, "token_id")
|
||||
assert.Nil(t, err)
|
||||
|
||||
tokenSecretFile, err := os.CreateTemp(tempDir, "token_secret")
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, err = tokenIDFile.WriteString("user!token-id")
|
||||
assert.Nil(t, err)
|
||||
_, err = tokenSecretFile.WriteString("secret")
|
||||
assert.Nil(t, err)
|
||||
|
||||
cfg := newClusterEnvWithFiles(tokenIDFile.Name(), tokenSecretFile.Name())
|
||||
|
||||
pClient, err := pxpool.NewProxmoxPool(cfg, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, pClient)
|
||||
assert.Equal(t, "user!token-id", cfg[0].TokenID)
|
||||
assert.Equal(t, "secret", cfg[0].TokenSecret)
|
||||
}
|
||||
|
||||
func TestCheckClusters(t *testing.T) {
|
||||
cfg := newClusterEnv()
|
||||
assert.NotNil(t, cfg)
|
||||
|
||||
Reference in New Issue
Block a user