pkg/resources/statefulset/mgmd_statefulset.go (142 lines of code) (raw):
// Copyright (c) 2022, 2023, Oracle and/or its affiliates.
//
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/
package statefulset
import (
"github.com/mysql/ndb-operator/config/debug"
v1 "github.com/mysql/ndb-operator/pkg/apis/ndbcontroller/v1"
"github.com/mysql/ndb-operator/pkg/constants"
"github.com/mysql/ndb-operator/pkg/ndbconfig"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
const (
// config.ini volume and mount path for the management pods
mgmdConfigIniVolumeName = constants.NdbNodeTypeMgmd + "-config-volume"
mgmdConfigIniMountPath = constants.DataDir + "/config"
)
var (
// Ports to be exposed by the container and service
mgmdPorts = []int32{1186}
)
// mgmdStatefulSet implements the NdbStatefulSetInterface to control a set of management nodes
type mgmdStatefulSet struct {
baseStatefulSet
}
func (mss *mgmdStatefulSet) NewGoverningService(nc *v1.NdbCluster) *corev1.Service {
return newService(nc, mgmdPorts, mss.nodeType, false,
nc.Spec.ManagementNode != nil && nc.Spec.ManagementNode.EnableLoadBalancer)
}
// getPodVolumes returns a slice of volumes to be
// made available to the management server pods.
func (mss *mgmdStatefulSet) getPodVolumes(nc *v1.NdbCluster) []corev1.Volume {
return []corev1.Volume{
// Empty Dir volume for the mgmd data dir
*mss.getEmptyDirPodVolume(mss.getDataDirVolumeName()),
// Load the config.ini script via a volume
{
Name: mgmdConfigIniVolumeName,
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: nc.GetConfigMapName(),
},
// Load only the config.ini key
Items: []corev1.KeyToPath{
{
Key: constants.ConfigIniKey,
Path: constants.ConfigIniKey,
},
},
},
},
},
// Load the helper scripts from
// the configmap into the pod via a volume
{
Name: helperScriptsVolName,
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: nc.GetConfigMapName(),
},
DefaultMode: &ownerCanExecMode,
Items: []corev1.KeyToPath{
{
// Load the startup probe
Key: constants.MgmdStartupProbeScript,
Path: constants.MgmdStartupProbeScript,
},
},
},
},
},
}
}
// getVolumeMounts returns the volumes to be mounted to the mgmd containers
func (mss *mgmdStatefulSet) getVolumeMounts() []corev1.VolumeMount {
return []corev1.VolumeMount{
// Append the empty dir volume mount to be used as a data dir
{
Name: mss.getDataDirVolumeName(),
MountPath: dataDirectoryMountPath,
},
// Mount the config map volume holding the MySQL Cluster configuration
{
Name: mgmdConfigIniVolumeName,
MountPath: mgmdConfigIniMountPath,
},
// Mount the helper scripts
mss.getHelperScriptVolumeMount(),
// Mount the work dir volume
mss.getWorkDirVolumeMount(),
}
}
// getContainers returns the containers to run a Management Node
func (mss *mgmdStatefulSet) getContainers(nc *v1.NdbCluster) []corev1.Container {
// Command and args to run the management server
cmdAndArgs := []string{
"/usr/sbin/ndb_mgmd",
"-f", mgmdConfigIniMountPath + "/config.ini",
"--initial",
"--nodaemon",
"--config-cache=0",
"--ndb-nodeid=$(cat " + NodeIdFilePath + ")",
}
if debug.Enabled {
// Increase verbosity in debug mode
cmdAndArgs = append(cmdAndArgs, "-v")
}
mgmdContainer := mss.createContainer(nc,
mss.getContainerName(false),
cmdAndArgs, mss.getVolumeMounts(), mgmdPorts)
// Startup probe for the mgmd container
mgmdContainer.StartupProbe = &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
Exec: &corev1.ExecAction{
Command: []string{
"/bin/bash",
helperScriptsMountPath + "/" + constants.MgmdStartupProbeScript,
},
},
},
// Startup probe - expects mgmd to get ready within a minute
PeriodSeconds: 1,
TimeoutSeconds: 20,
FailureThreshold: 60,
}
// Readiness probe checks if the port 1186 is open
mgmdContainer.ReadinessProbe = &corev1.Probe{
ProbeHandler: corev1.ProbeHandler{
TCPSocket: &corev1.TCPSocketAction{
Port: intstr.FromInt(1186),
},
},
}
return []corev1.Container{mgmdContainer}
}
func (mss *mgmdStatefulSet) getPodAntiAffinity() *corev1.PodAntiAffinity {
// Default pod AntiAffinity rules for Management Nodes
return GetPodAntiAffinityRules([]string{
constants.NdbNodeTypeMySQLD, constants.NdbNodeTypeNdbmtd, constants.NdbNodeTypeMgmd,
})
}
// NewStatefulSet returns the StatefulSet specification to start and manage the Management nodes.
func (mss *mgmdStatefulSet) NewStatefulSet(cs *ndbconfig.ConfigSummary, nc *v1.NdbCluster) (*appsv1.StatefulSet, error) {
statefulSet := mss.newStatefulSet(nc, cs)
statefulSetSpec := &statefulSet.Spec
// Fill in mgmd specific values
replicas := cs.NumOfManagementNodes
statefulSetSpec.Replicas = &replicas
// Set pod management policy to start Management nodes one by one
statefulSetSpec.PodManagementPolicy = appsv1.OrderedReadyPodManagement
// Update template pod spec
podSpec := &statefulSetSpec.Template.Spec
podSpec.Containers = mss.getContainers(nc)
podSpec.Volumes = append(podSpec.Volumes, mss.getPodVolumes(nc)...)
// Set default AntiAffinity rules
podSpec.Affinity = &corev1.Affinity{
PodAntiAffinity: mss.getPodAntiAffinity(),
}
// Copy down any podSpec specified via CRD
if nc.Spec.ManagementNode != nil {
CopyPodSpecFromNdbPodSpec(podSpec, nc.Spec.ManagementNode.NdbPodSpec)
}
return statefulSet, nil
}
// NewMgmdStatefulSet returns a new NdbStatefulSetInterface for management nodes
func NewMgmdStatefulSet() NdbStatefulSetInterface {
return &mgmdStatefulSet{
baseStatefulSet{
nodeType: constants.NdbNodeTypeMgmd,
},
}
}