From 3376ac74c2c172bab4a2bd3463cebef22802bddf Mon Sep 17 00:00:00 2001 From: sp-yduck Date: Sun, 12 Nov 2023 00:59:09 +0900 Subject: [PATCH] update proxmox-go version --- cmd/proxmox-cloud-controller-manager.go | 234 +++++++++++----------- go.mod | 8 +- go.sum | 7 +- pkg/cloudprovider/cloud.go | 254 ++++++++++++------------ pkg/cloudprovider/instances.go | 177 +++++++++-------- pkg/cloudprovider/instances_test.go | 58 +++--- pkg/cloudprovider/suite_test.go | 125 ++++++------ 7 files changed, 432 insertions(+), 431 deletions(-) diff --git a/cmd/proxmox-cloud-controller-manager.go b/cmd/proxmox-cloud-controller-manager.go index d92aa03..8f4816a 100644 --- a/cmd/proxmox-cloud-controller-manager.go +++ b/cmd/proxmox-cloud-controller-manager.go @@ -1,117 +1,117 @@ -/* -Copyright 2020 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// This file should be written by each cloud provider. -// For an minimal working example, please refer to k8s.io/cloud-provider/sample/basic_main.go -// For more details, please refer to k8s.io/kubernetes/cmd/cloud-controller-manager/main.go - -// Package main provides the CCM implementation. -package main - -import ( - "os" - - "github.com/spf13/pflag" - "k8s.io/apimachinery/pkg/util/wait" - cloudprovider "k8s.io/cloud-provider" - "k8s.io/cloud-provider/app" - "k8s.io/cloud-provider/app/config" - "k8s.io/cloud-provider/options" - "k8s.io/component-base/cli" - cliflag "k8s.io/component-base/cli/flag" - _ "k8s.io/component-base/metrics/prometheus/clientgo" // load all the prometheus client-go plugins - _ "k8s.io/component-base/metrics/prometheus/version" // for version metric registration - "k8s.io/klog/v2" - - "github.com/sp-yduck/cloud-provider-proxmox/pkg/cloudprovider" -) - -func main() { - ccmOptions, err := options.NewCloudControllerManagerOptions() - if err != nil { - klog.Fatalf("unable to initialize command options: %v", err) - } - - fss := cliflag.NamedFlagSets{} - command := app.NewCloudControllerManagerCommand(ccmOptions, cloudInitializer, controllerInitializers(), fss, wait.NeverStop) - - command.Flags().VisitAll(func(flag *pflag.Flag) { - if flag.Name == "cloud-provider" { - if err := flag.Value.Set(proxmox.ProviderName); err != nil { - klog.Fatalf("unable to set cloud-provider flag value: %s", err) - } - } - }) - - code := cli.Run(command) - os.Exit(code) -} - -// If custom ClientNames are used, as below, then the controller will not use -// the API server bootstrapped RBAC, and instead will require it to be installed -// separately. -func controllerInitializers() map[string]app.ControllerInitFuncConstructor { - klog.Info("initializing controllers") - - controllerInitializers := app.DefaultInitFuncConstructors - if constructor, ok := controllerInitializers["cloud-node"]; ok { - constructor.InitContext.ClientName = proxmox.ClientName - controllerInitializers["cloud-node"] = constructor - } - - if constructor, ok := controllerInitializers["cloud-node-lifecycle"]; ok { - constructor.InitContext.ClientName = proxmox.ClientName - controllerInitializers["cloud-node-lifecycle"] = constructor - } - - if constructor, ok := controllerInitializers["service"]; ok { - constructor.InitContext.ClientName = proxmox.ClientName - controllerInitializers["service"] = constructor - } - - if constructor, ok := controllerInitializers["route"]; ok { - constructor.InitContext.ClientName = proxmox.ClientName - controllerInitializers["route"] = constructor - } - - return controllerInitializers -} - -func cloudInitializer(config *config.CompletedConfig) cloudprovider.Interface { - klog.Info("initializing cloud") - - cloudConfig := config.ComponentConfig.KubeCloudShared.CloudProvider - - // initialize cloud provider with the cloud provider name and config file provided - cloud, err := cloudprovider.InitCloudProvider(cloudConfig.Name, cloudConfig.CloudConfigFile) - if err != nil { - klog.Fatalf("Cloud provider could not be initialized: %v", err) - } - - if cloud == nil { - klog.Fatalf("Cloud provider is nil") - } - - if !cloud.HasClusterID() { - if config.ComponentConfig.KubeCloudShared.AllowUntaggedCloud { - klog.Warning("detected a cluster without a ClusterID. A ClusterID will be required in the future. Please tag your cluster to avoid any future issues") - } else { - klog.Fatalf("no ClusterID found. A ClusterID is required for the cloud provider to function properly. This check can be bypassed by setting the allow-untagged-cloud option") - } - } - - return cloud -} +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This file should be written by each cloud provider. +// For an minimal working example, please refer to k8s.io/cloud-provider/sample/basic_main.go +// For more details, please refer to k8s.io/kubernetes/cmd/cloud-controller-manager/main.go + +// Package main provides the CCM implementation. +package main + +import ( + "os" + + "github.com/spf13/pflag" + "k8s.io/apimachinery/pkg/util/wait" + cloudprovider "k8s.io/cloud-provider" + "k8s.io/cloud-provider/app" + "k8s.io/cloud-provider/app/config" + "k8s.io/cloud-provider/options" + "k8s.io/component-base/cli" + cliflag "k8s.io/component-base/cli/flag" + _ "k8s.io/component-base/metrics/prometheus/clientgo" // load all the prometheus client-go plugins + _ "k8s.io/component-base/metrics/prometheus/version" // for version metric registration + "k8s.io/klog/v2" + + "github.com/sp-yduck/cloud-provider-proxmox/pkg/cloudprovider" +) + +func main() { + ccmOptions, err := options.NewCloudControllerManagerOptions() + if err != nil { + klog.Fatalf("unable to initialize command options: %v", err) + } + + fss := cliflag.NamedFlagSets{} + command := app.NewCloudControllerManagerCommand(ccmOptions, cloudInitializer, controllerInitializers(), fss, wait.NeverStop) + + command.Flags().VisitAll(func(flag *pflag.Flag) { + if flag.Name == "cloud-provider" { + if err := flag.Value.Set(proxmox.ProviderName); err != nil { + klog.Fatalf("unable to set cloud-provider flag value: %s", err) + } + } + }) + + code := cli.Run(command) + os.Exit(code) +} + +// If custom ClientNames are used, as below, then the controller will not use +// the API server bootstrapped RBAC, and instead will require it to be installed +// separately. +func controllerInitializers() map[string]app.ControllerInitFuncConstructor { + klog.Info("initializing controllers") + + controllerInitializers := app.DefaultInitFuncConstructors + if constructor, ok := controllerInitializers["cloud-node"]; ok { + constructor.InitContext.ClientName = proxmox.ClientName + controllerInitializers["cloud-node"] = constructor + } + + if constructor, ok := controllerInitializers["cloud-node-lifecycle"]; ok { + constructor.InitContext.ClientName = proxmox.ClientName + controllerInitializers["cloud-node-lifecycle"] = constructor + } + + if constructor, ok := controllerInitializers["service"]; ok { + constructor.InitContext.ClientName = proxmox.ClientName + controllerInitializers["service"] = constructor + } + + if constructor, ok := controllerInitializers["route"]; ok { + constructor.InitContext.ClientName = proxmox.ClientName + controllerInitializers["route"] = constructor + } + + return controllerInitializers +} + +func cloudInitializer(config *config.CompletedConfig) cloudprovider.Interface { + klog.Info("initializing cloud") + + cloudConfig := config.ComponentConfig.KubeCloudShared.CloudProvider + + // initialize cloud provider with the cloud provider name and config file provided + cloud, err := cloudprovider.InitCloudProvider(cloudConfig.Name, cloudConfig.CloudConfigFile) + if err != nil { + klog.Fatalf("Cloud provider could not be initialized: %v", err) + } + + if cloud == nil { + klog.Fatalf("Cloud provider is nil") + } + + if !cloud.HasClusterID() { + if config.ComponentConfig.KubeCloudShared.AllowUntaggedCloud { + klog.Warning("detected a cluster without a ClusterID. A ClusterID will be required in the future. Please tag your cluster to avoid any future issues") + } else { + klog.Fatalf("no ClusterID found. A ClusterID is required for the cloud provider to function properly. This check can be bypassed by setting the allow-untagged-cloud option") + } + } + + return cloud +} diff --git a/go.mod b/go.mod index 4c6ca83..7f71cd6 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/pkg/errors v0.9.1 - github.com/sp-yduck/proxmox-go v0.0.0-20230817151858-dfc9563487ca + github.com/sp-yduck/proxmox-go v0.0.0-alpha22 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.4 gopkg.in/yaml.v3 v3.0.1 @@ -32,7 +32,7 @@ require ( github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect @@ -111,6 +111,4 @@ require ( sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect -) - -// replace cloud.google.com/go => cloud.google.com/go v0.100.2 +) \ No newline at end of file diff --git a/go.sum b/go.sum index 563284b..ebcca3e 100644 --- a/go.sum +++ b/go.sum @@ -105,8 +105,9 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= @@ -264,8 +265,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= -github.com/sp-yduck/proxmox-go v0.0.0-20230817151858-dfc9563487ca h1:CBqb4OSv1SNBF8CndaF8odjctOWSBDOXZJf2piZiEg8= -github.com/sp-yduck/proxmox-go v0.0.0-20230817151858-dfc9563487ca/go.mod h1:XtUQue7w5tuB6c7xputkhnN+A3EZbzDWq+uXmlkOPfQ= +github.com/sp-yduck/proxmox-go v0.0.0-alpha22 h1:xMY7SBfieHUXMZY0f7AShO2fmGJtZZkmGbI/9PnmQ/c= +github.com/sp-yduck/proxmox-go v0.0.0-alpha22/go.mod h1:iEI7ilRwyUaWvoxuXs/30UJ2f8Z2TWXCW1cZ3QeU2JY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI= github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= diff --git a/pkg/cloudprovider/cloud.go b/pkg/cloudprovider/cloud.go index 9085bb7..92ef836 100644 --- a/pkg/cloudprovider/cloud.go +++ b/pkg/cloudprovider/cloud.go @@ -1,127 +1,127 @@ -package proxmox - -import ( - "io" - - "github.com/pkg/errors" - "gopkg.in/yaml.v3" - cloudprovider "k8s.io/cloud-provider" - "k8s.io/klog/v2" -) - -const ( - // RegisteredProviderName is the name of the cloud provider registered with - // Kubernetes. - RegisteredProviderName string = "proxmox" - - // ProviderName is the name used for constructing Provider ID - ProviderName string = "proxmox" - - // ClientName is the user agent passed into the controller client builder. - ClientName string = "proxmox-cloud-controller-manager" - - // dualStackFeatureGateEnv is a required environment variable when enabling dual-stack nodes - // dualStackFeatureGateEnv string = "ENABLE_ALPHA_DUAL_STACK" -) - -type Proxmox struct { - instancesV2 cloudprovider.InstancesV2 -} - -type cloudProviderConfig struct { - ProxmoxConfig proxmoxConfig `yaml:"proxmox"` -} - -type proxmoxConfig struct { - URL string `yaml:"url"` - User string `yaml:"user"` - Password string `yaml:"password"` - TokenID string `yaml:"tokenID"` - Secret string `yaml:"secret"` -} - -func init() { - klog.Info("registering cloud provider") - - cloudprovider.RegisterCloudProvider(RegisteredProviderName, func(config io.Reader) (cloudprovider.Interface, error) { - providerConfig, err := readCloudProviderConfig(config) - if err != nil { - return nil, err - } - return newCloud(providerConfig) - }) -} - -func newCloud(config *cloudProviderConfig) (cloudprovider.Interface, error) { - klog.Info("creating new cloud") - instance, err := newInstances(config.ProxmoxConfig) - if err != nil { - return nil, err - } - px := &Proxmox{instancesV2: instance} - return px, nil -} - -func readCloudProviderConfig(configReader io.Reader) (*cloudProviderConfig, error) { - config := &cloudProviderConfig{} - if configReader == nil { - return nil, errors.New("configReader must not be nil") - } - if err := yaml.NewDecoder(configReader).Decode(config); err != nil { - return nil, err - } - cfg := config.ProxmoxConfig - if cfg.URL == "" { - return nil, errors.New("url must not be empty") - } - return config, nil -} - -func (px *Proxmox) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, stop <-chan struct{}) { -} - -// LoadBalancer returns a balancer interface. Also returns true if the -// interface is supported, false otherwise. -func (px *Proxmox) LoadBalancer() (cloudprovider.LoadBalancer, bool) { - return nil, false -} - -// Instances returns an instances interface. Also returns true if the -// interface is supported, false otherwise. -func (px *Proxmox) Instances() (cloudprovider.Instances, bool) { - return nil, false -} - -// Instances returns an instances interface. Also returns true if the -// interface is supported, false otherwise. -func (px *Proxmox) InstancesV2() (cloudprovider.InstancesV2, bool) { - return px.instancesV2, true -} - -// Zones returns a zones interface. Also returns true if the interface -// is supported, false otherwise. -func (px *Proxmox) Zones() (cloudprovider.Zones, bool) { - return nil, false -} - -// Clusters returns a clusters interface. Also returns true if the interface -// is supported, false otherwise. -func (px *Proxmox) Clusters() (cloudprovider.Clusters, bool) { - return nil, false -} - -// Routes returns a routes interface along with whether the interface -// is supported. -func (px *Proxmox) Routes() (cloudprovider.Routes, bool) { - return nil, false -} - -// ProviderName returns the cloud provider ID. -func (px *Proxmox) ProviderName() string { - return ProviderName -} - -// HasClusterID returns true if a ClusterID is required and set/ -func (px *Proxmox) HasClusterID() bool { - return true -} +package proxmox + +import ( + "io" + + "github.com/pkg/errors" + "gopkg.in/yaml.v3" + cloudprovider "k8s.io/cloud-provider" + "k8s.io/klog/v2" +) + +const ( + // RegisteredProviderName is the name of the cloud provider registered with + // Kubernetes. + RegisteredProviderName string = "proxmox" + + // ProviderName is the name used for constructing Provider ID + ProviderName string = "proxmox" + + // ClientName is the user agent passed into the controller client builder. + ClientName string = "proxmox-cloud-controller-manager" + + // dualStackFeatureGateEnv is a required environment variable when enabling dual-stack nodes + // dualStackFeatureGateEnv string = "ENABLE_ALPHA_DUAL_STACK" +) + +type Proxmox struct { + instancesV2 cloudprovider.InstancesV2 +} + +type cloudProviderConfig struct { + ProxmoxConfig proxmoxConfig `yaml:"proxmox"` +} + +type proxmoxConfig struct { + URL string `yaml:"url"` + User string `yaml:"user"` + Password string `yaml:"password"` + TokenID string `yaml:"tokenID"` + Secret string `yaml:"secret"` +} + +func init() { + klog.Info("registering cloud provider") + + cloudprovider.RegisterCloudProvider(RegisteredProviderName, func(config io.Reader) (cloudprovider.Interface, error) { + providerConfig, err := readCloudProviderConfig(config) + if err != nil { + return nil, err + } + return newCloud(providerConfig) + }) +} + +func newCloud(config *cloudProviderConfig) (cloudprovider.Interface, error) { + klog.Info("creating new cloud") + instance, err := newInstances(config.ProxmoxConfig) + if err != nil { + return nil, err + } + px := &Proxmox{instancesV2: instance} + return px, nil +} + +func readCloudProviderConfig(configReader io.Reader) (*cloudProviderConfig, error) { + config := &cloudProviderConfig{} + if configReader == nil { + return nil, errors.New("configReader must not be nil") + } + if err := yaml.NewDecoder(configReader).Decode(config); err != nil { + return nil, err + } + cfg := config.ProxmoxConfig + if cfg.URL == "" { + return nil, errors.New("url must not be empty") + } + return config, nil +} + +func (px *Proxmox) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, stop <-chan struct{}) { +} + +// LoadBalancer returns a balancer interface. Also returns true if the +// interface is supported, false otherwise. +func (px *Proxmox) LoadBalancer() (cloudprovider.LoadBalancer, bool) { + return nil, false +} + +// Instances returns an instances interface. Also returns true if the +// interface is supported, false otherwise. +func (px *Proxmox) Instances() (cloudprovider.Instances, bool) { + return nil, false +} + +// Instances returns an instances interface. Also returns true if the +// interface is supported, false otherwise. +func (px *Proxmox) InstancesV2() (cloudprovider.InstancesV2, bool) { + return px.instancesV2, true +} + +// Zones returns a zones interface. Also returns true if the interface +// is supported, false otherwise. +func (px *Proxmox) Zones() (cloudprovider.Zones, bool) { + return nil, false +} + +// Clusters returns a clusters interface. Also returns true if the interface +// is supported, false otherwise. +func (px *Proxmox) Clusters() (cloudprovider.Clusters, bool) { + return nil, false +} + +// Routes returns a routes interface along with whether the interface +// is supported. +func (px *Proxmox) Routes() (cloudprovider.Routes, bool) { + return nil, false +} + +// ProviderName returns the cloud provider ID. +func (px *Proxmox) ProviderName() string { + return ProviderName +} + +// HasClusterID returns true if a ClusterID is required and set/ +func (px *Proxmox) HasClusterID() bool { + return true +} diff --git a/pkg/cloudprovider/instances.go b/pkg/cloudprovider/instances.go index 04b944c..4c19110 100644 --- a/pkg/cloudprovider/instances.go +++ b/pkg/cloudprovider/instances.go @@ -1,88 +1,89 @@ -package proxmox - -import ( - "context" - "fmt" - - "github.com/sp-yduck/proxmox-go/api" - "github.com/sp-yduck/proxmox-go/proxmox" - "github.com/sp-yduck/proxmox-go/rest" - v1 "k8s.io/api/core/v1" - cloudprovider "k8s.io/cloud-provider" - "k8s.io/klog/v2" -) - -const ( - Giga = 1024 * 1024 * 1024 -) - -type instance struct { - compute *proxmox.Service -} - -func newInstances(config proxmoxConfig) (cloudprovider.InstancesV2, error) { - authConfig := proxmox.AuthConfig{ - Username: config.User, - Password: config.Password, - TokenID: config.TokenID, - Secret: config.Secret, - } - client, err := proxmox.NewService(config.URL, authConfig, true) - if err != nil { - return nil, err - } - return &instance{compute: client}, nil -} - -func (i *instance) InstanceExists(ctx context.Context, node *v1.Node) (bool, error) { - klog.Infof("checking if instance exists (node=%s)", node.Name) - - _, err := i.compute.VirtualMachineFromUUID(ctx, node.Status.NodeInfo.SystemUUID) - if err != nil { - if rest.IsNotFound(err) { - return false, nil - } - return true, err - } - - return true, nil -} - -func (i *instance) InstanceShutdown(ctx context.Context, node *v1.Node) (bool, error) { - klog.V(2).Info("InstanceShutdown called") - - vm, err := i.compute.VirtualMachineFromUUID(ctx, node.Status.NodeInfo.SystemUUID) - if err != nil { - return false, err - } - - shutdonw := vm.VM.Status == api.ProcessStatusStopped - return shutdonw, nil -} - -func (i *instance) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) { - providerID := fmt.Sprintf("%s://%s", ProviderName, node.Status.NodeInfo.SystemUUID) - klog.Infof("getting metadata for node %s (providerID=%s)", node.Name, providerID) - - vm, err := i.compute.VirtualMachineFromUUID(ctx, node.Status.NodeInfo.SystemUUID) - if err != nil { - return nil, err - } - - cpu := vm.VM.Cpus - mem := roundBtoGB(vm.VM.MaxMem) - instanceType := fmt.Sprintf("proxmox-qemu.cpu-%d.mem-%s", cpu, mem) - - return &cloudprovider.InstanceMetadata{ - ProviderID: providerID, - NodeAddresses: []v1.NodeAddress{}, - InstanceType: instanceType, - Zone: "", - Region: "", - }, nil -} - -func roundBtoGB(size int) string { - rounded := float32(size) / Giga - return fmt.Sprintf("%.1fG", rounded) -} +package proxmox + +import ( + "context" + "fmt" + + "github.com/sp-yduck/proxmox-go/api" + "github.com/sp-yduck/proxmox-go/proxmox" + "github.com/sp-yduck/proxmox-go/rest" + v1 "k8s.io/api/core/v1" + cloudprovider "k8s.io/cloud-provider" + "k8s.io/klog/v2" +) + +const ( + Giga = 1024 * 1024 * 1024 +) + +type instance struct { + compute *proxmox.Service +} + +func newInstances(config proxmoxConfig) (cloudprovider.InstancesV2, error) { + authConfig := proxmox.AuthConfig{ + Username: config.User, + Password: config.Password, + TokenID: config.TokenID, + Secret: config.Secret, + } + params := proxmox.NewParams(config.URL, authConfig, proxmox.ClientConfig{InsecureSkipVerify: true}) + client, err := proxmox.GetOrCreateService(params) + if err != nil { + return nil, err + } + return &instance{compute: client}, nil +} + +func (i *instance) InstanceExists(ctx context.Context, node *v1.Node) (bool, error) { + klog.Infof("checking if instance exists (node=%s)", node.Name) + + _, err := i.compute.VirtualMachineFromUUID(ctx, node.Status.NodeInfo.SystemUUID) + if err != nil { + if rest.IsNotFound(err) { + return false, nil + } + return true, err + } + + return true, nil +} + +func (i *instance) InstanceShutdown(ctx context.Context, node *v1.Node) (bool, error) { + klog.V(2).Info("InstanceShutdown called") + + vm, err := i.compute.VirtualMachineFromUUID(ctx, node.Status.NodeInfo.SystemUUID) + if err != nil { + return false, err + } + + shutdonw := vm.VM.Status == api.ProcessStatusStopped + return shutdonw, nil +} + +func (i *instance) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) { + providerID := fmt.Sprintf("%s://%s", ProviderName, node.Status.NodeInfo.SystemUUID) + klog.Infof("getting metadata for node %s (providerID=%s)", node.Name, providerID) + + vm, err := i.compute.VirtualMachineFromUUID(ctx, node.Status.NodeInfo.SystemUUID) + if err != nil { + return nil, err + } + + cpu := vm.VM.Cpus + mem := roundBtoGB(vm.VM.MaxMem) + instanceType := fmt.Sprintf("proxmox-qemu.cpu-%d.mem-%s", cpu, mem) + + return &cloudprovider.InstanceMetadata{ + ProviderID: providerID, + NodeAddresses: []v1.NodeAddress{}, + InstanceType: instanceType, + Zone: "", + Region: "", + }, nil +} + +func roundBtoGB(size int) string { + rounded := float32(size) / Giga + return fmt.Sprintf("%.1fG", rounded) +} diff --git a/pkg/cloudprovider/instances_test.go b/pkg/cloudprovider/instances_test.go index b4c01ec..73982c7 100644 --- a/pkg/cloudprovider/instances_test.go +++ b/pkg/cloudprovider/instances_test.go @@ -1,29 +1,29 @@ -package proxmox - -import ( - "context" -) - -func (s *TestSuite) TestInstanceExists() { - exists, err := s.instance.InstanceExists(context.TODO(), &s.node) - if err != nil { - s.T().Fatalf("failed get instance: %v", err) - } - s.T().Logf("get instance: %v", exists) -} - -func (s *TestSuite) TestInstanceMetadata() { - meta, err := s.instance.InstanceMetadata(context.TODO(), &s.node) - if err != nil { - s.T().Fatalf("failed get instance metadata: %v", err) - } - s.T().Logf("get instance metadata: %v", *meta) -} - -func (s *TestSuite) TestInstanceShutdown() { - shutdown, err := s.instance.InstanceShutdown(context.TODO(), &s.node) - if err != nil { - s.T().Fatalf("failed get shutdown status: %v", err) - } - s.T().Logf("get shutdown status: %v", shutdown) -} +package proxmox + +import ( + "context" +) + +func (s *TestSuite) TestInstanceExists() { + exists, err := s.instance.InstanceExists(context.TODO(), &s.node) + if err != nil { + s.T().Fatalf("failed get instance: %v", err) + } + s.T().Logf("get instance: %v", exists) +} + +func (s *TestSuite) TestInstanceMetadata() { + meta, err := s.instance.InstanceMetadata(context.TODO(), &s.node) + if err != nil { + s.T().Fatalf("failed get instance metadata: %v", err) + } + s.T().Logf("get instance metadata: %v", *meta) +} + +func (s *TestSuite) TestInstanceShutdown() { + shutdown, err := s.instance.InstanceShutdown(context.TODO(), &s.node) + if err != nil { + s.T().Fatalf("failed get shutdown status: %v", err) + } + s.T().Logf("get shutdown status: %v", shutdown) +} diff --git a/pkg/cloudprovider/suite_test.go b/pkg/cloudprovider/suite_test.go index 5eec220..f8d1969 100644 --- a/pkg/cloudprovider/suite_test.go +++ b/pkg/cloudprovider/suite_test.go @@ -1,62 +1,63 @@ -package proxmox - -import ( - "os" - "testing" - - "github.com/sp-yduck/proxmox-go/proxmox" - "github.com/stretchr/testify/suite" - v1 "k8s.io/api/core/v1" -) - -type TestSuite struct { - suite.Suite - instance instance - node v1.Node -} - -func (s *TestSuite) SetupSuite() { - s.setupInstance() - s.setupTestNode() -} - -func (s *TestSuite) setupInstance() { - url := os.Getenv("PROXMOX_URL") - user := os.Getenv("PROXMOX_USERNAME") - password := os.Getenv("PROXMOX_PASSWORD") - tokeid := os.Getenv("PROXMOX_TOKENID") - secret := os.Getenv("PROXMOX_SECRET") - if url == "" { - s.T().Fatal("url must not be empty") - } - authConfig := proxmox.AuthConfig{ - Username: user, - Password: password, - TokenID: tokeid, - Secret: secret, - } - - svc, err := proxmox.NewService(url, authConfig, true) - if err != nil { - s.T().Logf("username=%s, password=%s, tokenid=%s, secret=%s", user, password, tokeid, secret) - s.T().Fatalf("failed to create rest client: %v", err) - } - s.instance.compute = svc -} - -func (s *TestSuite) setupTestNode() { - uuid := os.Getenv("PROXMOX_TEST_UUID") - node := v1.Node{ - Status: v1.NodeStatus{ - NodeInfo: v1.NodeSystemInfo{ - SystemUUID: uuid, - }, - }, - } - node.SetName("test-node") - s.node = node -} - -func TestSuiteIntegration(t *testing.T) { - suite.Run(t, new(TestSuite)) -} +package proxmox + +import ( + "os" + "testing" + + "github.com/sp-yduck/proxmox-go/proxmox" + "github.com/stretchr/testify/suite" + v1 "k8s.io/api/core/v1" +) + +type TestSuite struct { + suite.Suite + instance instance + node v1.Node +} + +func (s *TestSuite) SetupSuite() { + s.setupInstance() + s.setupTestNode() +} + +func (s *TestSuite) setupInstance() { + url := os.Getenv("PROXMOX_URL") + user := os.Getenv("PROXMOX_USERNAME") + password := os.Getenv("PROXMOX_PASSWORD") + tokeid := os.Getenv("PROXMOX_TOKENID") + secret := os.Getenv("PROXMOX_SECRET") + if url == "" { + s.T().Fatal("url must not be empty") + } + authConfig := proxmox.AuthConfig{ + Username: user, + Password: password, + TokenID: tokeid, + Secret: secret, + } + + params := proxmox.NewParams(url, authConfig, proxmox.ClientConfig{InsecureSkipVerify: true}) + svc, err := proxmox.NewService(params) + if err != nil { + s.T().Logf("username=%s, password=%s, tokenid=%s, secret=%s", user, password, tokeid, secret) + s.T().Fatalf("failed to create rest client: %v", err) + } + s.instance.compute = svc +} + +func (s *TestSuite) setupTestNode() { + uuid := os.Getenv("PROXMOX_TEST_UUID") + node := v1.Node{ + Status: v1.NodeStatus{ + NodeInfo: v1.NodeSystemInfo{ + SystemUUID: uuid, + }, + }, + } + node.SetName("test-node") + s.node = node +} + +func TestSuiteIntegration(t *testing.T) { + suite.Run(t, new(TestSuite)) +}