-
Notifications
You must be signed in to change notification settings - Fork 33
Fix providerID & getZone Implementation #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ import ( | |
|
|
||
| "github.com/apache/cloudstack-go/v2/cloudstack" | ||
| "gopkg.in/gcfg.v1" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| "k8s.io/apimachinery/pkg/types" | ||
| cloudprovider "k8s.io/cloud-provider" | ||
| "k8s.io/klog/v2" | ||
|
|
@@ -50,9 +51,10 @@ type CSConfig struct { | |
|
|
||
| // CSCloud is an implementation of Interface for CloudStack. | ||
| type CSCloud struct { | ||
| client *cloudstack.CloudStackClient | ||
| projectID string // If non-"", all resources will be created within this project | ||
| zone string | ||
| client *cloudstack.CloudStackClient | ||
| projectID string // If non-"", all resources will be created within this project | ||
| zone string | ||
| clientBuilder cloudprovider.ControllerClientBuilder | ||
| } | ||
|
|
||
| func init() { | ||
|
|
@@ -100,6 +102,7 @@ func newCSCloud(cfg *CSConfig) (*CSCloud, error) { | |
|
|
||
| // Initialize passes a Kubernetes clientBuilder interface to the cloud provider | ||
| func (cs *CSCloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, stop <-chan struct{}) { | ||
| cs.clientBuilder = clientBuilder | ||
| } | ||
|
|
||
| // LoadBalancer returns an implementation of LoadBalancer for CloudStack. | ||
|
|
@@ -172,15 +175,20 @@ func (cs *CSCloud) GetZone(ctx context.Context) (cloudprovider.Zone, error) { | |
| zone := cloudprovider.Zone{} | ||
|
|
||
| if cs.zone == "" { | ||
| hostname, err := os.Hostname() | ||
| // In Kubernetes pods, os.Hostname() returns the pod name, not the node hostname. | ||
| // We need to get the node name from the pod's spec.nodeName using the Kubernetes API. | ||
| nodeName, err := cs.getNodeNameFromPod(ctx) | ||
| if err != nil { | ||
| return zone, fmt.Errorf("failed to get hostname for retrieving the zone: %v", err) | ||
| return zone, fmt.Errorf("failed to get node name for retrieving the zone: %v", err) | ||
| } | ||
|
|
||
| instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByName(hostname) | ||
| instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByName( | ||
| nodeName, | ||
| cloudstack.WithProject(cs.projectID), | ||
| ) | ||
| if err != nil { | ||
| if count == 0 { | ||
| return zone, fmt.Errorf("could not find instance for retrieving the zone: %v", err) | ||
| return zone, fmt.Errorf("could not find CloudStack instance with name %s for retrieving the zone: %v", nodeName, err) | ||
| } | ||
| return zone, fmt.Errorf("error getting instance for retrieving the zone: %v", err) | ||
| } | ||
|
|
@@ -200,7 +208,7 @@ func (cs *CSCloud) GetZoneByProviderID(ctx context.Context, providerID string) ( | |
| zone := cloudprovider.Zone{} | ||
|
|
||
| instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByID( | ||
| providerID, | ||
| cs.getInstanceIDFromProviderID(providerID), | ||
| cloudstack.WithProject(cs.projectID), | ||
| ) | ||
| if err != nil { | ||
|
|
@@ -238,3 +246,54 @@ func (cs *CSCloud) GetZoneByNodeName(ctx context.Context, nodeName types.NodeNam | |
|
|
||
| return zone, nil | ||
| } | ||
|
|
||
| // getNodeNameFromPod gets the node name where this pod is running by querying the Kubernetes API. | ||
| // It uses the pod's name and namespace (from environment variables or hostname) to look up the pod | ||
| // and retrieve its spec.nodeName field. | ||
| func (cs *CSCloud) getNodeNameFromPod(ctx context.Context) (string, error) { | ||
| if cs.clientBuilder == nil { | ||
| return "", fmt.Errorf("clientBuilder not initialized, cannot query Kubernetes API") | ||
| } | ||
|
|
||
| client, err := cs.clientBuilder.Client("cloud-controller-manager") | ||
| if err != nil { | ||
| return "", fmt.Errorf("failed to get Kubernetes client: %v", err) | ||
| } | ||
|
|
||
| // Get pod name and namespace | ||
| // In Kubernetes, the pod name is available as HOSTNAME environment variable | ||
| // or we can use os.Hostname() which returns the pod name | ||
| podName := os.Getenv("HOSTNAME") | ||
| if podName == "" { | ||
| var err error | ||
| podName, err = os.Hostname() | ||
| if err != nil { | ||
| return "", fmt.Errorf("failed to get pod name: %v", err) | ||
| } | ||
| } | ||
|
|
||
| // Get namespace from environment variable or default to kube-system for CCM | ||
| namespace := os.Getenv("POD_NAMESPACE") | ||
| if namespace == "" { | ||
| // Try reading from service account namespace file (available in pods) | ||
| if data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil { | ||
| namespace = string(data) | ||
| } else { | ||
| // Default namespace for cloud controller manager | ||
| namespace = "kube-system" | ||
| } | ||
| } | ||
|
|
||
| // Get the pod object from Kubernetes API | ||
| pod, err := client.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{}) | ||
| if err != nil { | ||
| return "", fmt.Errorf("failed to get pod %s/%s from Kubernetes API: %v", namespace, podName, err) | ||
| } | ||
|
|
||
| if pod.Spec.NodeName == "" { | ||
| return "", fmt.Errorf("pod %s/%s does not have a nodeName assigned yet", namespace, podName) | ||
| } | ||
|
|
||
| klog.V(4).Infof("found node name %s for pod %s/%s", pod.Spec.NodeName, namespace, podName) | ||
| return pod.Spec.NodeName, nil | ||
| } | ||
|
Comment on lines
+253
to
+299
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,7 @@ import ( | |
| "errors" | ||
| "fmt" | ||
| "regexp" | ||
| "strings" | ||
|
|
||
| "github.com/apache/cloudstack-go/v2/cloudstack" | ||
| corev1 "k8s.io/api/core/v1" | ||
|
|
@@ -53,7 +54,7 @@ func (cs *CSCloud) NodeAddresses(ctx context.Context, name types.NodeName) ([]co | |
| // NodeAddressesByProviderID returns the addresses of the specified instance. | ||
| func (cs *CSCloud) NodeAddressesByProviderID(ctx context.Context, providerID string) ([]corev1.NodeAddress, error) { | ||
| instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByID( | ||
| providerID, | ||
| cs.getInstanceIDFromProviderID(providerID), | ||
| cloudstack.WithProject(cs.projectID), | ||
| ) | ||
| if err != nil { | ||
|
|
@@ -125,7 +126,7 @@ func (cs *CSCloud) InstanceType(ctx context.Context, name types.NodeName) (strin | |
| // InstanceTypeByProviderID returns the type of the specified instance. | ||
| func (cs *CSCloud) InstanceTypeByProviderID(ctx context.Context, providerID string) (string, error) { | ||
| instance, count, err := cs.client.VirtualMachine.GetVirtualMachineByID( | ||
| providerID, | ||
| cs.getInstanceIDFromProviderID(providerID), | ||
| cloudstack.WithProject(cs.projectID), | ||
| ) | ||
| if err != nil { | ||
|
|
@@ -151,7 +152,7 @@ func (cs *CSCloud) CurrentNodeName(ctx context.Context, hostname string) (types. | |
| // InstanceExistsByProviderID returns if the instance still exists. | ||
| func (cs *CSCloud) InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error) { | ||
| _, count, err := cs.client.VirtualMachine.GetVirtualMachineByID( | ||
| providerID, | ||
| cs.getInstanceIDFromProviderID(providerID), | ||
| cloudstack.WithProject(cs.projectID), | ||
| ) | ||
| if err != nil { | ||
|
|
@@ -185,6 +186,11 @@ func (cs *CSCloud) InstanceShutdown(ctx context.Context, node *corev1.Node) (boo | |
|
|
||
| func (cs *CSCloud) InstanceMetadata(ctx context.Context, node *corev1.Node) (*cloudprovider.InstanceMetadata, error) { | ||
|
|
||
| instanceID, err := cs.InstanceID(ctx, types.NodeName(node.Name)) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| instanceType, err := cs.InstanceType(ctx, types.NodeName(node.Name)) | ||
| if err != nil { | ||
| return nil, err | ||
|
|
@@ -201,10 +207,22 @@ func (cs *CSCloud) InstanceMetadata(ctx context.Context, node *corev1.Node) (*cl | |
| } | ||
|
|
||
| return &cloudprovider.InstanceMetadata{ | ||
| ProviderID: cs.ProviderName(), | ||
| ProviderID: cs.getProviderIDFromInstanceID(instanceID), | ||
| InstanceType: instanceType, | ||
| NodeAddresses: addresses, | ||
| Zone: cs.zone, | ||
| Region: zone.Region, | ||
| }, nil | ||
| } | ||
|
|
||
| func (cs *CSCloud) getProviderIDFromInstanceID(instanceID string) string { | ||
| return fmt.Sprintf("%s://%s", cs.ProviderName(), instanceID) | ||
| } | ||
|
|
||
| func (cs *CSCloud) getInstanceIDFromProviderID(providerID string) string { | ||
| parts := strings.Split(providerID, "://") | ||
| if len(parts) == 1 { | ||
| return providerID | ||
| } | ||
| return parts[1] | ||
|
Comment on lines
+223
to
+227
|
||
| } | ||
|
Comment on lines
+218
to
+228
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The namespace trimming logic doesn't handle potential whitespace. When reading from
/var/run/secrets/kubernetes.io/serviceaccount/namespace, the data might include trailing newlines or whitespace. Consider usingstrings.TrimSpace(string(data))instead of juststring(data)to ensure clean namespace values.