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

     1  // Copyright (C) 2020, 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 resources
     5  
     6  import (
     7  	"crypto/rand"
     8  	"fmt"
     9  	"math/big"
    10  	"os"
    11  	"regexp"
    12  	"strconv"
    13  	"strings"
    14  
    15  	vmcontrollerv1 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/apis/vmcontroller/v1"
    16  	"github.com/verrazzano/verrazzano-monitoring-operator/pkg/config"
    17  	"github.com/verrazzano/verrazzano-monitoring-operator/pkg/constants"
    18  
    19  	corev1 "k8s.io/api/core/v1"
    20  	netv1 "k8s.io/api/networking/v1"
    21  	"k8s.io/apimachinery/pkg/api/resource"
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	"k8s.io/apimachinery/pkg/runtime/schema"
    24  	"k8s.io/apimachinery/pkg/util/intstr"
    25  )
    26  
    27  var (
    28  	runes = []rune("abcdefghijklmnopqrstuvwxyz0123456789")
    29  )
    30  
    31  const (
    32  	serviceClusterLocal     = ".svc.cluster.local"
    33  	masterHTTPEndpoint      = "VMO_MASTER_HTTP_ENDPOINT"
    34  	dashboardsHTTPEndpoint  = "VMO_DASHBOARDS_HTTP_ENDPOINT"
    35  	OpenSearchIngestCmdTmpl = `#!/usr/bin/env bash -e
    36  	set -euo pipefail
    37      %s
    38  	/usr/local/bin/docker-entrypoint.sh`
    39  	OpenSearchDashboardCmdTmpl = `#!/usr/bin/env bash -e
    40      %s
    41  	/usr/local/bin/opensearch-dashboards-docker`
    42  	containerCmdTmpl = `#!/usr/bin/env bash -e
    43  	# Updating elastic search keystore with keys
    44  	# required for the repository-s3 plugin
    45  	if [ "${OBJECT_STORE_ACCESS_KEY_ID:-}" ]; then
    46  		echo "Updating object store access key..."
    47  		echo $OBJECT_STORE_ACCESS_KEY_ID | /usr/share/opensearch/bin/opensearch-keystore add --stdin --force s3.client.default.access_key;
    48  	fi
    49  	if [ "${OBJECT_STORE_SECRET_KEY_ID:-}" ]; then
    50  		echo "Updating object store secret key..."
    51  		echo $OBJECT_STORE_SECRET_KEY_ID | /usr/share/opensearch/bin/opensearch-keystore add --stdin --force s3.client.default.secret_key;
    52  	fi
    53  	
    54  	%s
    55  
    56      %s 
    57  	
    58  	/usr/local/bin/docker-entrypoint.sh`
    59  
    60  	jvmOptsDisableCmd = `
    61  	# Disable the jvm heap settings in jvm.options
    62  	echo "Commenting out java heap settings in jvm.options..."
    63  	sed -i -e '/^-Xms/s/^/#/g' -e '/^-Xmx/s/^/#/g' config/jvm.options
    64  	`
    65  	OSPluginsInstallTmpl = `
    66       set -euo pipefail
    67       # Install OS plugins that are not bundled with OS
    68       %s
    69      `
    70  	OSPluginsInstallCmd = `
    71      /usr/share/opensearch/bin/opensearch-plugin install -b %s
    72  	`
    73  	OSDashboardPluginsInstallCmd = `
    74      /usr/share/opensearch-dashboards/bin/opensearch-dashboards-plugin install %s
    75  	`
    76  )
    77  
    78  // CopyImmutableEnvVars copies the initial master node environment variable from an existing container to an expected container
    79  // cluster.initial_master_nodes shouldn't be changed after it's set.
    80  func CopyImmutableEnvVars(expected, existing []corev1.Container, containerName string) {
    81  	getContainer := func(containers []corev1.Container) (int, *corev1.Container) {
    82  		for idx, c := range containers {
    83  			if c.Name == containerName {
    84  				return idx, &c
    85  			}
    86  		}
    87  		return -1, nil
    88  	}
    89  
    90  	// Initial master nodes should not change
    91  	idx, currentContainer := getContainer(expected)
    92  	_, existingContainer := getContainer(existing)
    93  	if currentContainer == nil || existingContainer == nil {
    94  		return
    95  	}
    96  
    97  	getAndSetVar := func(varName string) {
    98  		envVar := GetEnvVar(existingContainer, varName)
    99  		if envVar != nil {
   100  			SetEnvVar(currentContainer, envVar)
   101  		}
   102  	}
   103  
   104  	getAndSetVar(constants.ClusterInitialMasterNodes)
   105  	getAndSetVar("node.roles")
   106  	expected[idx] = *currentContainer
   107  }
   108  
   109  // GetEnvVar retrieves a container EnvVar if it is present
   110  func GetEnvVar(container *corev1.Container, name string) *corev1.EnvVar {
   111  	for _, envVar := range container.Env {
   112  		if envVar.Name == name {
   113  			return &envVar
   114  		}
   115  	}
   116  	return nil
   117  }
   118  
   119  // SetEnvVar sets a container EnvVar, overriding if it was laready present
   120  func SetEnvVar(container *corev1.Container, envVar *corev1.EnvVar) {
   121  	for idx, env := range container.Env {
   122  		if env.Name == envVar.Name {
   123  			container.Env[idx] = *envVar
   124  			return
   125  		}
   126  	}
   127  	container.Env = append(container.Env, *envVar)
   128  }
   129  
   130  func GetOpenSearchHTTPEndpoint(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) string {
   131  	// The master HTTP port may be overridden if necessary.
   132  	// This can be useful in situations where the VMO does not have direct access to the cluster service,
   133  	// such as when you are using port-forwarding.
   134  	masterServiceEndpoint := os.Getenv(masterHTTPEndpoint)
   135  	if len(masterServiceEndpoint) > 0 {
   136  		return masterServiceEndpoint
   137  	}
   138  	return fmt.Sprintf("http://%s-http.%s%s:%d",
   139  		GetMetaName(vmo.Name, config.ElasticsearchMaster.Name),
   140  		vmo.Namespace,
   141  		serviceClusterLocal,
   142  		constants.OSHTTPPort)
   143  }
   144  
   145  func GetOpenSearchDashboardsHTTPEndpoint(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) string {
   146  	dashboardsServiceEndpoint := os.Getenv(dashboardsHTTPEndpoint)
   147  	if len(dashboardsServiceEndpoint) > 0 {
   148  		return dashboardsServiceEndpoint
   149  	}
   150  	return fmt.Sprintf("http://%s.%s%s:%d", GetMetaName(vmo.Name, config.OpenSearchDashboards.Name),
   151  		vmo.Namespace,
   152  		serviceClusterLocal,
   153  		constants.OSDashboardsHTTPPort)
   154  }
   155  
   156  func GetOwnerLabels(owner string) map[string]string {
   157  	return map[string]string{
   158  		"owner": owner,
   159  	}
   160  }
   161  
   162  // GetNewRandomID generates a random alphanumeric string of the format [a-z0-9]{size}
   163  func GetNewRandomID(size int) (string, error) {
   164  	builder := strings.Builder{}
   165  	for i := 0; i < size; i++ {
   166  		idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(runes))))
   167  		if err != nil {
   168  			return "", err
   169  		}
   170  		builder.WriteRune(runes[idx.Int64()])
   171  	}
   172  	return builder.String(), nil
   173  }
   174  
   175  // GetMetaName returns name
   176  func GetMetaName(vmoName string, componentName string) string {
   177  	return constants.VMOServiceNamePrefix + vmoName + "-" + componentName
   178  }
   179  
   180  // GetMetaLabels returns k8s-app and vmo lables
   181  func GetMetaLabels(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) map[string]string {
   182  	return map[string]string{constants.K8SAppLabel: constants.VMOGroup, constants.VMOLabel: vmo.Name}
   183  }
   184  
   185  // GetCompLabel returns a component value for opensearch
   186  func GetCompLabel(componentName string) string {
   187  	var componentLabelValue string
   188  	switch componentName {
   189  	case config.ElasticsearchMaster.Name, config.ElasticsearchData.Name, config.ElasticsearchIngest.Name, config.OpensearchIngest.Name:
   190  		componentLabelValue = constants.ComponentOpenSearchValue
   191  	default:
   192  		componentLabelValue = componentName
   193  	}
   194  	return componentLabelValue
   195  }
   196  
   197  // DeepCopyMap performs a deepcopy of a map
   198  func DeepCopyMap(srcMap map[string]string) map[string]string {
   199  	result := make(map[string]string, len(srcMap))
   200  	for k, v := range srcMap {
   201  		result[k] = v
   202  	}
   203  	return result
   204  }
   205  
   206  // GetSpecID returns app label
   207  func GetSpecID(vmoName string, componentName string) map[string]string {
   208  	return map[string]string{constants.ServiceAppLabel: vmoName + "-" + componentName}
   209  }
   210  
   211  // GetServicePort returns service port
   212  func GetServicePort(componentDetails config.ComponentDetails) corev1.ServicePort {
   213  	return corev1.ServicePort{Name: "http-" + componentDetails.Name, Port: int32(componentDetails.Port)}
   214  }
   215  
   216  // GetOwnerReferences returns owner references
   217  func GetOwnerReferences(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) []metav1.OwnerReference {
   218  	var ownerReferences []metav1.OwnerReference
   219  	if vmo.Spec.CascadingDelete {
   220  		ownerReferences = []metav1.OwnerReference{
   221  			*metav1.NewControllerRef(vmo, schema.GroupVersionKind{
   222  				Group:   vmcontrollerv1.SchemeGroupVersion.Group,
   223  				Version: vmcontrollerv1.SchemeGroupVersion.Version,
   224  				Kind:    constants.VMOKind,
   225  			}),
   226  		}
   227  	}
   228  	return ownerReferences
   229  }
   230  
   231  // SliceContains returns whether or not the given slice contains the given string
   232  func SliceContains(slice []string, value string) bool {
   233  	for _, a := range slice {
   234  		if a == value {
   235  			return true
   236  		}
   237  	}
   238  	return false
   239  }
   240  
   241  // GetStorageElementForComponent returns storage for a given component
   242  func GetStorageElementForComponent(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance, component *config.ComponentDetails) (storage *vmcontrollerv1.Storage) {
   243  	switch component.Name {
   244  	case config.Grafana.Name:
   245  		return &vmo.Spec.Grafana.Storage
   246  	case config.ElasticsearchData.Name:
   247  		return vmo.Spec.Elasticsearch.DataNode.Storage
   248  	}
   249  	return nil
   250  }
   251  
   252  // GetReplicasForComponent returns number of replicas for a given component
   253  func GetReplicasForComponent(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance, component *config.ComponentDetails) (replicas int32) {
   254  	switch component.Name {
   255  	case config.Grafana.Name:
   256  		return int32(1)
   257  	case config.ElasticsearchData.Name:
   258  		return vmo.Spec.Elasticsearch.DataNode.Replicas
   259  	}
   260  	return 0
   261  }
   262  
   263  // GetNextStringInSequence returns the next string in the incremental sequence given a string
   264  func GetNextStringInSequence(name string) string {
   265  	tokens := strings.Split(name, "-")
   266  	if len(tokens) < 2 {
   267  		return name + "-1" // Starting a new sequence
   268  	}
   269  	number, err := strconv.Atoi(tokens[len(tokens)-1])
   270  	if err != nil {
   271  		return name + "-1" // Starting a new sequence
   272  	}
   273  	tokens[len(tokens)-1] = strconv.Itoa(number + 1)
   274  	return strings.Join(tokens, "-")
   275  }
   276  
   277  // CreateContainerElement creates a generic container element for the given component of the given VMO object.
   278  func CreateContainerElement(vmoStorage *vmcontrollerv1.Storage,
   279  	vmoResources *vmcontrollerv1.Resources, componentDetails config.ComponentDetails) corev1.Container {
   280  
   281  	var volumeMounts []corev1.VolumeMount
   282  	if vmoStorage != nil && vmoStorage.PvcNames != nil && vmoStorage.Size != "" {
   283  		volumeMounts = append(volumeMounts, corev1.VolumeMount{MountPath: componentDetails.DataDir, Name: constants.StorageVolumeName})
   284  	}
   285  
   286  	limitResourceList := corev1.ResourceList{}
   287  	requestResourceList := corev1.ResourceList{}
   288  	if vmoResources != nil {
   289  		if vmoResources.LimitCPU != "" {
   290  			limitResourceList[corev1.ResourceCPU] = resource.MustParse(vmoResources.LimitCPU)
   291  		}
   292  		if vmoResources.LimitMemory != "" {
   293  			limitResourceList[corev1.ResourceMemory] = resource.MustParse(vmoResources.LimitMemory)
   294  		}
   295  		if vmoResources.RequestCPU != "" {
   296  			requestResourceList[corev1.ResourceCPU] = resource.MustParse(vmoResources.RequestCPU)
   297  		}
   298  		if vmoResources.RequestMemory != "" {
   299  			requestResourceList[corev1.ResourceMemory] = resource.MustParse(vmoResources.RequestMemory)
   300  		}
   301  	}
   302  
   303  	var livenessProbe *corev1.Probe
   304  	if componentDetails.LivenessHTTPPath != "" {
   305  		livenessProbe = &corev1.Probe{
   306  			ProbeHandler: corev1.ProbeHandler{
   307  				HTTPGet: &corev1.HTTPGetAction{
   308  					Path:   componentDetails.LivenessHTTPPath,
   309  					Port:   intstr.IntOrString{IntVal: int32(componentDetails.Port)},
   310  					Scheme: "HTTP",
   311  				},
   312  			},
   313  		}
   314  	}
   315  
   316  	var readinessProbe *corev1.Probe
   317  	if componentDetails.ReadinessHTTPPath != "" {
   318  		readinessProbe = &corev1.Probe{
   319  			ProbeHandler: corev1.ProbeHandler{
   320  				HTTPGet: &corev1.HTTPGetAction{
   321  					Path:   componentDetails.ReadinessHTTPPath,
   322  					Port:   intstr.IntOrString{IntVal: int32(componentDetails.Port)},
   323  					Scheme: "HTTP",
   324  				},
   325  			},
   326  		}
   327  	}
   328  	return corev1.Container{
   329  		Name:            componentDetails.Name,
   330  		Image:           componentDetails.Image,
   331  		ImagePullPolicy: constants.DefaultImagePullPolicy,
   332  		SecurityContext: &corev1.SecurityContext{
   333  			Privileged: &componentDetails.Privileged,
   334  		},
   335  		Ports: []corev1.ContainerPort{{Name: componentDetails.Name, ContainerPort: int32(componentDetails.Port)}},
   336  		Resources: corev1.ResourceRequirements{
   337  			Requests: requestResourceList,
   338  			Limits:   limitResourceList,
   339  		},
   340  		VolumeMounts:   volumeMounts,
   341  		LivenessProbe:  livenessProbe,
   342  		ReadinessProbe: readinessProbe,
   343  	}
   344  }
   345  
   346  // CreateZoneAntiAffinityElement return an Affinity resource for a given VMO instance and component
   347  func CreateZoneAntiAffinityElement(vmoName string, component string) *corev1.Affinity {
   348  	return &corev1.Affinity{
   349  		PodAntiAffinity: &corev1.PodAntiAffinity{
   350  			PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{
   351  				{
   352  					Weight: 100,
   353  					PodAffinityTerm: corev1.PodAffinityTerm{
   354  						LabelSelector: &metav1.LabelSelector{
   355  							MatchLabels: GetSpecID(vmoName, component),
   356  						},
   357  						TopologyKey: constants.K8sZoneLabel,
   358  					},
   359  				},
   360  			},
   361  		},
   362  	}
   363  }
   364  
   365  // GetElasticsearchMasterInitContainer return an Elasticsearch Init container for the master.  This changes ownership of
   366  // the ES directory permissions needed to access PV volume data.  Also set the max map count.
   367  func GetElasticsearchMasterInitContainer() *corev1.Container {
   368  	elasticsearchInitContainer := CreateContainerElement(nil, nil, config.ElasticsearchInit)
   369  	elasticsearchInitContainer.Command =
   370  		[]string{"sh", "-c", "chown -R 1000:1000 /usr/share/opensearch/data; sysctl -w vm.max_map_count=262144"}
   371  	elasticsearchInitContainer.Ports = nil
   372  	return &elasticsearchInitContainer
   373  }
   374  
   375  // GetElasticsearchInitContainer returns an Elasticsearch Init container object
   376  func GetElasticsearchInitContainer() *corev1.Container {
   377  	elasticsearchInitContainer := CreateContainerElement(nil, nil, config.ElasticsearchInit)
   378  	elasticsearchInitContainer.Args = []string{"sysctl", "-w", "vm.max_map_count=262144"}
   379  	elasticsearchInitContainer.Ports = nil
   380  	return &elasticsearchInitContainer
   381  }
   382  
   383  // NewVal return a pointer to an int32 given an int32 value
   384  func NewVal(value int32) *int32 {
   385  	var val = value
   386  	return &val
   387  }
   388  
   389  // New64Val return a pointer to an int64 given an int64 value
   390  func New64Val(value int64) *int64 {
   391  	var val = value
   392  	return &val
   393  }
   394  
   395  // oidcProxyName returns OIDC Proxy name of the component. ex. es-ingest-oidc
   396  func oidcProxyName(componentName string) string {
   397  	return componentName + "-" + config.OidcProxy.Name
   398  }
   399  
   400  // OidcProxyMetaName returns OIDC Proxy meta name of the component. ex. vmi-system-es-ingest-oidc
   401  func OidcProxyMetaName(vmoName string, component string) string {
   402  	return GetMetaName(vmoName, oidcProxyName(component))
   403  }
   404  
   405  // AuthProxyMetaName returns Auth Proxy service name
   406  // TESTING: should be passed in from hel chart as s
   407  func AuthProxyMetaName() string {
   408  	return os.Getenv("AUTH_PROXY_SERVICE_NAME")
   409  }
   410  
   411  // AuthProxyMetaName returns Auth Proxy service name
   412  func AuthProxyPort() string {
   413  	return os.Getenv("AUTH_PROXY_SERVICE_PORT")
   414  }
   415  
   416  // OidcProxyConfigName returns OIDC Proxy ConfigMap name of the component. ex. vmi-system-es-ingest-oidc-config
   417  func OidcProxyConfigName(vmo string, component string) string {
   418  	return OidcProxyMetaName(vmo, component) + "-config"
   419  }
   420  
   421  // OidcProxyIngressHost returns OIDC Proxy ingress host.
   422  func OidcProxyIngressHost(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance, component *config.ComponentDetails) string {
   423  	host := component.Name
   424  	if component.EndpointName != "" {
   425  		host = component.EndpointName
   426  	}
   427  	return fmt.Sprintf("%s.%s", host, vmo.Spec.URI)
   428  }
   429  
   430  // CreateOidcProxy creates OpenID Connect (OIDC) proxy container and config Volume
   431  func CreateOidcProxy(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance, vmoResources *vmcontrollerv1.Resources, component *config.ComponentDetails) ([]corev1.Volume, *corev1.Container) {
   432  	var volumes []corev1.Volume
   433  	configName := OidcProxyConfigName(vmo.Name, component.Name)
   434  	var defaultMode int32 = 0755
   435  	configVolume := corev1.Volume{Name: configName, VolumeSource: corev1.VolumeSource{
   436  		ConfigMap: &corev1.ConfigMapVolumeSource{
   437  			LocalObjectReference: corev1.LocalObjectReference{Name: configName},
   438  			DefaultMode:          &defaultMode,
   439  		},
   440  	}}
   441  	oidcProxContainer := CreateContainerElement(nil, vmoResources, *component.OidcProxy)
   442  	oidcProxContainer.Command = []string{"/bootstrap/startup.sh"}
   443  	oidcProxContainer.VolumeMounts = []corev1.VolumeMount{{Name: configName, MountPath: "/bootstrap"}}
   444  	if len(vmo.Labels[constants.ClusterNameData]) > 0 {
   445  		secretVolume := corev1.Volume{Name: "secret", VolumeSource: corev1.VolumeSource{
   446  			Secret: &corev1.SecretVolumeSource{
   447  				SecretName: constants.MCRegistrationSecret,
   448  			},
   449  		}}
   450  		volumes = append(volumes, secretVolume)
   451  		oidcProxContainer.VolumeMounts = append(oidcProxContainer.VolumeMounts, corev1.VolumeMount{Name: "secret", MountPath: "/secret"})
   452  	}
   453  	volumes = append(volumes, configVolume)
   454  	return volumes, &oidcProxContainer
   455  }
   456  
   457  // getIngressRule returns the ingressRule with the provided ingress host
   458  func GetIngressRule(ingressHost string) netv1.IngressRule {
   459  	ingressRule := netv1.IngressRule{
   460  		Host: ingressHost,
   461  		IngressRuleValue: netv1.IngressRuleValue{
   462  			HTTP: &netv1.HTTPIngressRuleValue{
   463  				Paths: []netv1.HTTPIngressPath{
   464  					{
   465  						Path: "/()(.*)",
   466  						Backend: netv1.IngressBackend{
   467  							Service: &netv1.IngressServiceBackend{
   468  								Port: netv1.ServiceBackendPort{
   469  									Number: int32(8775),
   470  								},
   471  							},
   472  						},
   473  					},
   474  				},
   475  			},
   476  		},
   477  	}
   478  	return ingressRule
   479  }
   480  
   481  // OidcProxyService creates OidcProxy Service
   482  func OidcProxyService(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance, component *config.ComponentDetails) *corev1.Service {
   483  	return &corev1.Service{
   484  		ObjectMeta: metav1.ObjectMeta{
   485  			Labels:          GetMetaLabels(vmo),
   486  			Name:            OidcProxyMetaName(vmo.Name, component.Name),
   487  			Namespace:       vmo.Namespace,
   488  			OwnerReferences: GetOwnerReferences(vmo),
   489  		},
   490  		Spec: corev1.ServiceSpec{
   491  			Type:     vmo.Spec.ServiceType,
   492  			Selector: GetSpecID(vmo.Name, component.Name),
   493  			Ports:    []corev1.ServicePort{{Name: "oidc", Port: int32(constants.OidcProxyPort)}},
   494  		},
   495  	}
   496  }
   497  
   498  // convertToRegexp converts index pattern to a regular expression pattern.
   499  func ConvertToRegexp(pattern string) string {
   500  	var result strings.Builder
   501  	// Add ^ at the beginning
   502  	result.WriteString("^")
   503  	for i, literal := range strings.Split(pattern, "*") {
   504  
   505  		// Replace * with .*
   506  		if i > 0 {
   507  			result.WriteString(".*")
   508  		}
   509  
   510  		// Quote any regular expression meta characters in the
   511  		// literal text.
   512  		result.WriteString(regexp.QuoteMeta(literal))
   513  	}
   514  	// Add $ at the end
   515  	result.WriteString("$")
   516  	return result.String()
   517  }
   518  
   519  // CreateOpenSearchContainerCMD creates the CMD for OpenSearch containers.
   520  // The resulting CMD contains
   521  // command to comment java heap settings in config/jvm/options if input javaOpts is non-empty
   522  // OS plugins installation commands if OpenSearch plugins are provided
   523  // and contains java min/max heap settings
   524  func CreateOpenSearchContainerCMD(javaOpts string, plugins []string) string {
   525  	pluginsInstallTmpl := GetOSPluginsInstallTmpl(plugins, OSPluginsInstallCmd)
   526  	if javaOpts != "" {
   527  		jvmOptsPair := strings.Split(javaOpts, " ")
   528  		minHeapMemory := ""
   529  		maxHeapMemory := ""
   530  		for _, opt := range jvmOptsPair {
   531  			if strings.HasPrefix(opt, "-Xms") {
   532  				minHeapMemory = opt
   533  			}
   534  
   535  			if strings.HasPrefix(opt, "-Xmx") {
   536  				maxHeapMemory = opt
   537  			}
   538  		}
   539  
   540  		if minHeapMemory != "" && maxHeapMemory != "" {
   541  			return fmt.Sprintf(containerCmdTmpl, jvmOptsDisableCmd, pluginsInstallTmpl)
   542  		}
   543  	}
   544  
   545  	return fmt.Sprintf(containerCmdTmpl, "", pluginsInstallTmpl)
   546  }
   547  
   548  // GetOpenSearchPluginList retrieves the list of plugins provided in the VMI CRD for OpenSearch.
   549  // GIVEN VMI CRD
   550  // RETURN the list of provided os plugins. If there is no plugins in VMI CRD, empty list is returned.
   551  func GetOpenSearchPluginList(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) []string {
   552  	if vmo.Spec.Elasticsearch.Enabled &&
   553  		vmo.Spec.Elasticsearch.Plugins.Enabled &&
   554  		len(vmo.Spec.Elasticsearch.Plugins.InstallList) > 0 {
   555  		return vmo.Spec.Elasticsearch.Plugins.InstallList
   556  	}
   557  	return []string{}
   558  }
   559  
   560  // GetOSDashboardPluginList retrieves the list of plugins provided in the VMI CRD for OpenSearch dashboard.
   561  // GIVEN VMI CRD
   562  // RETURN the list of provided OSD plugins. If there is no plugin in VMI CRD, an empty list is returned.
   563  func GetOSDashboardPluginList(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) []string {
   564  	if vmo.Spec.Kibana.Enabled &&
   565  		vmo.Spec.Kibana.Plugins.Enabled &&
   566  		len(vmo.Spec.Kibana.Plugins.InstallList) > 0 {
   567  		return vmo.Spec.Kibana.Plugins.InstallList
   568  	}
   569  	return []string{}
   570  }
   571  
   572  // GetOSPluginsInstallTmpl returns the OSPluginsInstallTmpl by updating it with the given plugins and plugins installation cmd.
   573  func GetOSPluginsInstallTmpl(plugins []string, osPluginInstallCmd string) string {
   574  	var pluginsInstallTmpl string
   575  	for _, plugin := range plugins {
   576  		pluginsInstallTmpl += fmt.Sprintf(OSPluginsInstallTmpl, fmt.Sprintf(osPluginInstallCmd, plugin))
   577  	}
   578  	return pluginsInstallTmpl
   579  }