blob: 4cab3457fb12e92c71b4f71e04f43d13e9e9d960 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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 objects
import (
"strconv"
"testing"
"time"
"github.com/google/btree"
"gotest.tools/v3/assert"
"github.com/apache/yunikorn-core/pkg/common/configs"
"github.com/apache/yunikorn-core/pkg/common/resources"
"github.com/apache/yunikorn-core/pkg/common/security"
"github.com/apache/yunikorn-core/pkg/events"
"github.com/apache/yunikorn-core/pkg/rmproxy"
"github.com/apache/yunikorn-core/pkg/scheduler/ugm"
"github.com/apache/yunikorn-scheduler-interface/lib/go/si"
)
const (
appID0 = "app-0"
appID1 = "app-1"
appID2 = "app-2"
aKey = "alloc-1"
aKey2 = "alloc-2"
nodeID1 = "node-1"
nodeID2 = "node-2"
instType1 = "itype-1"
)
// Create the root queue, base for all testing
func createRootQueue(maxRes map[string]string) (*Queue, error) {
return createManagedQueueWithProps(nil, "root", true, maxRes, nil)
}
// wrapper around the create call using the one syntax for all queue types
func createManagedQueue(parentSQ *Queue, name string, parent bool, maxRes map[string]string) (*Queue, error) {
return createManagedQueueWithProps(parentSQ, name, parent, maxRes, nil)
}
// create managed queue with props set
func createManagedQueueWithProps(parentSQ *Queue, name string, parent bool, maxRes, props map[string]string) (*Queue, error) {
return createManagedQueuePropsMaxApps(parentSQ, name, parent, maxRes, nil, props, uint64(0))
}
func createManagedQueueMaxApps(parentSQ *Queue, name string, parent bool, maxRes map[string]string, maxApps uint64) (*Queue, error) {
return createManagedQueuePropsMaxApps(parentSQ, name, parent, maxRes, nil, nil, maxApps)
}
func createManagedQueueGuaranteed(parentSQ *Queue, name string, parent bool, maxRes, guarRes map[string]string) (*Queue, error) {
return createManagedQueuePropsMaxApps(parentSQ, name, parent, maxRes, guarRes, nil, uint64(0))
}
func createManagedQueuePropsMaxApps(parentSQ *Queue, name string, parent bool, maxRes map[string]string, guarRes map[string]string, props map[string]string, maxApps uint64) (*Queue, error) {
queueConfig := configs.QueueConfig{
Name: name,
Parent: parent,
Queues: nil,
Properties: props,
MaxApplications: maxApps,
}
if maxRes != nil || guarRes != nil {
queueConfig.Resources = configs.Resources{
Max: maxRes,
Guaranteed: guarRes,
}
}
queue, err := NewConfiguredQueue(queueConfig, parentSQ)
if err != nil {
return nil, err
}
// root queue can not set the max via the config
if parentSQ == nil {
var max *resources.Resource
max, err = resources.NewResourceFromConf(maxRes)
if err != nil {
return nil, err
}
// only set if we have some limit
if len(max.Resources) > 0 && !resources.IsZero(max) {
queue.SetMaxResource(max)
}
}
return queue, nil
}
// wrapper around the create call using the one syntax for all queue types
// NOTE: test code uses a flag for parent=true, dynamic queues use leaf flag
func createDynamicQueue(parentSQ *Queue, name string, parent bool) (*Queue, error) {
return NewDynamicQueue(name, !parent, parentSQ)
}
// Create application with minimal info
func newApplication(appID, partition, queueName string) *Application {
tags := make(map[string]string)
return newApplicationWithTags(appID, partition, queueName, tags)
}
// Create application with minimal info
func newApplicationWithUserGroup(appID, partition, queueName string, userName string, groupList []string) *Application {
tags := make(map[string]string)
siApp := &si.AddApplicationRequest{
ApplicationID: appID,
QueueName: queueName,
PartitionName: partition,
Tags: tags,
}
return NewApplication(siApp, getUserGroup(userName, groupList), nil, "")
}
// Create application with tags set
func newApplicationWithTags(appID, partition, queueName string, tags map[string]string) *Application {
siApp := &si.AddApplicationRequest{
ApplicationID: appID,
QueueName: queueName,
PartitionName: partition,
Tags: tags,
}
return NewApplication(siApp, getTestUserGroup(), nil, "")
}
func newApplicationWithHandler(appID, partition, queueName string) (*Application, *rmproxy.MockedRMProxy) {
return newApplicationWithPlaceholderTimeout(appID, partition, queueName, 0)
}
func newApplicationWithPlaceholderTimeout(appID, partition, queueName string, phTimeout int64) (*Application, *rmproxy.MockedRMProxy) {
siApp := &si.AddApplicationRequest{
ApplicationID: appID,
QueueName: queueName,
PartitionName: partition,
ExecutionTimeoutMilliSeconds: phTimeout,
}
mockEventHandler := rmproxy.NewMockedRMProxy()
return NewApplication(siApp, getTestUserGroup(), mockEventHandler, ""), mockEventHandler
}
// Create node with minimal info
func newNode(nodeID string, totalMap map[string]resources.Quantity) *Node {
total := resources.NewResourceFromMap(totalMap)
return newNodeInternal(nodeID, total, resources.Zero)
}
func newNodeRes(nodeID string, total *resources.Resource) *Node {
return newNodeInternal(nodeID, total, resources.Zero)
}
func newNodeInternal(nodeID string, total, occupied *resources.Resource) *Node {
sn := &Node{
NodeID: nodeID,
Hostname: "",
Rackname: "",
Partition: "",
attributes: nil,
totalResource: total,
occupiedResource: occupied,
allocatedResource: resources.NewResource(),
availableResource: resources.Sub(total, occupied),
allocations: make(map[string]*Allocation),
schedulable: true,
reservations: make(map[string]*reservation),
}
sn.nodeEvents = newNodeEvents(sn, events.GetEventSystem())
return sn
}
func newProto(nodeID string, totalResource, occupiedResource *resources.Resource, attributes map[string]string) *si.NodeInfo {
proto := si.NodeInfo{
NodeID: nodeID,
Attributes: attributes,
}
if totalResource != nil {
proto.SchedulableResource = &si.Resource{
Resources: map[string]*si.Quantity{},
}
for name, value := range totalResource.Resources {
quantity := si.Quantity{Value: int64(value)}
proto.SchedulableResource.Resources[name] = &quantity
}
}
if occupiedResource != nil {
proto.OccupiedResource = &si.Resource{
Resources: map[string]*si.Quantity{},
}
for name, value := range occupiedResource.Resources {
quantity := si.Quantity{Value: int64(value)}
proto.OccupiedResource.Resources[name] = &quantity
}
}
return &proto
}
// Create a new Allocation with a random ask key
func newAllocation(appID, nodeID string, res *resources.Resource) *Allocation {
askKey := strconv.FormatInt((time.Now()).UnixNano(), 10)
ask := newAllocationAsk(askKey, appID, res)
return NewAllocation(nodeID, ask)
}
// Create a new Allocation with a random ask key
func newPlaceholderAlloc(appID, nodeID string, res *resources.Resource) *Allocation {
askKey := strconv.FormatInt((time.Now()).UnixNano(), 10)
ask := newAllocationAsk(askKey, appID, res)
ask.placeholder = true
return NewAllocation(nodeID, ask)
}
func newAllocationAsk(allocKey, appID string, res *resources.Resource) *AllocationAsk {
return newAllocationAskAll(allocKey, appID, "", res, false, 0)
}
func newAllocationAskPriority(allocKey, appID string, res *resources.Resource, priority int32) *AllocationAsk {
return newAllocationAskAll(allocKey, appID, "", res, false, priority)
}
func newAllocationAskTG(allocKey, appID, taskGroup string, res *resources.Resource) *AllocationAsk {
return newAllocationAskAll(allocKey, appID, taskGroup, res, taskGroup != "", 0)
}
func newAllocationAskAll(allocKey, appID, taskGroup string, res *resources.Resource, placeholder bool, priority int32) *AllocationAsk {
ask := &si.AllocationAsk{
AllocationKey: allocKey,
ApplicationID: appID,
PartitionName: "default",
ResourceAsk: res.ToProto(),
TaskGroupName: taskGroup,
Placeholder: placeholder,
Priority: priority,
}
return NewAllocationAskFromSI(ask)
}
func getTestUserGroup() security.UserGroup {
return security.UserGroup{User: "testuser", Groups: []string{"testgroup"}}
}
func getUserGroup(userName string, groupNameList []string) security.UserGroup {
return security.UserGroup{User: userName, Groups: groupNameList}
}
func assertUserGroupResource(t *testing.T, userGroup security.UserGroup, expected *resources.Resource) {
ugm := ugm.GetUserManager()
userResource := ugm.GetUserResources(userGroup)
groupResource := ugm.GetGroupResources(userGroup.Groups[0])
assert.Equal(t, resources.Equals(userResource, expected), true)
assert.Equal(t, resources.Equals(groupResource, nil), true)
}
func assertUserResourcesAndGroupResources(t *testing.T, userGroup security.UserGroup, expectedUserResources *resources.Resource, expectedGroupResources *resources.Resource, i int) {
ugm := ugm.GetUserManager()
userResource := ugm.GetUserResources(userGroup)
groupResource := ugm.GetGroupResources(userGroup.Groups[i])
assert.Equal(t, resources.Equals(userResource, expectedUserResources), true)
assert.Equal(t, resources.Equals(groupResource, expectedGroupResources), true)
}
func getNodeIteratorFn(nodes ...*Node) func() NodeIterator {
tree := btree.New(7)
for _, node := range nodes {
tree.ReplaceOrInsert(nodeRef{
node, 1,
})
}
return func() NodeIterator {
return NewTreeIterator(acceptAll, func() *btree.BTree {
return tree
})
}
}