mirror of
https://github.com/outbackdingo/proxmox-cloud-controller-manager.git
synced 2026-01-27 10:20:13 +00:00
feat: enable support for capmox
This makes ccm compatible with cluster api and cluster api provider proxmox (capmox) Signed-off-by: Matthias Teich <matthias.teich@gdata.de>
This commit is contained in:
@@ -16,7 +16,7 @@ maintainers:
|
|||||||
# This is the chart version. This version number should be incremented each time you make changes
|
# This is the chart version. This version number should be incremented each time you make changes
|
||||||
# to the chart and its templates, including the app version.
|
# to the chart and its templates, including the app version.
|
||||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||||
version: 0.2.9
|
version: 0.2.10
|
||||||
# This is the version number of the application being deployed. This version number should be
|
# This is the version number of the application being deployed. This version number should be
|
||||||
# incremented each time you make changes to the application. Versions are not expected to
|
# incremented each time you make changes to the application. Versions are not expected to
|
||||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ existingConfigSecretKey: config.yaml
|
|||||||
|
|
||||||
# -- Proxmox cluster config.
|
# -- Proxmox cluster config.
|
||||||
config:
|
config:
|
||||||
|
features:
|
||||||
|
# specify provider: proxmox if you are using capmox (cluster api provider for proxmox)
|
||||||
|
provider: 'default'
|
||||||
clusters: []
|
clusters: []
|
||||||
# - url: https://cluster-api-1.exmple.com:8006/api2/json
|
# - url: https://cluster-api-1.exmple.com:8006/api2/json
|
||||||
# insecure: false
|
# insecure: false
|
||||||
|
|||||||
@@ -26,8 +26,20 @@ import (
|
|||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Provider specifies the provider. Can be 'default' or 'capmox'
|
||||||
|
type Provider string
|
||||||
|
|
||||||
|
// ProviderDefault is the default provider
|
||||||
|
const ProviderDefault Provider = "default"
|
||||||
|
|
||||||
|
// ProviderCapmox is the Provider for capmox
|
||||||
|
const ProviderCapmox Provider = "capmox"
|
||||||
|
|
||||||
// ClustersConfig is proxmox multi-cluster cloud config.
|
// ClustersConfig is proxmox multi-cluster cloud config.
|
||||||
type ClustersConfig struct {
|
type ClustersConfig struct {
|
||||||
|
Features struct {
|
||||||
|
Provider Provider `yaml:"provider,omitempty"`
|
||||||
|
} `yaml:"features,omitempty"`
|
||||||
Clusters []struct {
|
Clusters []struct {
|
||||||
URL string `yaml:"url"`
|
URL string `yaml:"url"`
|
||||||
Insecure bool `yaml:"insecure,omitempty"`
|
Insecure bool `yaml:"insecure,omitempty"`
|
||||||
@@ -67,6 +79,10 @@ func ReadCloudConfig(config io.Reader) (ClustersConfig, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.Features.Provider == "" {
|
||||||
|
cfg.Features.Provider = ProviderDefault
|
||||||
|
}
|
||||||
|
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ clusters:
|
|||||||
assert.NotNil(t, cfg)
|
assert.NotNil(t, cfg)
|
||||||
assert.Equal(t, 1, len(cfg.Clusters))
|
assert.Equal(t, 1, len(cfg.Clusters))
|
||||||
|
|
||||||
// Valid config with one cluster (username/password)
|
// Valid config with one cluster (username/password), implicit default provider
|
||||||
cfg, err = cluster.ReadCloudConfig(strings.NewReader(`
|
cfg, err = cluster.ReadCloudConfig(strings.NewReader(`
|
||||||
clusters:
|
clusters:
|
||||||
- url: https://example.com
|
- url: https://example.com
|
||||||
@@ -81,6 +81,39 @@ clusters:
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotNil(t, cfg)
|
assert.NotNil(t, cfg)
|
||||||
assert.Equal(t, 1, len(cfg.Clusters))
|
assert.Equal(t, 1, len(cfg.Clusters))
|
||||||
|
assert.Equal(t, cluster.ProviderDefault, cfg.Features.Provider)
|
||||||
|
|
||||||
|
// Valid config with one cluster (username/password), explicit provider default
|
||||||
|
cfg, err = cluster.ReadCloudConfig(strings.NewReader(`
|
||||||
|
features:
|
||||||
|
provider: 'default'
|
||||||
|
clusters:
|
||||||
|
- url: https://example.com
|
||||||
|
insecure: false
|
||||||
|
username: "user@pam"
|
||||||
|
password: "secret"
|
||||||
|
region: cluster-1
|
||||||
|
`))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.NotNil(t, cfg)
|
||||||
|
assert.Equal(t, 1, len(cfg.Clusters))
|
||||||
|
assert.Equal(t, cluster.ProviderDefault, cfg.Features.Provider)
|
||||||
|
|
||||||
|
// Valid config with one cluster (username/password), explicit provider capmox
|
||||||
|
cfg, err = cluster.ReadCloudConfig(strings.NewReader(`
|
||||||
|
features:
|
||||||
|
provider: 'capmox'
|
||||||
|
clusters:
|
||||||
|
- url: https://example.com
|
||||||
|
insecure: false
|
||||||
|
username: "user@pam"
|
||||||
|
password: "secret"
|
||||||
|
region: cluster-1
|
||||||
|
`))
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.NotNil(t, cfg)
|
||||||
|
assert.Equal(t, 1, len(cfg.Clusters))
|
||||||
|
assert.Equal(t, cluster.ProviderCapmox, cfg.Features.Provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadCloudConfigFromFile(t *testing.T) {
|
func TestReadCloudConfigFromFile(t *testing.T) {
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ func GetProviderID(region string, vmr *pxapi.VmRef) string {
|
|||||||
return fmt.Sprintf("%s://%s/%d", ProviderName, region, vmr.VmId())
|
return fmt.Sprintf("%s://%s/%d", ProviderName, region, vmr.VmId())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetProviderIDFromUUID returns the magic providerID for kubernetes node.
|
||||||
|
func GetProviderIDFromUUID(uuid string) string {
|
||||||
|
return fmt.Sprintf("%s://%s", ProviderName, uuid)
|
||||||
|
}
|
||||||
|
|
||||||
// GetVMID returns the VM ID from the providerID.
|
// GetVMID returns the VM ID from the providerID.
|
||||||
func GetVMID(providerID string) (int, error) {
|
func GetVMID(providerID string) (int, error) {
|
||||||
if !strings.HasPrefix(providerID, ProviderName) {
|
if !strings.HasPrefix(providerID, ProviderName) {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func newCloud(config *cluster.ClustersConfig) (cloudprovider.Interface, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
instancesInterface := newInstances(client)
|
instancesInterface := newInstances(client, config.Features.Provider)
|
||||||
|
|
||||||
return &cloud{
|
return &cloud{
|
||||||
client: client,
|
client: client,
|
||||||
|
|||||||
@@ -34,12 +34,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type instances struct {
|
type instances struct {
|
||||||
c *cluster.Cluster
|
c *cluster.Cluster
|
||||||
|
provider cluster.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
func newInstances(client *cluster.Cluster) *instances {
|
func newInstances(client *cluster.Cluster, provider cluster.Provider) *instances {
|
||||||
return &instances{
|
return &instances{
|
||||||
c: client,
|
c: client,
|
||||||
|
provider: provider,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +151,12 @@ func (i *instances) InstanceMetadata(_ context.Context, node *v1.Node) (*cloudpr
|
|||||||
return nil, fmt.Errorf("instances.InstanceMetadata() - failed to find instance by name/uuid %s: %v, skipped", node.Name, err)
|
return nil, fmt.Errorf("instances.InstanceMetadata() - failed to find instance by name/uuid %s: %v, skipped", node.Name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if i.provider == cluster.ProviderCapmox {
|
||||||
|
providerID = provider.GetProviderIDFromUUID(uuid)
|
||||||
|
} else {
|
||||||
|
providerID = provider.GetProviderID(region, vmRef)
|
||||||
|
}
|
||||||
} else if !strings.HasPrefix(node.Spec.ProviderID, provider.ProviderName) {
|
} else if !strings.HasPrefix(node.Spec.ProviderID, provider.ProviderName) {
|
||||||
klog.V(4).InfoS("instances.InstanceMetadata() omitting unmanaged node", "node", klog.KObj(node), "providerID", node.Spec.ProviderID)
|
klog.V(4).InfoS("instances.InstanceMetadata() omitting unmanaged node", "node", klog.KObj(node), "providerID", node.Spec.ProviderID)
|
||||||
|
|
||||||
@@ -178,7 +186,7 @@ func (i *instances) InstanceMetadata(_ context.Context, node *v1.Node) (*cloudpr
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &cloudprovider.InstanceMetadata{
|
return &cloudprovider.InstanceMetadata{
|
||||||
ProviderID: provider.GetProviderID(region, vmRef),
|
ProviderID: providerID,
|
||||||
NodeAddresses: addresses,
|
NodeAddresses: addresses,
|
||||||
InstanceType: instanceType,
|
InstanceType: instanceType,
|
||||||
Zone: vmRef.Node(),
|
Zone: vmRef.Node(),
|
||||||
@@ -192,6 +200,17 @@ func (i *instances) InstanceMetadata(_ context.Context, node *v1.Node) (*cloudpr
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *instances) getInstance(node *v1.Node) (*pxapi.VmRef, string, error) {
|
func (i *instances) getInstance(node *v1.Node) (*pxapi.VmRef, string, error) {
|
||||||
|
if i.provider == cluster.ProviderCapmox {
|
||||||
|
uuid := node.Status.NodeInfo.SystemUUID
|
||||||
|
|
||||||
|
vmRef, region, err := i.c.FindVMByUUID(uuid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("instances.getInstance() error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return vmRef, region, nil
|
||||||
|
}
|
||||||
|
|
||||||
vm, region, err := provider.ParseProviderID(node.Spec.ProviderID)
|
vm, region, err := provider.ParseProviderID(node.Spec.ProviderID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", fmt.Errorf("instances.getInstance() error: %v", err)
|
return nil, "", fmt.Errorf("instances.getInstance() error: %v", err)
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
"github.com/sergelogvinov/proxmox-cloud-controller-manager/pkg/cluster"
|
proxmoxcluster "github.com/sergelogvinov/proxmox-cloud-controller-manager/pkg/cluster"
|
||||||
provider "github.com/sergelogvinov/proxmox-cloud-controller-manager/pkg/provider"
|
"github.com/sergelogvinov/proxmox-cloud-controller-manager/pkg/provider"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@@ -44,7 +44,7 @@ type ccmTestSuite struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ts *ccmTestSuite) SetupTest() {
|
func (ts *ccmTestSuite) SetupTest() {
|
||||||
cfg, err := cluster.ReadCloudConfig(strings.NewReader(`
|
cfg, err := proxmoxcluster.ReadCloudConfig(strings.NewReader(`
|
||||||
clusters:
|
clusters:
|
||||||
- url: https://127.0.0.1:8006/api2/json
|
- url: https://127.0.0.1:8006/api2/json
|
||||||
insecure: false
|
insecure: false
|
||||||
@@ -123,12 +123,12 @@ clusters:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
cluster, err := cluster.NewCluster(&cfg, &http.Client{})
|
cluster, err := proxmoxcluster.NewCluster(&cfg, &http.Client{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ts.T().Fatalf("failed to create cluster client: %v", err)
|
ts.T().Fatalf("failed to create cluster client: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ts.i = newInstances(cluster)
|
ts.i = newInstances(cluster, proxmoxcluster.ProviderDefault)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *ccmTestSuite) TearDownTest() {
|
func (ts *ccmTestSuite) TearDownTest() {
|
||||||
|
|||||||
Reference in New Issue
Block a user