mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 18:28:13 +00:00 
			
		
		
		
	Added Bind method to Scheduler Extender
- only one extender can support the bind method - if an extender supports bind, scheduler delegates the pod binding to the extender
This commit is contained in:
		| @@ -19,6 +19,7 @@ | |||||||
|         "urlPrefix": "http://127.0.0.1:12346/scheduler", |         "urlPrefix": "http://127.0.0.1:12346/scheduler", | ||||||
|         "apiVersion": "v1beta1", |         "apiVersion": "v1beta1", | ||||||
|         "filterVerb": "filter", |         "filterVerb": "filter", | ||||||
|  |         "bindVerb": "bind", | ||||||
|         "prioritizeVerb": "prioritize", |         "prioritizeVerb": "prioritize", | ||||||
|         "weight": 5, |         "weight": 5, | ||||||
|         "enableHttps": false, |         "enableHttps": false, | ||||||
|   | |||||||
| @@ -35,6 +35,12 @@ type SchedulerExtender interface { | |||||||
| 	// are used to compute the weighted score for an extender. The weighted scores are added to | 	// are used to compute the weighted score for an extender. The weighted scores are added to | ||||||
| 	// the scores computed  by Kubernetes scheduler. The total scores are used to do the host selection. | 	// the scores computed  by Kubernetes scheduler. The total scores are used to do the host selection. | ||||||
| 	Prioritize(pod *v1.Pod, nodes []*v1.Node) (hostPriorities *schedulerapi.HostPriorityList, weight int, err error) | 	Prioritize(pod *v1.Pod, nodes []*v1.Node) (hostPriorities *schedulerapi.HostPriorityList, weight int, err error) | ||||||
|  |  | ||||||
|  | 	// Bind delegates the action of binding a pod to a node to the extender. | ||||||
|  | 	Bind(binding *v1.Binding) error | ||||||
|  |  | ||||||
|  | 	// IsBinder returns whether this extender is configured for the Bind method. | ||||||
|  | 	IsBinder() bool | ||||||
| } | } | ||||||
|  |  | ||||||
| // ScheduleAlgorithm is an interface implemented by things that know how to schedule pods | // ScheduleAlgorithm is an interface implemented by things that know how to schedule pods | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ go_library( | |||||||
|         "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", |         "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", | ||||||
|         "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", |         "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", | ||||||
|         "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", |         "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", | ||||||
|  |         "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", | ||||||
|         "//vendor/k8s.io/client-go/rest:go_default_library", |         "//vendor/k8s.io/client-go/rest:go_default_library", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  | 	"k8s.io/apimachinery/pkg/types" | ||||||
| 	restclient "k8s.io/client-go/rest" | 	restclient "k8s.io/client-go/rest" | ||||||
| 	"k8s.io/kubernetes/pkg/api/v1" | 	"k8s.io/kubernetes/pkg/api/v1" | ||||||
| ) | ) | ||||||
| @@ -133,6 +134,10 @@ type ExtenderConfig struct { | |||||||
| 	// The numeric multiplier for the node scores that the prioritize call generates. | 	// The numeric multiplier for the node scores that the prioritize call generates. | ||||||
| 	// The weight should be a positive integer | 	// The weight should be a positive integer | ||||||
| 	Weight int | 	Weight int | ||||||
|  | 	// Verb for the bind call, empty if not supported. This verb is appended to the URLPrefix when issuing the bind call to extender. | ||||||
|  | 	// If this method is implemented by the extender, it is the extender's responsibility to bind the pod to apiserver. Only one extender | ||||||
|  | 	// can implement this function. | ||||||
|  | 	BindVerb string | ||||||
| 	// EnableHttps specifies whether https should be used to communicate with the extender | 	// EnableHttps specifies whether https should be used to communicate with the extender | ||||||
| 	EnableHttps bool | 	EnableHttps bool | ||||||
| 	// TLSConfig specifies the transport layer security config | 	// TLSConfig specifies the transport layer security config | ||||||
| @@ -176,6 +181,24 @@ type ExtenderFilterResult struct { | |||||||
| 	Error string | 	Error string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ExtenderBindingArgs represents the arguments to an extender for binding a pod to a node. | ||||||
|  | type ExtenderBindingArgs struct { | ||||||
|  | 	// PodName is the name of the pod being bound | ||||||
|  | 	PodName string | ||||||
|  | 	// PodNamespace is the namespace of the pod being bound | ||||||
|  | 	PodNamespace string | ||||||
|  | 	// PodUID is the UID of the pod being bound | ||||||
|  | 	PodUID types.UID | ||||||
|  | 	// Node selected by the scheduler | ||||||
|  | 	Node string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ExtenderBindingResult represents the result of binding of a pod to a node from an extender. | ||||||
|  | type ExtenderBindingResult struct { | ||||||
|  | 	// Error message indicating failure | ||||||
|  | 	Error string | ||||||
|  | } | ||||||
|  |  | ||||||
| // HostPriority represents the priority of scheduling to a particular host, higher priority is better. | // HostPriority represents the priority of scheduling to a particular host, higher priority is better. | ||||||
| type HostPriority struct { | type HostPriority struct { | ||||||
| 	// Name of the host | 	// Name of the host | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ go_library( | |||||||
|         "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", |         "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", | ||||||
|         "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", |         "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", | ||||||
|         "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", |         "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", | ||||||
|  |         "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", | ||||||
|         "//vendor/k8s.io/client-go/rest:go_default_library", |         "//vendor/k8s.io/client-go/rest:go_default_library", | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||||||
|  | 	"k8s.io/apimachinery/pkg/types" | ||||||
| 	restclient "k8s.io/client-go/rest" | 	restclient "k8s.io/client-go/rest" | ||||||
| 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | 	apiv1 "k8s.io/kubernetes/pkg/api/v1" | ||||||
| ) | ) | ||||||
| @@ -125,6 +126,10 @@ type ExtenderConfig struct { | |||||||
| 	// The numeric multiplier for the node scores that the prioritize call generates. | 	// The numeric multiplier for the node scores that the prioritize call generates. | ||||||
| 	// The weight should be a positive integer | 	// The weight should be a positive integer | ||||||
| 	Weight int `json:"weight,omitempty"` | 	Weight int `json:"weight,omitempty"` | ||||||
|  | 	// Verb for the bind call, empty if not supported. This verb is appended to the URLPrefix when issuing the bind call to extender. | ||||||
|  | 	// If this method is implemented by the extender, it is the extender's responsibility to bind the pod to apiserver. Only one extender | ||||||
|  | 	// can implement this function. | ||||||
|  | 	BindVerb string | ||||||
| 	// EnableHttps specifies whether https should be used to communicate with the extender | 	// EnableHttps specifies whether https should be used to communicate with the extender | ||||||
| 	EnableHttps bool `json:"enableHttps,omitempty"` | 	EnableHttps bool `json:"enableHttps,omitempty"` | ||||||
| 	// TLSConfig specifies the transport layer security config | 	// TLSConfig specifies the transport layer security config | ||||||
| @@ -168,6 +173,24 @@ type ExtenderFilterResult struct { | |||||||
| 	Error string `json:"error,omitempty"` | 	Error string `json:"error,omitempty"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ExtenderBindingArgs represents the arguments to an extender for binding a pod to a node. | ||||||
|  | type ExtenderBindingArgs struct { | ||||||
|  | 	// PodName is the name of the pod being bound | ||||||
|  | 	PodName string | ||||||
|  | 	// PodNamespace is the namespace of the pod being bound | ||||||
|  | 	PodNamespace string | ||||||
|  | 	// PodUID is the UID of the pod being bound | ||||||
|  | 	PodUID types.UID | ||||||
|  | 	// Node selected by the scheduler | ||||||
|  | 	Node string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ExtenderBindingResult represents the result of binding of a pod to a node from an extender. | ||||||
|  | type ExtenderBindingResult struct { | ||||||
|  | 	// Error message indicating failure | ||||||
|  | 	Error string | ||||||
|  | } | ||||||
|  |  | ||||||
| // HostPriority represents the priority of scheduling to a particular host, higher priority is better. | // HostPriority represents the priority of scheduling to a particular host, higher priority is better. | ||||||
| type HostPriority struct { | type HostPriority struct { | ||||||
| 	// Name of the host | 	// Name of the host | ||||||
|   | |||||||
| @@ -34,10 +34,17 @@ func ValidatePolicy(policy schedulerapi.Policy) error { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	binders := 0 | ||||||
| 	for _, extender := range policy.ExtenderConfigs { | 	for _, extender := range policy.ExtenderConfigs { | ||||||
| 		if extender.Weight <= 0 { | 		if extender.Weight <= 0 { | ||||||
| 			validationErrors = append(validationErrors, fmt.Errorf("Priority for extender %s should have a positive weight applied to it", extender.URLPrefix)) | 			validationErrors = append(validationErrors, fmt.Errorf("Priority for extender %s should have a positive weight applied to it", extender.URLPrefix)) | ||||||
| 		} | 		} | ||||||
|  | 		if extender.BindVerb != "" { | ||||||
|  | 			binders++ | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if binders > 1 { | ||||||
|  | 		validationErrors = append(validationErrors, fmt.Errorf("Only one extender can implement bind, found %v", binders)) | ||||||
| 	} | 	} | ||||||
| 	return utilerrors.NewAggregate(validationErrors) | 	return utilerrors.NewAggregate(validationErrors) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -72,3 +72,15 @@ func TestValidateExtenderWithNegativeWeight(t *testing.T) { | |||||||
| 		t.Errorf("Expected error about priority weight for extender not being positive") | 		t.Errorf("Expected error about priority weight for extender not being positive") | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestValidateMultipleExtendersWithBind(t *testing.T) { | ||||||
|  | 	extenderPolicy := api.Policy{ | ||||||
|  | 		ExtenderConfigs: []api.ExtenderConfig{ | ||||||
|  | 			{URLPrefix: "http://127.0.0.1:8081/extender", BindVerb: "bind"}, | ||||||
|  | 			{URLPrefix: "http://127.0.0.1:8082/extender", BindVerb: "bind"}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	if ValidatePolicy(extenderPolicy) == nil { | ||||||
|  | 		t.Errorf("Expected failure when multiple extenders with bind") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -41,6 +41,7 @@ type HTTPExtender struct { | |||||||
| 	extenderURL      string | 	extenderURL      string | ||||||
| 	filterVerb       string | 	filterVerb       string | ||||||
| 	prioritizeVerb   string | 	prioritizeVerb   string | ||||||
|  | 	bindVerb         string | ||||||
| 	weight           int | 	weight           int | ||||||
| 	client           *http.Client | 	client           *http.Client | ||||||
| 	nodeCacheCapable bool | 	nodeCacheCapable bool | ||||||
| @@ -86,6 +87,7 @@ func NewHTTPExtender(config *schedulerapi.ExtenderConfig) (algorithm.SchedulerEx | |||||||
| 		extenderURL:      config.URLPrefix, | 		extenderURL:      config.URLPrefix, | ||||||
| 		filterVerb:       config.FilterVerb, | 		filterVerb:       config.FilterVerb, | ||||||
| 		prioritizeVerb:   config.PrioritizeVerb, | 		prioritizeVerb:   config.PrioritizeVerb, | ||||||
|  | 		bindVerb:         config.BindVerb, | ||||||
| 		weight:           config.Weight, | 		weight:           config.Weight, | ||||||
| 		client:           client, | 		client:           client, | ||||||
| 		nodeCacheCapable: config.NodeCacheCapable, | 		nodeCacheCapable: config.NodeCacheCapable, | ||||||
| @@ -193,6 +195,33 @@ func (h *HTTPExtender) Prioritize(pod *v1.Pod, nodes []*v1.Node) (*schedulerapi. | |||||||
| 	return &result, h.weight, nil | 	return &result, h.weight, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Bind delegates the action of binding a pod to a node to the extender. | ||||||
|  | func (h *HTTPExtender) Bind(binding *v1.Binding) error { | ||||||
|  | 	var result schedulerapi.ExtenderBindingResult | ||||||
|  | 	if !h.IsBinder() { | ||||||
|  | 		// This shouldn't happen as this extender wouldn't have become a Binder. | ||||||
|  | 		return fmt.Errorf("Unexpected empty bindVerb in extender") | ||||||
|  | 	} | ||||||
|  | 	req := &schedulerapi.ExtenderBindingArgs{ | ||||||
|  | 		PodName:      binding.Name, | ||||||
|  | 		PodNamespace: binding.Namespace, | ||||||
|  | 		PodUID:       binding.UID, | ||||||
|  | 		Node:         binding.Target.Name, | ||||||
|  | 	} | ||||||
|  | 	if err := h.send(h.bindVerb, &req, &result); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if result.Error != "" { | ||||||
|  | 		return fmt.Errorf(result.Error) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsBinder returns whether this extender is configured for the Bind method. | ||||||
|  | func (h *HTTPExtender) IsBinder() bool { | ||||||
|  | 	return h.bindVerb != "" | ||||||
|  | } | ||||||
|  |  | ||||||
| // Helper function to send messages to the extender | // Helper function to send messages to the extender | ||||||
| func (h *HTTPExtender) send(action string, args interface{}, result interface{}) error { | func (h *HTTPExtender) send(action string, args interface{}, result interface{}) error { | ||||||
| 	out, err := json.Marshal(args) | 	out, err := json.Marshal(args) | ||||||
| @@ -214,6 +243,10 @@ func (h *HTTPExtender) send(action string, args interface{}, result interface{}) | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if resp.StatusCode != http.StatusOK { | ||||||
|  | 		return fmt.Errorf("Failed %v with extender at URL %v, code %v", action, h.extenderURL, resp.StatusCode) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	defer resp.Body.Close() | 	defer resp.Body.Close() | ||||||
| 	body, err := ioutil.ReadAll(resp.Body) | 	body, err := ioutil.ReadAll(resp.Body) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
| @@ -109,6 +109,7 @@ type FakeExtender struct { | |||||||
| 	prioritizers     []priorityConfig | 	prioritizers     []priorityConfig | ||||||
| 	weight           int | 	weight           int | ||||||
| 	nodeCacheCapable bool | 	nodeCacheCapable bool | ||||||
|  | 	filteredNodes    []*v1.Node | ||||||
| } | } | ||||||
|  |  | ||||||
| func (f *FakeExtender) Filter(pod *v1.Pod, nodes []*v1.Node, nodeNameToInfo map[string]*schedulercache.NodeInfo) ([]*v1.Node, schedulerapi.FailedNodesMap, error) { | func (f *FakeExtender) Filter(pod *v1.Pod, nodes []*v1.Node, nodeNameToInfo map[string]*schedulercache.NodeInfo) ([]*v1.Node, schedulerapi.FailedNodesMap, error) { | ||||||
| @@ -133,6 +134,7 @@ func (f *FakeExtender) Filter(pod *v1.Pod, nodes []*v1.Node, nodeNameToInfo map[ | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	f.filteredNodes = filtered | ||||||
| 	if f.nodeCacheCapable { | 	if f.nodeCacheCapable { | ||||||
| 		return filtered, failedNodesMap, nil | 		return filtered, failedNodesMap, nil | ||||||
| 	} | 	} | ||||||
| @@ -162,6 +164,25 @@ func (f *FakeExtender) Prioritize(pod *v1.Pod, nodes []*v1.Node) (*schedulerapi. | |||||||
| 	return &result, f.weight, nil | 	return &result, f.weight, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (f *FakeExtender) Bind(binding *v1.Binding) error { | ||||||
|  | 	if len(f.filteredNodes) != 0 { | ||||||
|  | 		for _, node := range f.filteredNodes { | ||||||
|  | 			if node.Name == binding.Target.Name { | ||||||
|  | 				f.filteredNodes = nil | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		err := fmt.Errorf("Node %v not in filtered nodes %v", binding.Target.Name, f.filteredNodes) | ||||||
|  | 		f.filteredNodes = nil | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *FakeExtender) IsBinder() bool { | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestGenericSchedulerWithExtenders(t *testing.T) { | func TestGenericSchedulerWithExtenders(t *testing.T) { | ||||||
| 	tests := []struct { | 	tests := []struct { | ||||||
| 		name                 string | 		name                 string | ||||||
|   | |||||||
| @@ -386,6 +386,16 @@ func (f *ConfigFactory) CreateFromConfig(policy schedulerapi.Policy) (*scheduler | |||||||
| 	return f.CreateFromKeys(predicateKeys, priorityKeys, extenders) | 	return f.CreateFromKeys(predicateKeys, priorityKeys, extenders) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // getBinder returns an extender that supports bind or a default binder. | ||||||
|  | func (f *ConfigFactory) getBinder(extenders []algorithm.SchedulerExtender) scheduler.Binder { | ||||||
|  | 	for i := range extenders { | ||||||
|  | 		if extenders[i].IsBinder() { | ||||||
|  | 			return extenders[i] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return &binder{f.client} | ||||||
|  | } | ||||||
|  |  | ||||||
| // Creates a scheduler from a set of registered fit predicate keys and priority keys. | // Creates a scheduler from a set of registered fit predicate keys and priority keys. | ||||||
| func (f *ConfigFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String, extenders []algorithm.SchedulerExtender) (*scheduler.Config, error) { | func (f *ConfigFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String, extenders []algorithm.SchedulerExtender) (*scheduler.Config, error) { | ||||||
| 	glog.V(2).Infof("Creating scheduler with fit predicates '%v' and priority functions '%v", predicateKeys, priorityKeys) | 	glog.V(2).Infof("Creating scheduler with fit predicates '%v' and priority functions '%v", predicateKeys, priorityKeys) | ||||||
| @@ -422,7 +432,7 @@ func (f *ConfigFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String, | |||||||
| 		// The scheduler only needs to consider schedulable nodes. | 		// The scheduler only needs to consider schedulable nodes. | ||||||
| 		NodeLister:          &nodePredicateLister{f.nodeLister}, | 		NodeLister:          &nodePredicateLister{f.nodeLister}, | ||||||
| 		Algorithm:           algo, | 		Algorithm:           algo, | ||||||
| 		Binder:              &binder{f.client}, | 		Binder:              f.getBinder(extenders), | ||||||
| 		PodConditionUpdater: &podConditionUpdater{f.client}, | 		PodConditionUpdater: &podConditionUpdater{f.client}, | ||||||
| 		WaitForCacheSync: func() bool { | 		WaitForCacheSync: func() bool { | ||||||
| 			return cache.WaitForCacheSync(f.StopEverything, f.scheduledPodsHasSynced) | 			return cache.WaitForCacheSync(f.StopEverything, f.scheduledPodsHasSynced) | ||||||
|   | |||||||
| @@ -271,7 +271,7 @@ func (sched *Scheduler) scheduleOne() { | |||||||
| 	// bind the pod to its host asynchronously (we can do this b/c of the assumption step above). | 	// bind the pod to its host asynchronously (we can do this b/c of the assumption step above). | ||||||
| 	go func() { | 	go func() { | ||||||
| 		err := sched.bind(pod, &v1.Binding{ | 		err := sched.bind(pod, &v1.Binding{ | ||||||
| 			ObjectMeta: metav1.ObjectMeta{Namespace: pod.Namespace, Name: pod.Name}, | 			ObjectMeta: metav1.ObjectMeta{Namespace: pod.Namespace, Name: pod.Name, UID: pod.UID}, | ||||||
| 			Target: v1.ObjectReference{ | 			Target: v1.ObjectReference{ | ||||||
| 				Kind: "Node", | 				Kind: "Node", | ||||||
| 				Name: suggestedHost, | 				Name: suggestedHost, | ||||||
|   | |||||||
| @@ -49,6 +49,7 @@ import ( | |||||||
| const ( | const ( | ||||||
| 	filter     = "filter" | 	filter     = "filter" | ||||||
| 	prioritize = "prioritize" | 	prioritize = "prioritize" | ||||||
|  | 	bind       = "bind" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type fitPredicate func(pod *v1.Pod, node *v1.Node) (bool, error) | type fitPredicate func(pod *v1.Pod, node *v1.Node) (bool, error) | ||||||
| @@ -64,39 +65,59 @@ type Extender struct { | |||||||
| 	predicates       []fitPredicate | 	predicates       []fitPredicate | ||||||
| 	prioritizers     []priorityConfig | 	prioritizers     []priorityConfig | ||||||
| 	nodeCacheCapable bool | 	nodeCacheCapable bool | ||||||
|  | 	Client           clientset.Interface | ||||||
| } | } | ||||||
|  |  | ||||||
| func (e *Extender) serveHTTP(t *testing.T, w http.ResponseWriter, req *http.Request) { | func (e *Extender) serveHTTP(t *testing.T, w http.ResponseWriter, req *http.Request) { | ||||||
| 	var args schedulerapi.ExtenderArgs |  | ||||||
|  |  | ||||||
| 	decoder := json.NewDecoder(req.Body) | 	decoder := json.NewDecoder(req.Body) | ||||||
| 	defer req.Body.Close() | 	defer req.Body.Close() | ||||||
|  |  | ||||||
| 	if err := decoder.Decode(&args); err != nil { |  | ||||||
| 		http.Error(w, "Decode error", http.StatusBadRequest) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	encoder := json.NewEncoder(w) | 	encoder := json.NewEncoder(w) | ||||||
|  |  | ||||||
| 	if strings.Contains(req.URL.Path, filter) { | 	if strings.Contains(req.URL.Path, filter) || strings.Contains(req.URL.Path, prioritize) { | ||||||
| 		resp := &schedulerapi.ExtenderFilterResult{} | 		var args schedulerapi.ExtenderArgs | ||||||
| 		resp, err := e.Filter(&args) |  | ||||||
| 		if err != nil { | 		if err := decoder.Decode(&args); err != nil { | ||||||
|  | 			http.Error(w, "Decode error", http.StatusBadRequest) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if strings.Contains(req.URL.Path, filter) { | ||||||
|  | 			resp := &schedulerapi.ExtenderFilterResult{} | ||||||
|  | 			resp, err := e.Filter(&args) | ||||||
|  | 			if err != nil { | ||||||
|  | 				resp.Error = err.Error() | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if err := encoder.Encode(resp); err != nil { | ||||||
|  | 				t.Fatalf("Failed to encode %v", resp) | ||||||
|  | 			} | ||||||
|  | 		} else if strings.Contains(req.URL.Path, prioritize) { | ||||||
|  | 			// Prioritize errors are ignored. Default k8s priorities or another extender's | ||||||
|  | 			// priorities may be applied. | ||||||
|  | 			priorities, _ := e.Prioritize(&args) | ||||||
|  |  | ||||||
|  | 			if err := encoder.Encode(priorities); err != nil { | ||||||
|  | 				t.Fatalf("Failed to encode %+v", priorities) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else if strings.Contains(req.URL.Path, bind) { | ||||||
|  | 		var args schedulerapi.ExtenderBindingArgs | ||||||
|  |  | ||||||
|  | 		if err := decoder.Decode(&args); err != nil { | ||||||
|  | 			http.Error(w, "Decode error", http.StatusBadRequest) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		resp := &schedulerapi.ExtenderBindingResult{} | ||||||
|  |  | ||||||
|  | 		if err := e.Bind(&args); err != nil { | ||||||
| 			resp.Error = err.Error() | 			resp.Error = err.Error() | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if err := encoder.Encode(resp); err != nil { | 		if err := encoder.Encode(resp); err != nil { | ||||||
| 			t.Fatalf("Failed to encode %+v", resp) | 			t.Fatalf("Failed to encode %+v", resp) | ||||||
| 		} | 		} | ||||||
| 	} else if strings.Contains(req.URL.Path, prioritize) { |  | ||||||
| 		// Prioritize errors are ignored. Default k8s priorities or another extender's |  | ||||||
| 		// priorities may be applied. |  | ||||||
| 		priorities, _ := e.Prioritize(&args) |  | ||||||
|  |  | ||||||
| 		if err := encoder.Encode(priorities); err != nil { |  | ||||||
| 			t.Fatalf("Failed to encode %+v", priorities) |  | ||||||
| 		} |  | ||||||
| 	} else { | 	} else { | ||||||
| 		http.Error(w, "Unknown method", http.StatusNotFound) | 		http.Error(w, "Unknown method", http.StatusNotFound) | ||||||
| 	} | 	} | ||||||
| @@ -209,6 +230,18 @@ func (e *Extender) Prioritize(args *schedulerapi.ExtenderArgs) (*schedulerapi.Ho | |||||||
| 	return &result, nil | 	return &result, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (e *Extender) Bind(binding *schedulerapi.ExtenderBindingArgs) error { | ||||||
|  | 	b := &v1.Binding{ | ||||||
|  | 		ObjectMeta: metav1.ObjectMeta{Namespace: binding.PodNamespace, Name: binding.PodName, UID: binding.PodUID}, | ||||||
|  | 		Target: v1.ObjectReference{ | ||||||
|  | 			Kind: "Node", | ||||||
|  | 			Name: binding.Node, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return e.Client.CoreV1().Pods(b.Namespace).Bind(b) | ||||||
|  | } | ||||||
|  |  | ||||||
| func machine_1_2_3_Predicate(pod *v1.Pod, node *v1.Node) (bool, error) { | func machine_1_2_3_Predicate(pod *v1.Pod, node *v1.Node) (bool, error) { | ||||||
| 	if node.Name == "machine1" || node.Name == "machine2" || node.Name == "machine3" { | 	if node.Name == "machine1" || node.Name == "machine2" || node.Name == "machine3" { | ||||||
| 		return true, nil | 		return true, nil | ||||||
| @@ -276,6 +309,7 @@ func TestSchedulerExtender(t *testing.T) { | |||||||
| 		name:         "extender2", | 		name:         "extender2", | ||||||
| 		predicates:   []fitPredicate{machine_2_3_5_Predicate}, | 		predicates:   []fitPredicate{machine_2_3_5_Predicate}, | ||||||
| 		prioritizers: []priorityConfig{{machine_3_Prioritizer, 1}}, | 		prioritizers: []priorityConfig{{machine_3_Prioritizer, 1}}, | ||||||
|  | 		Client:       clientSet, | ||||||
| 	} | 	} | ||||||
| 	es2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | 	es2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||||
| 		extender2.serveHTTP(t, w, req) | 		extender2.serveHTTP(t, w, req) | ||||||
| @@ -306,6 +340,7 @@ func TestSchedulerExtender(t *testing.T) { | |||||||
| 				URLPrefix:      es2.URL, | 				URLPrefix:      es2.URL, | ||||||
| 				FilterVerb:     filter, | 				FilterVerb:     filter, | ||||||
| 				PrioritizeVerb: prioritize, | 				PrioritizeVerb: prioritize, | ||||||
|  | 				BindVerb:       bind, | ||||||
| 				Weight:         4, | 				Weight:         4, | ||||||
| 				EnableHttps:    false, | 				EnableHttps:    false, | ||||||
| 			}, | 			}, | ||||||
| @@ -402,5 +437,13 @@ func DoTestPodScheduling(ns *v1.Namespace, t *testing.T, cs clientset.Interface) | |||||||
| 	} else if myPod.Spec.NodeName != "machine2" { | 	} else if myPod.Spec.NodeName != "machine2" { | ||||||
| 		t.Fatalf("Failed to schedule using extender, expected machine2, got %v", myPod.Spec.NodeName) | 		t.Fatalf("Failed to schedule using extender, expected machine2, got %v", myPod.Spec.NodeName) | ||||||
| 	} | 	} | ||||||
|  | 	var gracePeriod int64 | ||||||
|  | 	if err := cs.Core().Pods(ns.Name).Delete(myPod.Name, &metav1.DeleteOptions{GracePeriodSeconds: &gracePeriod}); err != nil { | ||||||
|  | 		t.Fatalf("Failed to delete pod: %v", err) | ||||||
|  | 	} | ||||||
|  | 	_, err = cs.Core().Pods(ns.Name).Get(myPod.Name, metav1.GetOptions{}) | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Fatalf("Failed to delete pod: %v", err) | ||||||
|  | 	} | ||||||
| 	t.Logf("Scheduled pod using extenders") | 	t.Logf("Scheduled pod using extenders") | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Ravi Gadde
					Ravi Gadde