kubernetes utils_test 源码

  • 2022-09-18
  • 浏览 (27)

kubernetes utils_test 代码

文件路径:/pkg/controller/endpointslice/topologycache/utils_test.go

/*
Copyright 2021 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 topologycache

import (
	"reflect"
	"testing"

	discovery "k8s.io/api/discovery/v1"
	utilpointer "k8s.io/utils/pointer"
)

func Test_redistributeHints(t *testing.T) {
	testCases := []struct {
		name                    string
		slices                  []*discovery.EndpointSlice
		givingZones             map[string]int
		receivingZones          map[string]int
		expectedRedistributions map[string]int
	}{{
		name:                    "empty",
		slices:                  []*discovery.EndpointSlice{},
		givingZones:             map[string]int{},
		receivingZones:          map[string]int{},
		expectedRedistributions: map[string]int{},
	}, {
		name: "single endpoint",
		slices: []*discovery.EndpointSlice{{
			Endpoints: []discovery.Endpoint{{
				Zone:       utilpointer.StringPtr("zone-a"),
				Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-a"}}},
				Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
			}},
		}},
		givingZones:             map[string]int{"zone-a": 1},
		receivingZones:          map[string]int{"zone-b": 1},
		expectedRedistributions: map[string]int{"zone-a": -1, "zone-b": 1},
	}, {
		name: "endpoints from 1 zone redistributed to 2 other zones",
		slices: []*discovery.EndpointSlice{{
			Endpoints: []discovery.Endpoint{{
				Zone:       utilpointer.StringPtr("zone-a"),
				Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-a"}}},
				Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
			}, {
				Zone:       utilpointer.StringPtr("zone-a"),
				Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-a"}}},
				Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
			}, {
				Zone:       utilpointer.StringPtr("zone-a"),
				Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-a"}}},
				Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
			}},
		}},
		givingZones:             map[string]int{"zone-a": 2},
		receivingZones:          map[string]int{"zone-b": 1, "zone-c": 1},
		expectedRedistributions: map[string]int{"zone-a": -2, "zone-b": 1, "zone-c": 1},
	}}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			actualRedistributions := redistributeHints(tc.slices, tc.givingZones, tc.receivingZones)

			if len(actualRedistributions) != len(tc.expectedRedistributions) {
				t.Fatalf("Expected redistributions for %d zones, got %d (%+v)", len(tc.expectedRedistributions), len(actualRedistributions), actualRedistributions)
			}

			for zone, expectedNum := range tc.expectedRedistributions {
				actualNum, _ := actualRedistributions[zone]
				if actualNum != expectedNum {
					t.Errorf("Expected redistribution of %d for zone %s, got %d", expectedNum, zone, actualNum)
				}
			}
		})
	}
}

func Test_getGivingAndReceivingZones(t *testing.T) {
	testCases := []struct {
		name                   string
		allocations            map[string]Allocation
		allocatedHintsByZone   map[string]int
		expectedGivingZones    map[string]int
		expectedReceivingZones map[string]int
	}{{
		name:                   "empty",
		allocations:            map[string]Allocation{},
		allocatedHintsByZone:   map[string]int{},
		expectedGivingZones:    map[string]int{},
		expectedReceivingZones: map[string]int{},
	}, {
		name: "simple allocation with no need for rebalancing",
		allocations: map[string]Allocation{
			"zone-a": {Desired: 1.2},
			"zone-b": {Desired: 1.1},
			"zone-c": {Desired: 1.0},
		},
		allocatedHintsByZone:   map[string]int{"zone-a": 1, "zone-b": 1, "zone-c": 1},
		expectedGivingZones:    map[string]int{},
		expectedReceivingZones: map[string]int{},
	}, {
		name: "preference for same zone even when giving an extra endpoint would result in slightly better distribution",
		allocations: map[string]Allocation{
			"zone-a": {Desired: 5.1},
			"zone-b": {Desired: 5.1},
			"zone-c": {Desired: 5.8},
		},
		allocatedHintsByZone:   map[string]int{"zone-a": 16},
		expectedGivingZones:    map[string]int{"zone-a": 10},
		expectedReceivingZones: map[string]int{"zone-b": 5, "zone-c": 5},
	}, {
		name: "when 2 zones need < 1 endpoint, give to zone that needs endpoint most",
		allocations: map[string]Allocation{
			"zone-a": {Desired: 5.0},
			"zone-b": {Desired: 5.6},
			"zone-c": {Desired: 5.4},
		},
		allocatedHintsByZone:   map[string]int{"zone-a": 16},
		expectedGivingZones:    map[string]int{"zone-a": 11},
		expectedReceivingZones: map[string]int{"zone-b": 6, "zone-c": 5},
	}, {
		name: "when 2 zones have extra endpoints, give from zone with most extra",
		allocations: map[string]Allocation{
			"zone-a": {Desired: 5.0},
			"zone-b": {Desired: 5.6},
			"zone-c": {Desired: 5.4},
		},
		allocatedHintsByZone:   map[string]int{"zone-b": 8, "zone-c": 8},
		expectedGivingZones:    map[string]int{"zone-b": 2, "zone-c": 3},
		expectedReceivingZones: map[string]int{"zone-a": 5},
	}, {
		name: "ensure function can handle unexpected data (more allocated than allocations)",
		allocations: map[string]Allocation{
			"zone-a": {Desired: 5.0},
			"zone-b": {Desired: 5.0},
			"zone-c": {Desired: 5.0},
		},
		allocatedHintsByZone:   map[string]int{"zone-a": 6, "zone-b": 6, "zone-c": 6},
		expectedGivingZones:    map[string]int{},
		expectedReceivingZones: map[string]int{},
	}, {
		name: "ensure function can handle unexpected data (negative allocations)",
		allocations: map[string]Allocation{
			"zone-a": {Desired: -5.0},
			"zone-b": {Desired: -5.0},
			"zone-c": {Desired: -5.0},
		},
		allocatedHintsByZone:   map[string]int{"zone-a": 6, "zone-b": 6, "zone-c": 6},
		expectedGivingZones:    map[string]int{},
		expectedReceivingZones: map[string]int{},
	}, {
		name: "ensure function can handle unexpected data (negative allocated)",
		allocations: map[string]Allocation{
			"zone-a": {Desired: 5.0},
			"zone-b": {Desired: 5.0},
			"zone-c": {Desired: 5.0},
		},
		allocatedHintsByZone:   map[string]int{"zone-a": -4, "zone-b": -3, "zone-c": -2},
		expectedGivingZones:    map[string]int{},
		expectedReceivingZones: map[string]int{},
	}, {
		name: "ensure function can handle unexpected data (negative for 1 zone)",
		allocations: map[string]Allocation{
			"zone-a": {Desired: 5.0},
			"zone-b": {Desired: 5.0},
			"zone-c": {Desired: 5.0},
		},
		allocatedHintsByZone:   map[string]int{"zone-a": -40, "zone-b": 20, "zone-c": 20},
		expectedGivingZones:    map[string]int{"zone-b": 15, "zone-c": 15},
		expectedReceivingZones: map[string]int{"zone-a": 30},
	}}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			actualGivingZones, actualReceivingZones := getGivingAndReceivingZones(tc.allocations, tc.allocatedHintsByZone)

			if !reflect.DeepEqual(actualGivingZones, tc.expectedGivingZones) {
				t.Errorf("Expected %+v giving zones, got %+v", tc.expectedGivingZones, actualGivingZones)
			}
			if !reflect.DeepEqual(actualReceivingZones, tc.expectedReceivingZones) {
				t.Errorf("Expected %+v receiving zones, got %+v", tc.expectedReceivingZones, actualReceivingZones)
			}
		})
	}
}

func Test_getHintsByZone(t *testing.T) {
	testCases := []struct {
		name                 string
		slice                discovery.EndpointSlice
		allocatedHintsByZone EndpointZoneInfo
		allocations          map[string]Allocation
		expectedHintsByZone  map[string]int
	}{{
		name:                 "empty",
		slice:                discovery.EndpointSlice{},
		allocations:          map[string]Allocation{},
		allocatedHintsByZone: EndpointZoneInfo{},
		expectedHintsByZone:  map[string]int{},
	}, {
		name: "single zone hint",
		slice: discovery.EndpointSlice{
			Endpoints: []discovery.Endpoint{{
				Zone:       utilpointer.StringPtr("zone-a"),
				Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-a"}}},
				Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
			}},
		},
		allocations: map[string]Allocation{
			"zone-a": {Maximum: 3},
		},
		allocatedHintsByZone: EndpointZoneInfo{"zone-a": 1},
		expectedHintsByZone: map[string]int{
			"zone-a": 1,
		},
	}, {
		name: "single zone hint with 1 unready endpoint and 1 unknown endpoint",
		slice: discovery.EndpointSlice{
			Endpoints: []discovery.Endpoint{{
				Zone:       utilpointer.StringPtr("zone-a"),
				Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-a"}}},
				Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
			}, {
				Zone:       utilpointer.StringPtr("zone-a"),
				Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-a"}}},
				Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(false)},
			}, {
				Zone:  utilpointer.StringPtr("zone-a"),
				Hints: &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-a"}}},
			}},
		},
		allocations: map[string]Allocation{
			"zone-a": {Maximum: 3},
		},
		allocatedHintsByZone: EndpointZoneInfo{"zone-a": 1},
		expectedHintsByZone: map[string]int{
			"zone-a": 1,
		},
	}, {
		name: "multiple zone hints",
		slice: discovery.EndpointSlice{
			Endpoints: []discovery.Endpoint{
				{
					Zone:       utilpointer.StringPtr("zone-a"),
					Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-a"}}},
					Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
				},
				{
					Zone:       utilpointer.StringPtr("zone-a"),
					Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-b"}}},
					Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
				},
				{
					Zone:       utilpointer.StringPtr("zone-b"),
					Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-b"}}},
					Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
				},
			},
		},
		allocations: map[string]Allocation{
			"zone-a": {Maximum: 3},
			"zone-b": {Maximum: 3},
			"zone-c": {Maximum: 3},
		},
		allocatedHintsByZone: EndpointZoneInfo{"zone-a": 1, "zone-b": 1, "zone-c": 1},
		expectedHintsByZone: map[string]int{
			"zone-a": 1,
			"zone-b": 2,
		},
	}, {
		name: "invalid by zones that no longer requires any allocations",
		slice: discovery.EndpointSlice{
			Endpoints: []discovery.Endpoint{
				{
					Zone:       utilpointer.StringPtr("zone-a"),
					Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-non-existent"}}},
					Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
				},
			},
		},
		allocations: map[string]Allocation{
			"zone-a": {Maximum: 3},
		},
		allocatedHintsByZone: EndpointZoneInfo{"zone-a": 1, "zone-b": 1, "zone-c": 1},
		expectedHintsByZone:  nil,
	}, {
		name: "invalid by endpoints with nil hints",
		slice: discovery.EndpointSlice{
			Endpoints: []discovery.Endpoint{
				{
					Zone:       utilpointer.StringPtr("zone-a"),
					Hints:      nil,
					Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
				},
			},
		},
		allocations: map[string]Allocation{
			"zone-a": {Maximum: 3},
		},
		allocatedHintsByZone: EndpointZoneInfo{},
		expectedHintsByZone:  nil,
	}, {
		name: "invalid by endpoint with no hints",
		slice: discovery.EndpointSlice{
			Endpoints: []discovery.Endpoint{
				{
					Zone:       utilpointer.StringPtr("zone-a"),
					Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{}},
					Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
				},
			},
		},
		allocations: map[string]Allocation{
			"zone-a": {Maximum: 3},
		},
		allocatedHintsByZone: EndpointZoneInfo{},
		expectedHintsByZone:  nil,
	}, {
		name: "invalid by hints that would make minimum allocations impossible",
		slice: discovery.EndpointSlice{
			Endpoints: []discovery.Endpoint{
				{
					Zone:       utilpointer.StringPtr("zone-a"),
					Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-a"}}},
					Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
				},
				{
					Zone:       utilpointer.StringPtr("zone-a"),
					Hints:      &discovery.EndpointHints{ForZones: []discovery.ForZone{{Name: "zone-a"}}},
					Conditions: discovery.EndpointConditions{Ready: utilpointer.BoolPtr(true)},
				},
			},
		},
		allocations: map[string]Allocation{
			"zone-a": {Maximum: 2},
		},
		allocatedHintsByZone: EndpointZoneInfo{"zone-a": 1},
		expectedHintsByZone:  nil,
	}}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			actualHintsByZone := getHintsByZone(&tc.slice, tc.allocatedHintsByZone, tc.allocations)

			if !reflect.DeepEqual(actualHintsByZone, tc.expectedHintsByZone) {
				// %#v for distinguishing between nil and empty map
				t.Errorf("Expected %#v hints by zones, got %#v", tc.expectedHintsByZone, actualHintsByZone)
			}
		})
	}
}

相关信息

kubernetes 源码目录

相关文章

kubernetes sliceinfo 源码

kubernetes sliceinfo_test 源码

kubernetes topologycache 源码

kubernetes topologycache_test 源码

kubernetes utils 源码

0  赞