Skip to content

Commit f7e3bcd

Browse files
authored
Merge pull request kubernetes#93582 from andrewsykim/instance-v2-fixes
Mark cloud provider InstanceV2 as experimental and remove provider ID references
2 parents 9dec51e + 46d522d commit f7e3bcd

File tree

14 files changed

+318
-571
lines changed

14 files changed

+318
-571
lines changed

staging/src/k8s.io/cloud-provider/cloud.go

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@ type Interface interface {
4949
LoadBalancer() (LoadBalancer, bool)
5050
// Instances returns an instances interface. Also returns true if the interface is supported, false otherwise.
5151
Instances() (Instances, bool)
52-
// InstancesV2 is an implementation for instances only used by cloud node-controller now.
52+
// InstancesV2 is an implementation for instances and should only be implemented by external cloud providers.
53+
// Implementing InstancesV2 is behaviorally identical to Instances but is optimized to significantly reduce
54+
// API calls to the cloud provider when registering and syncing nodes.
5355
// Also returns true if the interface is supported, false otherwise.
56+
// WARNING: InstancesV2 is an experimental interface and is subject to change in v1.20.
5457
InstancesV2() (InstancesV2, bool)
5558
// Zones returns a zones interface. Also returns true if the interface is supported, false otherwise.
5659
Zones() (Zones, bool)
@@ -189,15 +192,20 @@ type Instances interface {
189192
InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error)
190193
}
191194

192-
// InstancesV2 is an abstract, pluggable interface for sets of instances.
193-
// Unlike Instances, it is only used by cloud node-controller now.
195+
// InstancesV2 is an abstract, pluggable interface for cloud provider instances.
196+
// Unlike the Instances interface, it is designed for external cloud providers and should only be used by them.
197+
// WARNING: InstancesV2 is an experimental interface and is subject to change in v1.20.
194198
type InstancesV2 interface {
195-
// InstanceExistsByProviderID returns true if the instance for the given provider exists.
196-
InstanceExistsByProviderID(ctx context.Context, providerID string) (bool, error)
197-
// InstanceShutdownByProviderID returns true if the instance is shutdown in cloudprovider.
198-
InstanceShutdownByProviderID(ctx context.Context, providerID string) (bool, error)
199-
// InstanceMetadataByProviderID returns the instance's metadata.
200-
InstanceMetadataByProviderID(ctx context.Context, providerID string) (*InstanceMetadata, error)
199+
// InstanceExists returns true if the instance for the given node exists according to the cloud provider.
200+
// Use the node.name or node.spec.providerID field to find the node in the cloud provider.
201+
InstanceExists(ctx context.Context, node *v1.Node) (bool, error)
202+
// InstanceShutdown returns true if the instance is shutdown according to the cloud provider.
203+
// Use the node.name or node.spec.providerID field to find the node in the cloud provider.
204+
InstanceShutdown(ctx context.Context, node *v1.Node) (bool, error)
205+
// InstanceMetadata returns the instance's metadata. The values returned in InstanceMetadata are
206+
// translated into specific fields in the Node object on registration.
207+
// Use the node.name or node.spec.providerID field to find the node in the cloud provider.
208+
InstanceMetadata(ctx context.Context, node *v1.Node) (*InstanceMetadata, error)
201209
}
202210

203211
// Route is a representation of an advanced routing rule.
@@ -265,12 +273,25 @@ type PVLabeler interface {
265273
GetLabelsForVolume(ctx context.Context, pv *v1.PersistentVolume) (map[string]string, error)
266274
}
267275

268-
// InstanceMetadata contains metadata about the specific instance.
276+
// InstanceMetadata contains metadata about a specific instance.
277+
// Values returned in InstanceMetadata are translated into specific fields in Node.
269278
type InstanceMetadata struct {
270-
// ProviderID is provider's id that instance belongs to.
279+
// ProviderID is a unique ID used to idenfitify an instance on the cloud provider.
280+
// The ProviderID set here will be set on the node's spec.providerID field.
281+
// The provider ID format can be set by the cloud provider but providers should
282+
// ensure the format does not change in any incompatible way.
283+
//
284+
// The provider ID format used by existing cloud provider has been:
285+
// <provider-name>://<instance-id>
286+
// Existing providers setting this field should preserve the existing format
287+
// currently being set in node.spec.providerID.
271288
ProviderID string
272-
// Type is instance's type.
273-
Type string
289+
// InstanceType is the instance's type.
290+
// The InstanceType set here will be set using the following labels on the node object:
291+
// * node.kubernetes.io/instance-type=<instance-type>
292+
// * beta.kubernetes.io/instance-type=<instance-type> (DEPRECATED)
293+
InstanceType string
274294
// NodeAddress contains information for the instance's address.
295+
// The node addresses returned here will be set on the node's status.addresses field.
275296
NodeAddresses []v1.NodeAddress
276297
}

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

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ func NewCloudNodeController(
108108
klog.Infof("Sending events to api server.")
109109
eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: kubeClient.CoreV1().Events("")})
110110

111-
if _, ok := cloud.Instances(); !ok {
111+
_, instancesSupported := cloud.Instances()
112+
_, instancesV2Supported := cloud.InstancesV2()
113+
if !instancesSupported && !instancesV2Supported {
112114
return nil, errors.New("cloud provider does not support instances")
113115
}
114116

@@ -225,7 +227,7 @@ func (cnc *CloudNodeController) updateNodeAddress(ctx context.Context, node *v1.
225227

226228
instanceMetadataGetter := func(providerID string, node *v1.Node) (*cloudprovider.InstanceMetadata, error) {
227229
if instancesV2, ok := cnc.cloud.InstancesV2(); instancesV2 != nil && ok {
228-
return instancesV2.InstanceMetadataByProviderID(ctx, providerID)
230+
return instancesV2.InstanceMetadata(ctx, node)
229231
}
230232

231233
// If InstancesV2 not implement, use Instances.
@@ -435,9 +437,9 @@ func (cnc *CloudNodeController) getNodeModifiersFromCloudProvider(ctx context.Co
435437
providerID = node.Spec.ProviderID
436438
}
437439

438-
instanceMetadataGetter := func(providerID string, nodeName string) (*cloudprovider.InstanceMetadata, error) {
440+
instanceMetadataGetter := func(providerID string, nodeName string, node *v1.Node) (*cloudprovider.InstanceMetadata, error) {
439441
if instancesV2, ok := cnc.cloud.InstancesV2(); instancesV2 != nil && ok {
440-
return instancesV2.InstanceMetadataByProviderID(ctx, providerID)
442+
return instancesV2.InstanceMetadata(ctx, node)
441443
}
442444

443445
// If InstancesV2 not implement, use Instances.
@@ -454,12 +456,12 @@ func (cnc *CloudNodeController) getNodeModifiersFromCloudProvider(ctx context.Co
454456
return nil, err
455457
}
456458
return &cloudprovider.InstanceMetadata{
457-
Type: instanceType,
459+
InstanceType: instanceType,
458460
NodeAddresses: nodeAddresses,
459461
}, nil
460462
}
461463

462-
instanceMeta, err := instanceMetadataGetter(providerID, node.Name)
464+
instanceMeta, err := instanceMetadataGetter(providerID, node.Name, node)
463465
if err != nil {
464466
return nil, err
465467
}
@@ -470,15 +472,15 @@ func (cnc *CloudNodeController) getNodeModifiersFromCloudProvider(ctx context.Co
470472
return nil, errors.New("failed to find kubelet node IP from cloud provider")
471473
}
472474

473-
if instanceMeta.Type != "" {
474-
klog.V(2).Infof("Adding node label from cloud provider: %s=%s", v1.LabelInstanceType, instanceMeta.Type)
475-
klog.V(2).Infof("Adding node label from cloud provider: %s=%s", v1.LabelInstanceTypeStable, instanceMeta.Type)
475+
if instanceMeta.InstanceType != "" {
476+
klog.V(2).Infof("Adding node label from cloud provider: %s=%s", v1.LabelInstanceType, instanceMeta.InstanceType)
477+
klog.V(2).Infof("Adding node label from cloud provider: %s=%s", v1.LabelInstanceTypeStable, instanceMeta.InstanceType)
476478
nodeModifiers = append(nodeModifiers, func(n *v1.Node) {
477479
if n.Labels == nil {
478480
n.Labels = map[string]string{}
479481
}
480-
n.Labels[v1.LabelInstanceType] = instanceMeta.Type
481-
n.Labels[v1.LabelInstanceTypeStable] = instanceMeta.Type
482+
n.Labels[v1.LabelInstanceType] = instanceMeta.InstanceType
483+
n.Labels[v1.LabelInstanceTypeStable] = instanceMeta.InstanceType
482484
})
483485
}
484486

staging/src/k8s.io/cloud-provider/controllers/nodelifecycle/node_lifecycle_controller.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ func NewCloudNodeLifecycleController(
8585
return nil, errors.New("no cloud provider provided")
8686
}
8787

88-
if _, ok := cloud.Instances(); !ok {
88+
_, instancesSupported := cloud.Instances()
89+
_, instancesV2Supported := cloud.InstancesV2()
90+
if !instancesSupported && !instancesV2Supported {
8991
return nil, errors.New("cloud provider does not support instances")
9092
}
9193

@@ -118,12 +120,6 @@ func (c *CloudNodeLifecycleController) Run(stopCh <-chan struct{}) {
118120
// or shutdown. If deleted, it deletes the node resource. If shutdown it
119121
// applies a shutdown taint to the node
120122
func (c *CloudNodeLifecycleController) MonitorNodes() {
121-
instances, ok := c.cloud.Instances()
122-
if !ok {
123-
utilruntime.HandleError(fmt.Errorf("failed to get instances from cloud provider"))
124-
return
125-
}
126-
127123
nodes, err := c.nodeLister.List(labels.Everything())
128124
if err != nil {
129125
klog.Errorf("error listing nodes from cache: %s", err)
@@ -148,7 +144,7 @@ func (c *CloudNodeLifecycleController) MonitorNodes() {
148144

149145
// At this point the node has NotReady status, we need to check if the node has been removed
150146
// from the cloud provider. If node cannot be found in cloudprovider, then delete the node
151-
exists, err := ensureNodeExistsByProviderID(context.TODO(), instances, node)
147+
exists, err := ensureNodeExistsByProviderID(context.TODO(), c.cloud, node)
152148
if err != nil {
153149
klog.Errorf("error checking if node %s exists: %v", node.Name, err)
154150
continue
@@ -195,6 +191,10 @@ func (c *CloudNodeLifecycleController) MonitorNodes() {
195191

196192
// shutdownInCloudProvider returns true if the node is shutdown on the cloud provider
197193
func shutdownInCloudProvider(ctx context.Context, cloud cloudprovider.Interface, node *v1.Node) (bool, error) {
194+
if instanceV2, ok := cloud.InstancesV2(); ok {
195+
return instanceV2.InstanceShutdown(ctx, node)
196+
}
197+
198198
instances, ok := cloud.Instances()
199199
if !ok {
200200
return false, errors.New("cloud provider does not support instances")
@@ -210,7 +210,16 @@ func shutdownInCloudProvider(ctx context.Context, cloud cloudprovider.Interface,
210210

211211
// ensureNodeExistsByProviderID checks if the instance exists by the provider id,
212212
// If provider id in spec is empty it calls instanceId with node name to get provider id
213-
func ensureNodeExistsByProviderID(ctx context.Context, instances cloudprovider.Instances, node *v1.Node) (bool, error) {
213+
func ensureNodeExistsByProviderID(ctx context.Context, cloud cloudprovider.Interface, node *v1.Node) (bool, error) {
214+
if instanceV2, ok := cloud.InstancesV2(); ok {
215+
return instanceV2.InstanceExists(ctx, node)
216+
}
217+
218+
instances, ok := cloud.Instances()
219+
if !ok {
220+
return false, errors.New("instances interface not supported in the cloud provider")
221+
}
222+
214223
providerID := node.Spec.ProviderID
215224
if providerID == "" {
216225
var err error

0 commit comments

Comments
 (0)