open-nomad/nomad/structs/node_pool.go

275 lines
7.3 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package structs
import (
"fmt"
"regexp"
"sort"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/nomad/helper/pointer"
"golang.org/x/crypto/blake2b"
"golang.org/x/exp/maps"
)
const (
// NodePoolAll is a built-in node pool that always includes all nodes in
// the cluster.
NodePoolAll = "all"
NodePoolAllDescription = "Node pool with all nodes in the cluster."
// NodePoolDefault is a built-in node pool for nodes that don't specify a
// node pool in their configuration.
NodePoolDefault = "default"
NodePoolDefaultDescription = "Default node pool."
// maxNodePoolDescriptionLength is the maximum length allowed for a node
// pool description.
maxNodePoolDescriptionLength = 256
)
var (
// validNodePoolName is the rule used to validate a node pool name.
validNodePoolName = regexp.MustCompile("^[a-zA-Z0-9-_]{1,128}$")
)
// ValidadeNodePoolName returns an error if a node pool name is invalid.
func ValidateNodePoolName(pool string) error {
if !validNodePoolName.MatchString(pool) {
return fmt.Errorf("invalid name %q, must match regex %s", pool, validNodePoolName)
}
return nil
}
// NodePool allows partioning infrastructure
type NodePool struct {
// Name is the node pool name. It must be unique.
Name string
// Description is the human-friendly description of the node pool.
Description string
// Meta is a set of user-provided metadata for the node pool.
Meta map[string]string
// SchedulerConfiguration is the scheduler configuration specific to the
// node pool.
SchedulerConfiguration *NodePoolSchedulerConfiguration
// Hash is the hash of the node pool which is used to efficiently diff when
// we replicate pools across regions.
Hash []byte
// Raft indexes.
CreateIndex uint64
ModifyIndex uint64
}
// GetID implements the IDGetter interface required for pagination.
func (n *NodePool) GetID() string {
return n.Name
}
// Validate returns an error if the node pool is invalid.
func (n *NodePool) Validate() error {
var mErr *multierror.Error
mErr = multierror.Append(mErr, ValidateNodePoolName(n.Name))
if len(n.Description) > maxNodePoolDescriptionLength {
mErr = multierror.Append(mErr, fmt.Errorf("description longer than %d", maxNodePoolDescriptionLength))
}
mErr = multierror.Append(mErr, n.SchedulerConfiguration.Validate())
return mErr.ErrorOrNil()
}
// Copy returns a deep copy of the node pool.
func (n *NodePool) Copy() *NodePool {
if n == nil {
return nil
}
nc := new(NodePool)
*nc = *n
nc.Meta = maps.Clone(nc.Meta)
nc.SchedulerConfiguration = nc.SchedulerConfiguration.Copy()
nc.Hash = make([]byte, len(n.Hash))
copy(nc.Hash, n.Hash)
return nc
}
// IsBuiltIn returns true if the node pool is one of the built-in pools.
//
// Built-in node pools are created automatically by Nomad and can never be
// deleted or modified so they are always present in the cluster..
func (n *NodePool) IsBuiltIn() bool {
switch n.Name {
case NodePoolAll, NodePoolDefault:
return true
default:
return false
}
}
// MemoryOversubscriptionEnabled returns true if memory oversubscription is
// enabled in the node pool or in the global cluster configuration.
func (n *NodePool) MemoryOversubscriptionEnabled(global *SchedulerConfiguration) bool {
// Default to the global scheduler config.
memOversubEnabled := global != nil && global.MemoryOversubscriptionEnabled
// But overwrite it if the node pool also has it configured.
poolHasMemOversub := n != nil &&
n.SchedulerConfiguration != nil &&
n.SchedulerConfiguration.MemoryOversubscriptionEnabled != nil
if poolHasMemOversub {
memOversubEnabled = *n.SchedulerConfiguration.MemoryOversubscriptionEnabled
}
return memOversubEnabled
}
// SetHash is used to compute and set the hash of node pool
func (n *NodePool) SetHash() []byte {
// Initialize a 256bit Blake2 hash (32 bytes)
hash, err := blake2b.New256(nil)
if err != nil {
panic(err)
}
// Write all the user set fields
_, _ = hash.Write([]byte(n.Name))
_, _ = hash.Write([]byte(n.Description))
if n.SchedulerConfiguration != nil {
_, _ = hash.Write([]byte(n.SchedulerConfiguration.SchedulerAlgorithm))
memSub := n.SchedulerConfiguration.MemoryOversubscriptionEnabled
if memSub != nil {
if *memSub {
_, _ = hash.Write([]byte("memory_oversubscription_enabled"))
} else {
_, _ = hash.Write([]byte("memory_oversubscription_disabled"))
}
}
}
// sort keys to ensure hash stability when meta is stored later
var keys []string
for k := range n.Meta {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
_, _ = hash.Write([]byte(k))
_, _ = hash.Write([]byte(n.Meta[k]))
}
// Finalize the hash
hashVal := hash.Sum(nil)
// Set and return the hash
n.Hash = hashVal
return hashVal
}
// NodePoolSchedulerConfiguration is the scheduler confinguration applied to a
// node pool.
//
// When adding new values that should override global scheduler configuration,
// verify the scheduler handles the node pool configuration as well.
type NodePoolSchedulerConfiguration struct {
// SchedulerAlgorithm is the scheduling algorithm to use for the pool.
// If not defined, the global cluster scheduling algorithm is used.
SchedulerAlgorithm SchedulerAlgorithm `hcl:"scheduler_algorithm"`
// MemoryOversubscriptionEnabled specifies whether memory oversubscription
// is enabled. If not defined, the global cluster configuration is used.
MemoryOversubscriptionEnabled *bool `hcl:"memory_oversubscription_enabled"`
}
// Copy returns a deep copy of the node pool scheduler configuration.
func (n *NodePoolSchedulerConfiguration) Copy() *NodePoolSchedulerConfiguration {
if n == nil {
return nil
}
nc := new(NodePoolSchedulerConfiguration)
*nc = *n
if n.MemoryOversubscriptionEnabled != nil {
nc.MemoryOversubscriptionEnabled = pointer.Of(*n.MemoryOversubscriptionEnabled)
}
return nc
}
// NodePoolListRequest is used to list node pools.
type NodePoolListRequest struct {
QueryOptions
}
// NodePoolListResponse is the response to node pools list request.
type NodePoolListResponse struct {
NodePools []*NodePool
QueryMeta
}
// NodePoolSpecificRequest is used to make a request for a specific node pool.
type NodePoolSpecificRequest struct {
Name string
QueryOptions
}
// SingleNodePoolResponse is the response to a specific node pool request.
type SingleNodePoolResponse struct {
NodePool *NodePool
QueryMeta
}
// NodePoolUpsertRequest is used to make a request to insert or update a node
// pool.
type NodePoolUpsertRequest struct {
NodePools []*NodePool
WriteRequest
}
// NodePoolDeleteRequest is used to make a request to delete a node pool.
type NodePoolDeleteRequest struct {
Names []string
WriteRequest
}
// NodePoolNodesRequest is used to list all nodes that are part of a node pool.
type NodePoolNodesRequest struct {
Name string
Fields *NodeStubFields
QueryOptions
}
// NodePoolNodesResponse is used to return a list nodes in the node pool.
type NodePoolNodesResponse struct {
Nodes []*NodeListStub
QueryMeta
}
// NodePoolJobsRequest is used to make a request for the jobs in a specific node pool.
type NodePoolJobsRequest struct {
Name string
Fields *JobStubFields
QueryOptions
}
// NodePoolJobsResponse returns a list of jobs in a specific node pool.
type NodePoolJobsResponse struct {
Jobs []*JobListStub
QueryMeta
}