github.com/hernad/nomad@v1.6.112/nomad/structs/node_pool.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package structs
     5  
     6  import (
     7  	"fmt"
     8  	"regexp"
     9  	"sort"
    10  
    11  	"github.com/hashicorp/go-multierror"
    12  	"github.com/hernad/nomad/helper/pointer"
    13  	"golang.org/x/crypto/blake2b"
    14  	"golang.org/x/exp/maps"
    15  )
    16  
    17  const (
    18  	// NodePoolAll is a built-in node pool that always includes all nodes in
    19  	// the cluster.
    20  	NodePoolAll            = "all"
    21  	NodePoolAllDescription = "Node pool with all nodes in the cluster."
    22  
    23  	// NodePoolDefault is a built-in node pool for nodes that don't specify a
    24  	// node pool in their configuration.
    25  	NodePoolDefault            = "default"
    26  	NodePoolDefaultDescription = "Default node pool."
    27  
    28  	// maxNodePoolDescriptionLength is the maximum length allowed for a node
    29  	// pool description.
    30  	maxNodePoolDescriptionLength = 256
    31  )
    32  
    33  var (
    34  	// validNodePoolName is the rule used to validate a node pool name.
    35  	validNodePoolName = regexp.MustCompile("^[a-zA-Z0-9-_]{1,128}$")
    36  )
    37  
    38  // ValidadeNodePoolName returns an error if a node pool name is invalid.
    39  func ValidateNodePoolName(pool string) error {
    40  	if !validNodePoolName.MatchString(pool) {
    41  		return fmt.Errorf("invalid name %q, must match regex %s", pool, validNodePoolName)
    42  	}
    43  	return nil
    44  }
    45  
    46  // NodePool allows partioning infrastructure
    47  type NodePool struct {
    48  	// Name is the node pool name. It must be unique.
    49  	Name string
    50  
    51  	// Description is the human-friendly description of the node pool.
    52  	Description string
    53  
    54  	// Meta is a set of user-provided metadata for the node pool.
    55  	Meta map[string]string
    56  
    57  	// SchedulerConfiguration is the scheduler configuration specific to the
    58  	// node pool.
    59  	SchedulerConfiguration *NodePoolSchedulerConfiguration
    60  
    61  	// Hash is the hash of the node pool which is used to efficiently diff when
    62  	// we replicate pools across regions.
    63  	Hash []byte
    64  
    65  	// Raft indexes.
    66  	CreateIndex uint64
    67  	ModifyIndex uint64
    68  }
    69  
    70  // GetID implements the IDGetter interface required for pagination.
    71  func (n *NodePool) GetID() string {
    72  	return n.Name
    73  }
    74  
    75  // Validate returns an error if the node pool is invalid.
    76  func (n *NodePool) Validate() error {
    77  	var mErr *multierror.Error
    78  
    79  	mErr = multierror.Append(mErr, ValidateNodePoolName(n.Name))
    80  
    81  	if len(n.Description) > maxNodePoolDescriptionLength {
    82  		mErr = multierror.Append(mErr, fmt.Errorf("description longer than %d", maxNodePoolDescriptionLength))
    83  	}
    84  
    85  	mErr = multierror.Append(mErr, n.SchedulerConfiguration.Validate())
    86  
    87  	return mErr.ErrorOrNil()
    88  }
    89  
    90  // Copy returns a deep copy of the node pool.
    91  func (n *NodePool) Copy() *NodePool {
    92  	if n == nil {
    93  		return nil
    94  	}
    95  
    96  	nc := new(NodePool)
    97  	*nc = *n
    98  	nc.Meta = maps.Clone(nc.Meta)
    99  	nc.SchedulerConfiguration = nc.SchedulerConfiguration.Copy()
   100  
   101  	nc.Hash = make([]byte, len(n.Hash))
   102  	copy(nc.Hash, n.Hash)
   103  
   104  	return nc
   105  }
   106  
   107  // IsBuiltIn returns true if the node pool is one of the built-in pools.
   108  //
   109  // Built-in node pools are created automatically by Nomad and can never be
   110  // deleted or modified so they are always present in the cluster..
   111  func (n *NodePool) IsBuiltIn() bool {
   112  	switch n.Name {
   113  	case NodePoolAll, NodePoolDefault:
   114  		return true
   115  	default:
   116  		return false
   117  	}
   118  }
   119  
   120  // MemoryOversubscriptionEnabled returns true if memory oversubscription is
   121  // enabled in the node pool or in the global cluster configuration.
   122  func (n *NodePool) MemoryOversubscriptionEnabled(global *SchedulerConfiguration) bool {
   123  
   124  	// Default to the global scheduler config.
   125  	memOversubEnabled := global != nil && global.MemoryOversubscriptionEnabled
   126  
   127  	// But overwrite it if the node pool also has it configured.
   128  	poolHasMemOversub := n != nil &&
   129  		n.SchedulerConfiguration != nil &&
   130  		n.SchedulerConfiguration.MemoryOversubscriptionEnabled != nil
   131  	if poolHasMemOversub {
   132  		memOversubEnabled = *n.SchedulerConfiguration.MemoryOversubscriptionEnabled
   133  	}
   134  
   135  	return memOversubEnabled
   136  }
   137  
   138  // SetHash is used to compute and set the hash of node pool
   139  func (n *NodePool) SetHash() []byte {
   140  	// Initialize a 256bit Blake2 hash (32 bytes)
   141  	hash, err := blake2b.New256(nil)
   142  	if err != nil {
   143  		panic(err)
   144  	}
   145  
   146  	// Write all the user set fields
   147  	_, _ = hash.Write([]byte(n.Name))
   148  	_, _ = hash.Write([]byte(n.Description))
   149  	if n.SchedulerConfiguration != nil {
   150  		_, _ = hash.Write([]byte(n.SchedulerConfiguration.SchedulerAlgorithm))
   151  
   152  		memSub := n.SchedulerConfiguration.MemoryOversubscriptionEnabled
   153  		if memSub != nil {
   154  			if *memSub {
   155  				_, _ = hash.Write([]byte("memory_oversubscription_enabled"))
   156  			} else {
   157  				_, _ = hash.Write([]byte("memory_oversubscription_disabled"))
   158  			}
   159  		}
   160  	}
   161  
   162  	// sort keys to ensure hash stability when meta is stored later
   163  	var keys []string
   164  	for k := range n.Meta {
   165  		keys = append(keys, k)
   166  	}
   167  	sort.Strings(keys)
   168  
   169  	for _, k := range keys {
   170  		_, _ = hash.Write([]byte(k))
   171  		_, _ = hash.Write([]byte(n.Meta[k]))
   172  	}
   173  
   174  	// Finalize the hash
   175  	hashVal := hash.Sum(nil)
   176  
   177  	// Set and return the hash
   178  	n.Hash = hashVal
   179  	return hashVal
   180  }
   181  
   182  // NodePoolSchedulerConfiguration is the scheduler confinguration applied to a
   183  // node pool.
   184  //
   185  // When adding new values that should override global scheduler configuration,
   186  // verify the scheduler handles the node pool configuration as well.
   187  type NodePoolSchedulerConfiguration struct {
   188  
   189  	// SchedulerAlgorithm is the scheduling algorithm to use for the pool.
   190  	// If not defined, the global cluster scheduling algorithm is used.
   191  	SchedulerAlgorithm SchedulerAlgorithm `hcl:"scheduler_algorithm"`
   192  
   193  	// MemoryOversubscriptionEnabled specifies whether memory oversubscription
   194  	// is enabled. If not defined, the global cluster configuration is used.
   195  	MemoryOversubscriptionEnabled *bool `hcl:"memory_oversubscription_enabled"`
   196  }
   197  
   198  // Copy returns a deep copy of the node pool scheduler configuration.
   199  func (n *NodePoolSchedulerConfiguration) Copy() *NodePoolSchedulerConfiguration {
   200  	if n == nil {
   201  		return nil
   202  	}
   203  
   204  	nc := new(NodePoolSchedulerConfiguration)
   205  	*nc = *n
   206  
   207  	if n.MemoryOversubscriptionEnabled != nil {
   208  		nc.MemoryOversubscriptionEnabled = pointer.Of(*n.MemoryOversubscriptionEnabled)
   209  	}
   210  
   211  	return nc
   212  }
   213  
   214  // NodePoolListRequest is used to list node pools.
   215  type NodePoolListRequest struct {
   216  	QueryOptions
   217  }
   218  
   219  // NodePoolListResponse is the response to node pools list request.
   220  type NodePoolListResponse struct {
   221  	NodePools []*NodePool
   222  	QueryMeta
   223  }
   224  
   225  // NodePoolSpecificRequest is used to make a request for a specific node pool.
   226  type NodePoolSpecificRequest struct {
   227  	Name string
   228  	QueryOptions
   229  }
   230  
   231  // SingleNodePoolResponse is the response to a specific node pool request.
   232  type SingleNodePoolResponse struct {
   233  	NodePool *NodePool
   234  	QueryMeta
   235  }
   236  
   237  // NodePoolUpsertRequest is used to make a request to insert or update a node
   238  // pool.
   239  type NodePoolUpsertRequest struct {
   240  	NodePools []*NodePool
   241  	WriteRequest
   242  }
   243  
   244  // NodePoolDeleteRequest is used to make a request to delete a node pool.
   245  type NodePoolDeleteRequest struct {
   246  	Names []string
   247  	WriteRequest
   248  }
   249  
   250  // NodePoolNodesRequest is used to list all nodes that are part of a node pool.
   251  type NodePoolNodesRequest struct {
   252  	Name   string
   253  	Fields *NodeStubFields
   254  	QueryOptions
   255  }
   256  
   257  // NodePoolNodesResponse is used to return a list nodes in the node pool.
   258  type NodePoolNodesResponse struct {
   259  	Nodes []*NodeListStub
   260  	QueryMeta
   261  }
   262  
   263  // NodePoolJobsRequest is used to make a request for the jobs in a specific node pool.
   264  type NodePoolJobsRequest struct {
   265  	Name   string
   266  	Fields *JobStubFields
   267  	QueryOptions
   268  }
   269  
   270  // NodePoolJobsResponse returns a list of jobs in a specific node pool.
   271  type NodePoolJobsResponse struct {
   272  	Jobs []*JobListStub
   273  	QueryMeta
   274  }