Merge pull request #51087 from oracle/for/upstream/master/ccm-instance-exists

Automatic merge from submit-queue (batch tested with PRs 51174, 51363, 51087, 51382, 51388)

Add InstanceExistsByProviderID to cloud provider interface for CCM

**What this PR does / why we need it**:

Currently, [`MonitorNode()`](02b520f0a4/pkg/controller/cloud/nodecontroller.go (L240)) in the node controller checks with the CCM if a node still exists by calling `ExternalID(nodeName)`. `ExternalID` is supposed to return the provider id of a node which is not supported on every cloud. This means that any clouds who cannot infer the provider id by the node name from a remote location will never remove nodes that no longer exist. 


**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #50985

**Special notes for your reviewer**:

We'll want to create a subsequent issue to track the implementation of these two new methods in the cloud providers.

**Release note**:

```release-note
Adds `InstanceExists` and `InstanceExistsByProviderID` to cloud provider interface for the cloud controller manager
```

/cc @wlan0 @thockin @andrewsykim @luxas @jhorwit2

/area cloudprovider
/sig cluster-lifecycle
This commit is contained in:
Kubernetes Submit Queue
2017-08-26 06:43:30 -07:00
committed by GitHub
13 changed files with 244 additions and 27 deletions

View File

@@ -17,6 +17,8 @@ limitations under the License.
package cloud
import (
"errors"
"reflect"
"testing"
"time"
@@ -39,6 +41,119 @@ import (
"k8s.io/kubernetes/plugin/pkg/scheduler/algorithm"
)
func TestEnsureNodeExistsByProviderIDOrNodeName(t *testing.T) {
testCases := []struct {
testName string
node *v1.Node
expectedCalls []string
existsByNodeName bool
existsByProviderID bool
nodeNameErr error
providerIDErr error
}{
{
testName: "node exists by provider id",
existsByProviderID: true,
providerIDErr: nil,
existsByNodeName: false,
nodeNameErr: errors.New("unimplemented"),
expectedCalls: []string{"instance-exists-by-provider-id"},
node: &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node0",
},
Spec: v1.NodeSpec{
ProviderID: "node0",
},
},
},
{
testName: "does not exist by provider id",
existsByProviderID: false,
providerIDErr: nil,
existsByNodeName: false,
nodeNameErr: errors.New("unimplemented"),
expectedCalls: []string{"instance-exists-by-provider-id"},
node: &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node0",
},
Spec: v1.NodeSpec{
ProviderID: "node0",
},
},
},
{
testName: "node exists by node name",
existsByProviderID: false,
providerIDErr: errors.New("unimplemented"),
existsByNodeName: true,
nodeNameErr: nil,
expectedCalls: []string{"instance-exists-by-provider-id", "external-id"},
node: &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node0",
},
Spec: v1.NodeSpec{
ProviderID: "node0",
},
},
},
{
testName: "does not exist by node name",
existsByProviderID: false,
providerIDErr: errors.New("unimplemented"),
existsByNodeName: false,
nodeNameErr: cloudprovider.InstanceNotFound,
expectedCalls: []string{"instance-exists-by-provider-id", "external-id"},
node: &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node0",
},
Spec: v1.NodeSpec{
ProviderID: "node0",
},
},
},
}
for _, tc := range testCases {
t.Run(tc.testName, func(t *testing.T) {
fc := &fakecloud.FakeCloud{
Exists: tc.existsByNodeName,
ExistsByProviderID: tc.existsByProviderID,
Err: tc.nodeNameErr,
ErrByProviderID: tc.providerIDErr,
}
instances, _ := fc.Instances()
exists, err := ensureNodeExistsByProviderIDOrExternalID(instances, tc.node)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(fc.Calls, tc.expectedCalls) {
t.Errorf("expected cloud provider methods `%v` to be called but `%v` was called ", tc.expectedCalls, fc.Calls)
}
if tc.existsByProviderID && tc.existsByProviderID != exists {
t.Errorf("expected exist by provider id to be `%t` but got `%t`", tc.existsByProviderID, exists)
}
if tc.existsByNodeName && tc.existsByNodeName != exists {
t.Errorf("expected exist by node name to be `%t` but got `%t`", tc.existsByNodeName, exists)
}
if !tc.existsByNodeName && !tc.existsByProviderID && exists {
t.Error("node is not supposed to exist")
}
})
}
}
// This test checks that the node is deleted when kubelet stops reporting
// and cloud provider says node is gone
func TestNodeDeleted(t *testing.T) {
@@ -105,9 +220,12 @@ func TestNodeDeleted(t *testing.T) {
eventBroadcaster := record.NewBroadcaster()
cloudNodeController := &CloudNodeController{
kubeClient: fnh,
nodeInformer: factory.Core().V1().Nodes(),
cloud: &fakecloud.FakeCloud{Err: cloudprovider.InstanceNotFound},
kubeClient: fnh,
nodeInformer: factory.Core().V1().Nodes(),
cloud: &fakecloud.FakeCloud{
ExistsByProviderID: false,
Err: nil,
},
nodeMonitorPeriod: 1 * time.Second,
recorder: eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "cloud-controller-manager"}),
nodeStatusUpdateFrequency: 1 * time.Second,
@@ -520,7 +638,8 @@ func TestNodeAddresses(t *testing.T) {
FailureDomain: "us-west-1a",
Region: "us-west",
},
Err: nil,
ExistsByProviderID: true,
Err: nil,
}
eventBroadcaster := record.NewBroadcaster()
@@ -639,7 +758,8 @@ func TestNodeProvidedIPAddresses(t *testing.T) {
FailureDomain: "us-west-1a",
Region: "us-west",
},
Err: nil,
ExistsByProviderID: true,
Err: nil,
}
eventBroadcaster := record.NewBroadcaster()