diff --git a/staging/src/k8s.io/cloud-provider/controllers/node/node_controller.go b/staging/src/k8s.io/cloud-provider/controllers/node/node_controller.go index dced9aab29a..c14fe43b8f8 100644 --- a/staging/src/k8s.io/cloud-provider/controllers/node/node_controller.go +++ b/staging/src/k8s.io/cloud-provider/controllers/node/node_controller.go @@ -424,6 +424,11 @@ func (cnc *CloudNodeController) syncNode(ctx context.Context, nodeName string) e if err != nil { return fmt.Errorf("failed to get instance metadata for node %s: %v", nodeName, err) } + if instanceMetadata == nil { + // do nothing when external cloud providers provide nil instanceMetadata + klog.Infof("Skip sync node %s because cloud provided nil metadata", nodeName) + return nil + } nodeModifiers, err := cnc.getNodeModifiersFromCloudProvider(ctx, providerID, copyNode, instanceMetadata) if err != nil { diff --git a/staging/src/k8s.io/cloud-provider/controllers/node/node_controller_test.go b/staging/src/k8s.io/cloud-provider/controllers/node/node_controller_test.go index a93fa5bcce0..bfb9952c0d8 100644 --- a/staging/src/k8s.io/cloud-provider/controllers/node/node_controller_test.go +++ b/staging/src/k8s.io/cloud-provider/controllers/node/node_controller_test.go @@ -471,6 +471,96 @@ func Test_syncNode(t *testing.T) { }, }, }, + { + name: "nil instanceMetadata provided by InstanceV2", + fakeCloud: &fakecloud.Cloud{ + EnableInstancesV2: true, + Err: nil, + OverrideInstanceMetadata: func(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) { + return nil, nil + }, + }, + existingNode: &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node0", + CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), + Annotations: map[string]string{ + cloudproviderapi.AnnotationAlphaProvidedIPAddr: "10.0.0.1", + }, + }, + Spec: v1.NodeSpec{ + Taints: []v1.Taint{ + { + Key: "ImproveCoverageTaint", + Value: "true", + Effect: v1.TaintEffectNoSchedule, + }, + { + Key: cloudproviderapi.TaintExternalCloudProvider, + Value: "true", + Effect: v1.TaintEffectNoSchedule, + }, + }, + ProviderID: "node0.aws.12345", + }, + Status: v1.NodeStatus{ + Conditions: []v1.NodeCondition{ + { + Type: v1.NodeReady, + Status: v1.ConditionUnknown, + LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), + LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), + }, + }, + Addresses: []v1.NodeAddress{ + { + Type: v1.NodeHostName, + Address: "node0.cloud.internal", + }, + }, + }, + }, + updatedNode: &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node0", + CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), + Annotations: map[string]string{ + cloudproviderapi.AnnotationAlphaProvidedIPAddr: "10.0.0.1", + }, + }, + Spec: v1.NodeSpec{ + Taints: []v1.Taint{ + { + Key: "ImproveCoverageTaint", + Value: "true", + Effect: v1.TaintEffectNoSchedule, + }, + { + Key: cloudproviderapi.TaintExternalCloudProvider, + Value: "true", + Effect: v1.TaintEffectNoSchedule, + }, + }, + ProviderID: "node0.aws.12345", + }, + Status: v1.NodeStatus{ + Conditions: []v1.NodeCondition{ + { + Type: v1.NodeReady, + Status: v1.ConditionUnknown, + LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), + LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC), + }, + }, + Addresses: []v1.NodeAddress{ + { + Type: v1.NodeHostName, + Address: "node0.cloud.internal", + }, + }, + }, + }, + }, { name: "provided node IP address", fakeCloud: &fakecloud.Cloud{ diff --git a/staging/src/k8s.io/cloud-provider/fake/fake.go b/staging/src/k8s.io/cloud-provider/fake/fake.go index 48f2684b7ca..6074591e8b8 100644 --- a/staging/src/k8s.io/cloud-provider/fake/fake.go +++ b/staging/src/k8s.io/cloud-provider/fake/fake.go @@ -53,6 +53,7 @@ var _ cloudprovider.Routes = (*Cloud)(nil) var _ cloudprovider.Zones = (*Cloud)(nil) var _ cloudprovider.PVLabeler = (*Cloud)(nil) var _ cloudprovider.Clusters = (*Cloud)(nil) +var _ cloudprovider.InstancesV2 = (*Cloud)(nil) // Cloud is a test-double implementation of Interface, LoadBalancer, Instances, and Routes. It is useful for testing. type Cloud struct { @@ -93,6 +94,8 @@ type Cloud struct { cloudprovider.Zone VolumeLabelMap map[string]map[string]string + OverrideInstanceMetadata func(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) + RequestDelay time.Duration } @@ -328,6 +331,9 @@ func (f *Cloud) InstanceShutdown(ctx context.Context, node *v1.Node) (bool, erro // InstanceMetadata returns metadata of the specified instance. func (f *Cloud) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) { + if f.OverrideInstanceMetadata != nil { + return f.OverrideInstanceMetadata(ctx, node) + } f.addCall("instance-metadata-by-provider-id") f.addressesMux.Lock() defer f.addressesMux.Unlock()