123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- /*
- Copyright 2017 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package sync
- import (
- "context"
- "fmt"
- "net"
- "reflect"
- "testing"
- "time"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/klog"
- "k8s.io/kubernetes/pkg/controller/nodeipam/ipam/cidrset"
- "k8s.io/kubernetes/pkg/controller/nodeipam/ipam/test"
- "k8s.io/api/core/v1"
- )
- var (
- _, clusterCIDRRange, _ = net.ParseCIDR("10.1.0.0/16")
- )
- type fakeEvent struct {
- nodeName string
- reason string
- }
- type fakeAPIs struct {
- aliasRange *net.IPNet
- aliasErr error
- addAliasErr error
- nodeRet *v1.Node
- nodeErr error
- updateNodeErr error
- resyncTimeout time.Duration
- reportChan chan struct{}
- updateNodeNetworkUnavailableErr error
- calls []string
- events []fakeEvent
- results []error
- }
- func (f *fakeAPIs) Alias(ctx context.Context, nodeName string) (*net.IPNet, error) {
- f.calls = append(f.calls, fmt.Sprintf("alias %v", nodeName))
- return f.aliasRange, f.aliasErr
- }
- func (f *fakeAPIs) AddAlias(ctx context.Context, nodeName string, cidrRange *net.IPNet) error {
- f.calls = append(f.calls, fmt.Sprintf("addAlias %v %v", nodeName, cidrRange))
- return f.addAliasErr
- }
- func (f *fakeAPIs) Node(ctx context.Context, name string) (*v1.Node, error) {
- f.calls = append(f.calls, fmt.Sprintf("node %v", name))
- return f.nodeRet, f.nodeErr
- }
- func (f *fakeAPIs) UpdateNodePodCIDR(ctx context.Context, node *v1.Node, cidrRange *net.IPNet) error {
- f.calls = append(f.calls, fmt.Sprintf("updateNode %v", node))
- return f.updateNodeErr
- }
- func (f *fakeAPIs) UpdateNodeNetworkUnavailable(nodeName string, unavailable bool) error {
- f.calls = append(f.calls, fmt.Sprintf("updateNodeNetworkUnavailable %v %v", nodeName, unavailable))
- return f.updateNodeNetworkUnavailableErr
- }
- func (f *fakeAPIs) EmitNodeWarningEvent(nodeName, reason, fmtStr string, args ...interface{}) {
- f.events = append(f.events, fakeEvent{nodeName, reason})
- }
- func (f *fakeAPIs) ReportResult(err error) {
- klog.V(2).Infof("ReportResult %v", err)
- f.results = append(f.results, err)
- if f.reportChan != nil {
- f.reportChan <- struct{}{}
- }
- }
- func (f *fakeAPIs) ResyncTimeout() time.Duration {
- if f.resyncTimeout == 0 {
- return time.Second * 10000
- }
- return f.resyncTimeout
- }
- func (f *fakeAPIs) dumpTrace() {
- for i, x := range f.calls {
- klog.Infof("trace %v: %v", i, x)
- }
- }
- var nodeWithoutCIDRRange = &v1.Node{
- ObjectMeta: metav1.ObjectMeta{Name: "node1"},
- }
- var nodeWithCIDRRange = &v1.Node{
- ObjectMeta: metav1.ObjectMeta{Name: "node1"},
- Spec: v1.NodeSpec{PodCIDR: "10.1.1.0/24"},
- }
- func TestNodeSyncUpdate(t *testing.T) {
- t.Parallel()
- for _, tc := range []struct {
- desc string
- mode NodeSyncMode
- node *v1.Node
- fake fakeAPIs
- events []fakeEvent
- wantError bool
- }{
- {
- desc: "validate range ==",
- mode: SyncFromCloud,
- node: nodeWithCIDRRange,
- fake: fakeAPIs{
- aliasRange: test.MustParseCIDR(nodeWithCIDRRange.Spec.PodCIDR),
- },
- },
- {
- desc: "validate range !=",
- mode: SyncFromCloud,
- node: nodeWithCIDRRange,
- fake: fakeAPIs{aliasRange: test.MustParseCIDR("192.168.0.0/24")},
- events: []fakeEvent{{"node1", "CloudCIDRAllocatorMismatch"}},
- },
- {
- desc: "update alias from node",
- mode: SyncFromCloud,
- node: nodeWithCIDRRange,
- events: []fakeEvent{{"node1", "CloudCIDRAllocatorInvalidMode"}},
- wantError: true,
- },
- {
- desc: "update alias from node",
- mode: SyncFromCluster,
- node: nodeWithCIDRRange,
- // XXX/bowei -- validation
- },
- {
- desc: "update node from alias",
- mode: SyncFromCloud,
- node: nodeWithoutCIDRRange,
- fake: fakeAPIs{aliasRange: test.MustParseCIDR("10.1.2.3/16")},
- // XXX/bowei -- validation
- },
- {
- desc: "update node from alias",
- mode: SyncFromCluster,
- node: nodeWithoutCIDRRange,
- fake: fakeAPIs{aliasRange: test.MustParseCIDR("10.1.2.3/16")},
- events: []fakeEvent{{"node1", "CloudCIDRAllocatorInvalidMode"}},
- wantError: true,
- },
- {
- desc: "allocate range",
- mode: SyncFromCloud,
- node: nodeWithoutCIDRRange,
- events: []fakeEvent{{"node1", "CloudCIDRAllocatorInvalidMode"}},
- wantError: true,
- },
- {
- desc: "allocate range",
- mode: SyncFromCluster,
- node: nodeWithoutCIDRRange,
- },
- {
- desc: "update with node==nil",
- mode: SyncFromCluster,
- node: nil,
- fake: fakeAPIs{
- nodeRet: nodeWithCIDRRange,
- },
- wantError: false,
- },
- } {
- cidr, _ := cidrset.NewCIDRSet(clusterCIDRRange, 24)
- sync := New(&tc.fake, &tc.fake, &tc.fake, tc.mode, "node1", cidr)
- doneChan := make(chan struct{})
- // Do a single step of the loop.
- go sync.Loop(doneChan)
- sync.Update(tc.node)
- close(sync.opChan)
- <-doneChan
- tc.fake.dumpTrace()
- if !reflect.DeepEqual(tc.fake.events, tc.events) {
- t.Errorf("%v, %v; fake.events = %#v, want %#v", tc.desc, tc.mode, tc.fake.events, tc.events)
- }
- var hasError bool
- for _, r := range tc.fake.results {
- hasError = hasError || (r != nil)
- }
- if hasError != tc.wantError {
- t.Errorf("%v, %v; hasError = %t, errors = %v, want %t",
- tc.desc, tc.mode, hasError, tc.fake.events, tc.wantError)
- }
- }
- }
- func TestNodeSyncResync(t *testing.T) {
- fake := &fakeAPIs{
- nodeRet: nodeWithCIDRRange,
- resyncTimeout: time.Millisecond,
- reportChan: make(chan struct{}),
- }
- cidr, _ := cidrset.NewCIDRSet(clusterCIDRRange, 24)
- sync := New(fake, fake, fake, SyncFromCluster, "node1", cidr)
- doneChan := make(chan struct{})
- go sync.Loop(doneChan)
- <-fake.reportChan
- close(sync.opChan)
- // Unblock loop().
- go func() {
- <-fake.reportChan
- }()
- <-doneChan
- fake.dumpTrace()
- }
- func TestNodeSyncDelete(t *testing.T) {
- t.Parallel()
- for _, tc := range []struct {
- desc string
- mode NodeSyncMode
- node *v1.Node
- fake fakeAPIs
- }{
- {
- desc: "delete",
- mode: SyncFromCluster,
- node: nodeWithCIDRRange,
- },
- {
- desc: "delete without CIDR range",
- mode: SyncFromCluster,
- node: nodeWithoutCIDRRange,
- },
- {
- desc: "delete with invalid CIDR range",
- mode: SyncFromCluster,
- node: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{Name: "node1"},
- Spec: v1.NodeSpec{PodCIDR: "invalid"},
- },
- },
- } {
- cidr, _ := cidrset.NewCIDRSet(clusterCIDRRange, 24)
- sync := New(&tc.fake, &tc.fake, &tc.fake, tc.mode, "node1", cidr)
- doneChan := make(chan struct{})
- // Do a single step of the loop.
- go sync.Loop(doneChan)
- sync.Delete(tc.node)
- <-doneChan
- tc.fake.dumpTrace()
- /*
- if !reflect.DeepEqual(tc.fake.events, tc.events) {
- t.Errorf("%v, %v; fake.events = %#v, want %#v", tc.desc, tc.mode, tc.fake.events, tc.events)
- }
- var hasError bool
- for _, r := range tc.fake.results {
- hasError = hasError || (r != nil)
- }
- if hasError != tc.wantError {
- t.Errorf("%v, %v; hasError = %t, errors = %v, want %t",
- tc.desc, tc.mode, hasError, tc.fake.events, tc.wantError)
- }
- */
- }
- }
|