Skip to content

Commit 9678d4f

Browse files
committed
reduce cloud api calls in cloud-node-controller by passing instanceMetadata to updateNodeAddress
1 parent f5a54e3 commit 9678d4f

File tree

2 files changed

+129
-115
lines changed

2 files changed

+129
-115
lines changed

staging/src/k8s.io/cloud-provider/controllers/node/node_controller.go

Lines changed: 119 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,12 @@ func (cnc *CloudNodeController) UpdateNodeStatus(ctx context.Context) {
155155
}
156156

157157
for i := range nodes.Items {
158-
cnc.updateNodeAddress(ctx, &nodes.Items[i])
158+
instanceMetadata, err := cnc.getInstanceNodeAddresses(ctx, &nodes.Items[i])
159+
if err != nil {
160+
klog.Errorf("Error getting instance metadata for node addresses: %v", err)
161+
continue
162+
}
163+
cnc.updateNodeAddress(ctx, &nodes.Items[i], instanceMetadata)
159164
}
160165

161166
for _, node := range nodes.Items {
@@ -217,42 +222,15 @@ func (cnc *CloudNodeController) reconcileNodeLabels(nodeName string) error {
217222
}
218223

219224
// UpdateNodeAddress updates the nodeAddress of a single node
220-
func (cnc *CloudNodeController) updateNodeAddress(ctx context.Context, node *v1.Node) {
225+
func (cnc *CloudNodeController) updateNodeAddress(ctx context.Context, node *v1.Node, instanceMetadata *cloudprovider.InstanceMetadata) {
221226
// Do not process nodes that are still tainted
222227
cloudTaint := getCloudTaint(node.Spec.Taints)
223228
if cloudTaint != nil {
224229
klog.V(5).Infof("This node %s is still tainted. Will not process.", node.Name)
225230
return
226231
}
227232

228-
instanceMetadataGetter := func(providerID string, node *v1.Node) (*cloudprovider.InstanceMetadata, error) {
229-
if instancesV2, ok := cnc.cloud.InstancesV2(); instancesV2 != nil && ok {
230-
return instancesV2.InstanceMetadata(ctx, node)
231-
}
232-
233-
// If InstancesV2 not implement, use Instances.
234-
instances, ok := cnc.cloud.Instances()
235-
if !ok {
236-
return nil, fmt.Errorf("failed to get instances from cloud provider")
237-
}
238-
239-
nodeAddresses, err := getNodeAddressesByProviderIDOrName(ctx, instances, node.Spec.ProviderID, node.Name)
240-
if err != nil {
241-
klog.Errorf("Error getting node addresses for node %q: %v", node.Name, err)
242-
return nil, err
243-
}
244-
return &cloudprovider.InstanceMetadata{
245-
NodeAddresses: nodeAddresses,
246-
}, nil
247-
}
248-
249-
instanceMeta, err := instanceMetadataGetter(node.Spec.ProviderID, node)
250-
if err != nil {
251-
utilruntime.HandleError(err)
252-
return
253-
}
254-
255-
nodeAddresses := instanceMeta.NodeAddresses
233+
nodeAddresses := instanceMetadata.NodeAddresses
256234
if len(nodeAddresses) == 0 {
257235
klog.V(5).Infof("Skipping node address update for node %q since cloud provider did not return any", node.Name)
258236
return
@@ -363,9 +341,19 @@ func (cnc *CloudNodeController) initializeNode(ctx context.Context, node *v1.Nod
363341
return
364342
}
365343

366-
// TODO: getNodeModifiersFromCloudProvider and updateNodeAddress both call cloud api to get instanceMetadata,
367-
// get instanceMetadata and pass it to getNodeModifiersFromCloudProvider and updateNodeAddress which reduces api calls.
368-
nodeModifiers, err := cnc.getNodeModifiersFromCloudProvider(ctx, curNode)
344+
providerID, err := cnc.getProviderID(ctx, curNode)
345+
if err != nil {
346+
utilruntime.HandleError(fmt.Errorf("failed to get provider ID for node %s at cloudprovider: %v", node.Name, err))
347+
return
348+
}
349+
350+
instanceMetadata, err := cnc.getInstanceMetadata(ctx, providerID, curNode)
351+
if err != nil {
352+
utilruntime.HandleError(fmt.Errorf("failed to get instance metadata for node %s: %v", node.Name, err))
353+
return
354+
}
355+
356+
nodeModifiers, err := cnc.getNodeModifiersFromCloudProvider(ctx, providerID, curNode, instanceMetadata)
369357
if err != nil {
370358
utilruntime.HandleError(fmt.Errorf("failed to initialize node %s at cloudprovider: %v", node.Name, err))
371359
return
@@ -392,7 +380,7 @@ func (cnc *CloudNodeController) initializeNode(ctx context.Context, node *v1.Nod
392380

393381
// After adding, call UpdateNodeAddress to set the CloudProvider provided IPAddresses
394382
// So that users do not see any significant delay in IP addresses being filled into the node
395-
cnc.updateNodeAddress(ctx, curNode)
383+
cnc.updateNodeAddress(ctx, curNode, instanceMetadata)
396384

397385
klog.Infof("Successfully initialized node %s with cloud provider", node.Name)
398386
return nil
@@ -407,86 +395,16 @@ func (cnc *CloudNodeController) initializeNode(ctx context.Context, node *v1.Nod
407395
// a node object with provider-specific information.
408396
// All of the returned functions are idempotent, because they are used in a retry-if-conflict
409397
// loop, meaning they could get called multiple times.
410-
func (cnc *CloudNodeController) getNodeModifiersFromCloudProvider(ctx context.Context, node *v1.Node) ([]nodeModifier, error) {
411-
var (
412-
nodeModifiers []nodeModifier
413-
providerID string
414-
err error
415-
)
416-
417-
if node.Spec.ProviderID == "" {
418-
providerID, err = cloudprovider.GetInstanceProviderID(ctx, cnc.cloud, types.NodeName(node.Name))
419-
if err == nil {
420-
nodeModifiers = append(nodeModifiers, func(n *v1.Node) {
421-
if n.Spec.ProviderID == "" {
422-
n.Spec.ProviderID = providerID
423-
}
424-
})
425-
} else if err == cloudprovider.NotImplemented {
426-
// if the cloud provider being used does not support provider IDs,
427-
// we can safely continue since we will attempt to set node
428-
// addresses given the node name in getNodeAddressesByProviderIDOrName
429-
klog.Warningf("cloud provider does not set node provider ID, using node name to discover node %s", node.Name)
430-
} else {
431-
// if the cloud provider being used supports provider IDs, we want
432-
// to propagate the error so that we re-try in the future; if we
433-
// do not, the taint will be removed, and this will not be retried
434-
return nil, err
435-
}
436-
} else {
437-
providerID = node.Spec.ProviderID
438-
}
439-
440-
instanceMetadataGetter := func(providerID string, nodeName string, node *v1.Node) (*cloudprovider.InstanceMetadata, error) {
441-
if instancesV2, ok := cnc.cloud.InstancesV2(); instancesV2 != nil && ok {
442-
return instancesV2.InstanceMetadata(ctx, node)
443-
}
444-
445-
// If InstancesV2 not implement, use Instances.
446-
instances, ok := cnc.cloud.Instances()
447-
if !ok {
448-
return nil, fmt.Errorf("failed to get instances from cloud provider")
449-
}
450-
451-
nodeAddresses, err := getNodeAddressesByProviderIDOrName(ctx, instances, providerID, nodeName)
452-
if err != nil {
453-
return nil, err
454-
}
455-
456-
instanceType, err := getInstanceTypeByProviderIDOrName(ctx, instances, providerID, nodeName)
457-
if err != nil {
458-
return nil, err
459-
}
460-
461-
instanceMetadata := &cloudprovider.InstanceMetadata{
462-
InstanceType: instanceType,
463-
NodeAddresses: nodeAddresses,
464-
}
465-
466-
zones, ok := cnc.cloud.Zones()
467-
if !ok {
468-
return instanceMetadata, nil
469-
}
470-
471-
zone, err := getZoneByProviderIDOrName(ctx, zones, providerID, node.Name)
472-
if err != nil {
473-
return nil, fmt.Errorf("failed to get zone from cloud provider: %v", err)
474-
}
475-
476-
if zone.FailureDomain != "" {
477-
instanceMetadata.Zone = zone.FailureDomain
478-
}
479-
480-
if zone.Region != "" {
481-
instanceMetadata.Region = zone.Region
482-
}
398+
func (cnc *CloudNodeController) getNodeModifiersFromCloudProvider(
399+
ctx context.Context,
400+
providerID string,
401+
node *v1.Node,
402+
instanceMeta *cloudprovider.InstanceMetadata,
403+
) ([]nodeModifier, error) {
483404

484-
return instanceMetadata, nil
485-
}
486-
487-
instanceMeta, err := instanceMetadataGetter(providerID, node.Name, node)
488-
if err != nil {
489-
return nil, err
405+
var nodeModifiers []nodeModifier
406+
if node.Spec.ProviderID == "" && providerID != "" {
407+
nodeModifiers = append(nodeModifiers, func(n *v1.Node) { n.Spec.ProviderID = providerID })
490408
}
491409

492410
// If user provided an IP address, ensure that IP address is found
@@ -533,6 +451,94 @@ func (cnc *CloudNodeController) getNodeModifiersFromCloudProvider(ctx context.Co
533451
return nodeModifiers, nil
534452
}
535453

454+
func (cnc *CloudNodeController) getProviderID(ctx context.Context, node *v1.Node) (string, error) {
455+
if node.Spec.ProviderID != "" {
456+
return node.Spec.ProviderID, nil
457+
}
458+
459+
providerID, err := cloudprovider.GetInstanceProviderID(ctx, cnc.cloud, types.NodeName(node.Name))
460+
if err == cloudprovider.NotImplemented {
461+
// if the cloud provider being used does not support provider IDs,
462+
// we can safely continue since we will attempt to set node
463+
// addresses given the node name in getNodeAddressesByProviderIDOrName
464+
klog.Warningf("cloud provider does not set node provider ID, using node name to discover node %s", node.Name)
465+
return "", nil
466+
}
467+
468+
// if the cloud provider being used supports provider IDs, we want
469+
// to propagate the error so that we re-try in the future; if we
470+
// do not, the taint will be removed, and this will not be retried
471+
return providerID, err
472+
}
473+
474+
// getInstanceMetadata get instance type and nodeAddresses, use Instances if InstancesV2 is off.
475+
func (cnc *CloudNodeController) getInstanceMetadata(ctx context.Context, providerID string, node *v1.Node) (*cloudprovider.InstanceMetadata, error) {
476+
if instancesV2, ok := cnc.cloud.InstancesV2(); instancesV2 != nil && ok {
477+
return instancesV2.InstanceMetadata(ctx, node)
478+
}
479+
480+
// If InstancesV2 not implement, use Instances.
481+
instances, ok := cnc.cloud.Instances()
482+
if !ok {
483+
return nil, fmt.Errorf("failed to get instances from cloud provider")
484+
}
485+
nodeAddresses, err := getNodeAddressesByProviderIDOrName(ctx, instances, providerID, node.Name)
486+
if err != nil {
487+
return nil, err
488+
}
489+
instanceType, err := getInstanceTypeByProviderIDOrName(ctx, instances, providerID, node.Name)
490+
if err != nil {
491+
return nil, err
492+
}
493+
494+
instanceMetadata := &cloudprovider.InstanceMetadata{
495+
InstanceType: instanceType,
496+
NodeAddresses: nodeAddresses,
497+
}
498+
499+
zones, ok := cnc.cloud.Zones()
500+
if !ok {
501+
return instanceMetadata, nil
502+
}
503+
504+
zone, err := getZoneByProviderIDOrName(ctx, zones, providerID, node.Name)
505+
if err != nil {
506+
return nil, fmt.Errorf("failed to get zone from cloud provider: %v", err)
507+
}
508+
509+
if zone.FailureDomain != "" {
510+
instanceMetadata.Zone = zone.FailureDomain
511+
}
512+
513+
if zone.Region != "" {
514+
instanceMetadata.Region = zone.Region
515+
}
516+
517+
return instanceMetadata, nil
518+
}
519+
520+
// getInstanceAddresses returns InstanceMetadata.NodeAddresses. If InstancesV2 not supported, it won't get instanceType
521+
// which avoid an api call compared with getInstanceMetadata.
522+
func (cnc *CloudNodeController) getInstanceNodeAddresses(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) {
523+
if instancesV2, ok := cnc.cloud.InstancesV2(); instancesV2 != nil && ok {
524+
return instancesV2.InstanceMetadata(ctx, node)
525+
}
526+
527+
// If InstancesV2 not implement, use Instances.
528+
instances, ok := cnc.cloud.Instances()
529+
if !ok {
530+
return nil, fmt.Errorf("failed to get instances from cloud provider")
531+
}
532+
nodeAddresses, err := getNodeAddressesByProviderIDOrName(ctx, instances, node.Spec.ProviderID, node.Name)
533+
if err != nil {
534+
return nil, err
535+
}
536+
537+
return &cloudprovider.InstanceMetadata{
538+
NodeAddresses: nodeAddresses,
539+
}, nil
540+
}
541+
536542
func getCloudTaint(taints []v1.Taint) *v1.Taint {
537543
for _, taint := range taints {
538544
if taint.Key == cloudproviderapi.TaintExternalCloudProvider {

staging/src/k8s.io/cloud-provider/controllers/node/node_controller_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,7 +1776,11 @@ func TestNodeAddressesNotUpdateV2(t *testing.T) {
17761776
cloud: fakeCloud,
17771777
}
17781778

1779-
cloudNodeController.updateNodeAddress(context.TODO(), existingNode)
1779+
instanceMeta, err := cloudNodeController.getInstanceNodeAddresses(context.TODO(), existingNode)
1780+
if err != nil {
1781+
t.Errorf("get instance metadata with error %v", err)
1782+
}
1783+
cloudNodeController.updateNodeAddress(context.TODO(), existingNode, instanceMeta)
17801784

17811785
updatedNode, err := clientset.CoreV1().Nodes().Get(context.TODO(), existingNode.Name, metav1.GetOptions{})
17821786
if err != nil {
@@ -1848,7 +1852,11 @@ func TestNodeAddressesNotUpdate(t *testing.T) {
18481852
cloud: fakeCloud,
18491853
}
18501854

1851-
cloudNodeController.updateNodeAddress(context.TODO(), existingNode)
1855+
instanceMeta, err := cloudNodeController.getInstanceNodeAddresses(context.TODO(), existingNode)
1856+
if err != nil {
1857+
t.Errorf("get instance metadata with error %v", err)
1858+
}
1859+
cloudNodeController.updateNodeAddress(context.TODO(), existingNode, instanceMeta)
18521860

18531861
updatedNode, err := clientset.CoreV1().Nodes().Get(context.TODO(), existingNode.Name, metav1.GetOptions{})
18541862
if err != nil {

0 commit comments

Comments
 (0)