github.com/verrazzano/verrazzano-monitoring-operator@v0.0.30/pkg/resources/nodes/nodes.go (about)

     1  // Copyright (C) 2022, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package nodes
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	vmcontrollerv1 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/apis/vmcontroller/v1"
    10  	"github.com/verrazzano/verrazzano-monitoring-operator/pkg/constants"
    11  	"github.com/verrazzano/verrazzano-monitoring-operator/pkg/resources"
    12  	"strings"
    13  )
    14  
    15  type NodeCount struct {
    16  	// amount of nodes with 'master' role
    17  	MasterNodes int32
    18  	// amount of nodes with 'ingest' role
    19  	IngestNodes int32
    20  	// amount of nodes with 'data' role
    21  	DataNodes int32
    22  	// sum of node replicas
    23  	// this may be greater than the sum of master, data, and ingest, since nodes may have 1-3 roles.
    24  	Replicas int32
    25  }
    26  
    27  var (
    28  	RoleMaster   = GetRoleLabel(vmcontrollerv1.MasterRole)
    29  	RoleData     = GetRoleLabel(vmcontrollerv1.DataRole)
    30  	RoleIngest   = GetRoleLabel(vmcontrollerv1.IngestRole)
    31  	RoleAssigned = "true"
    32  )
    33  
    34  // MasterNodes returns the list of master role containing nodes in the VMI spec. These nodes will be created as statefulsets.
    35  func MasterNodes(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) []vmcontrollerv1.ElasticsearchNode {
    36  	return append([]vmcontrollerv1.ElasticsearchNode{vmo.Spec.Elasticsearch.MasterNode}, filterNodes(vmo, masterNodeMatcher)...)
    37  }
    38  
    39  // DataNodes returns the list of data nodes (that are not masters) in the VMI spec.
    40  func DataNodes(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) []vmcontrollerv1.ElasticsearchNode {
    41  	return append([]vmcontrollerv1.ElasticsearchNode{vmo.Spec.Elasticsearch.DataNode}, filterNodes(vmo, dataNodeMatcher)...)
    42  }
    43  
    44  // IngestNodes returns the list of ingest nodes in the VMI spec. These nodes will have no other role but ingest.
    45  func IngestNodes(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) []vmcontrollerv1.ElasticsearchNode {
    46  	return append([]vmcontrollerv1.ElasticsearchNode{vmo.Spec.Elasticsearch.IngestNode}, filterNodes(vmo, ingestNodeMatcher)...)
    47  }
    48  
    49  // GetRolesString turns a nodes role list into a role string
    50  // roles: [master, ingest, data] => "master,ingest,data"
    51  // we have to use a buffer because NodeRole is a type alias
    52  func GetRolesString(node *vmcontrollerv1.ElasticsearchNode) string {
    53  	var buf bytes.Buffer
    54  	for idx, role := range node.Roles {
    55  		buf.WriteString(string(role))
    56  		if idx < len(node.Roles)-1 {
    57  			buf.WriteString(",")
    58  		}
    59  	}
    60  	return buf.String()
    61  }
    62  
    63  func GetRoleLabel(role vmcontrollerv1.NodeRole) string {
    64  	return fmt.Sprintf("opensearch.%s/role-%s", constants.VMOGroup, string(role))
    65  }
    66  
    67  // SetNodeRoleLabels adds node role labels to an existing label map
    68  // role labels follow the format: opensearch.verrazzano.io/role-<role name>=true
    69  func SetNodeRoleLabels(node *vmcontrollerv1.ElasticsearchNode, labels map[string]string) {
    70  	for _, role := range node.Roles {
    71  		labels[GetRoleLabel(role)] = RoleAssigned
    72  	}
    73  }
    74  
    75  // IsSingleNodeCluster Returns true if only a single master node is requested; single-node ES cluster
    76  func IsSingleNodeCluster(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) bool {
    77  	nodeCount := GetNodeCount(vmo)
    78  	return nodeCount.MasterNodes == 1 && nodeCount.Replicas == 1
    79  }
    80  
    81  // GetNodeCount returns a struct containing the count of nodes of each role type, and the sum of all node replicas.
    82  func GetNodeCount(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) *NodeCount {
    83  	nodeCount := &NodeCount{}
    84  	for _, node := range AllNodes(vmo) {
    85  		nodeCount.Replicas += node.Replicas
    86  		for _, role := range node.Roles {
    87  			switch role {
    88  			case vmcontrollerv1.IngestRole:
    89  				nodeCount.IngestNodes += node.Replicas
    90  			case vmcontrollerv1.DataRole:
    91  				nodeCount.DataNodes += node.Replicas
    92  			default:
    93  				nodeCount.MasterNodes += node.Replicas
    94  			}
    95  		}
    96  	}
    97  	return nodeCount
    98  }
    99  
   100  // InitialMasterNodes returns a comma separated list of master nodes for cluster bootstrapping
   101  func InitialMasterNodes(vmoName string, masterNodes []vmcontrollerv1.ElasticsearchNode) string {
   102  	var j int32
   103  	var initialMasterNodes []string
   104  	for _, node := range masterNodes {
   105  		for j = 0; j < node.Replicas; j++ {
   106  			initialMasterNodes = append(initialMasterNodes, resources.GetMetaName(vmoName, node.Name)+"-"+fmt.Sprintf("%d", j))
   107  		}
   108  	}
   109  	return strings.Join(initialMasterNodes, ",")
   110  }
   111  
   112  // AllNodes returns a list of all nodes that need to be created
   113  func AllNodes(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) []vmcontrollerv1.ElasticsearchNode {
   114  	return append(vmo.Spec.Elasticsearch.Nodes, vmo.Spec.Elasticsearch.MasterNode, vmo.Spec.Elasticsearch.DataNode, vmo.Spec.Elasticsearch.IngestNode)
   115  }
   116  
   117  func filterNodes(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance, matcher func(node vmcontrollerv1.ElasticsearchNode) bool) []vmcontrollerv1.ElasticsearchNode {
   118  	var nodes []vmcontrollerv1.ElasticsearchNode
   119  	for _, node := range vmo.Spec.Elasticsearch.Nodes {
   120  		if matcher(node) {
   121  			nodes = append(nodes, node)
   122  		}
   123  	}
   124  	return nodes
   125  }
   126  
   127  func matcherFactory(excluded, matched func(role vmcontrollerv1.NodeRole) bool) func(node vmcontrollerv1.ElasticsearchNode) bool {
   128  	return func(node vmcontrollerv1.ElasticsearchNode) bool {
   129  		var isMatch bool
   130  		for _, role := range node.Roles {
   131  			if excluded(role) {
   132  				return false
   133  			}
   134  			if matched(role) {
   135  				isMatch = true
   136  			}
   137  		}
   138  		return isMatch
   139  	}
   140  }
   141  
   142  var (
   143  	// matches any node with master role
   144  	masterNodeMatcher = matcherFactory(func(role vmcontrollerv1.NodeRole) bool {
   145  		return false
   146  	}, func(role vmcontrollerv1.NodeRole) bool {
   147  		return role == vmcontrollerv1.MasterRole
   148  	})
   149  	// matches nodes with data role, or data + ingest
   150  	dataNodeMatcher = matcherFactory(func(role vmcontrollerv1.NodeRole) bool {
   151  		return role == vmcontrollerv1.MasterRole
   152  	}, func(role vmcontrollerv1.NodeRole) bool {
   153  		return role == vmcontrollerv1.DataRole
   154  	})
   155  	// Matches only nodes who have ingest role, and nothing else
   156  	ingestNodeMatcher = matcherFactory(func(role vmcontrollerv1.NodeRole) bool {
   157  		return role == vmcontrollerv1.MasterRole || role == vmcontrollerv1.DataRole
   158  	}, func(role vmcontrollerv1.NodeRole) bool {
   159  		return role == vmcontrollerv1.IngestRole
   160  	})
   161  )