Skip to content

Commit 29df8e4

Browse files
committed
[OCTRL-1091] split deployment of k8s and mesos tasks in manager
1 parent d6c0b40 commit 29df8e4

1 file changed

Lines changed: 137 additions & 9 deletions

File tree

core/task/manager.go

Lines changed: 137 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"sync"
3434
"time"
3535

36+
"github.com/AliceO2Group/Control/common/controlmode"
3637
"github.com/AliceO2Group/Control/common/utils/safeacks"
3738

3839
"github.com/AliceO2Group/Control/apricot"
@@ -51,6 +52,7 @@ import (
5152
"github.com/spf13/viper"
5253
"gopkg.in/yaml.v3"
5354

55+
k8sclient "github.com/AliceO2Group/Control/control-operator/pkg/client"
5456
"github.com/AliceO2Group/Control/core/controlcommands"
5557
"github.com/AliceO2Group/Control/core/task/channel"
5658
"github.com/k0kubun/pp"
@@ -103,6 +105,9 @@ type Manager struct {
103105
internalEventCh chan<- event.Event
104106
ackKilledTasks *safeacks.SafeAcks
105107
killTasksMu sync.Mutex // to avoid races when attempting to kill the same tasks in different goroutines
108+
109+
k8sClient *k8sclient.Client
110+
k8sEnvs *k8sEnvRegistry
106111
}
107112

108113
func NewManager(shutdown func(), internalEventCh chan<- event.Event) (taskman *Manager, err error) {
@@ -144,6 +149,13 @@ func NewManager(shutdown func(), internalEventCh chan<- event.Event) (taskman *M
144149
taskman.tasksToDeploy = taskman.schedulerState.tasksToDeploy
145150
taskman.reviveOffersTrg = taskman.schedulerState.reviveOffersTrg
146151
taskman.ackKilledTasks = safeacks.NewAcks()
152+
taskman.k8sEnvs = newK8sEnvRegistry()
153+
if k8sClient, k8sErr := newK8sClientFromViper(); k8sErr != nil {
154+
log.WithError(k8sErr).Warn("K8s client init failed, K8s tasks disabled")
155+
} else {
156+
log.Info("K8s client initiated")
157+
taskman.k8sClient = k8sClient
158+
}
147159

148160
schedState.setupCli()
149161

@@ -476,6 +488,33 @@ func (m *Manager) acquireTasks(envId uid.ID, taskDescriptors Descriptors) (err e
476488
tasksToRun = append(tasksToRun, taskDescriptors...)
477489
}
478490

491+
// Split K8s descriptors out before the Mesos retry loop.
492+
var k8sDescriptors Descriptors
493+
{
494+
mesos := make(Descriptors, 0, len(tasksToRun))
495+
for _, desc := range tasksToRun {
496+
class, ok := m.classes.GetClass(desc.TaskClassName)
497+
if ok && class != nil && (class.Control.Mode == controlmode.KUBERNETES_DIRECT ||
498+
class.Control.Mode == controlmode.KUBERNETES_FAIRMQ) {
499+
k8sDescriptors = append(k8sDescriptors, desc)
500+
} else {
501+
mesos = append(mesos, desc)
502+
}
503+
}
504+
tasksToRun = mesos
505+
}
506+
507+
var k8sDeployed DeploymentMap
508+
var k8sDeployErr error
509+
if len(k8sDescriptors) > 0 {
510+
if m.k8sClient == nil {
511+
k8sDeployErr = fmt.Errorf("K8s client not configured")
512+
} else {
513+
// TODO: this is synchronous call it would be worth it to make it async, so mesos loop doesn't have to wait for k8s
514+
k8sDeployed, k8sDeployErr = m.deployKubernetesTasks(context.Background(), envId, k8sDescriptors)
515+
}
516+
}
517+
479518
// TODO: fill out tasksToTeardown in the previous loop
480519
//if len(tasksToTeardown) > 0 {
481520
// err = m.TeardownTasks(tasksToTeardown)
@@ -617,6 +656,32 @@ func (m *Manager) acquireTasks(envId uid.ID, taskDescriptors Descriptors) (err e
617656
}
618657
}
619658

659+
// Merge K8s deployment results into unified tracking.
660+
if k8sDeployErr != nil {
661+
logWithId.WithError(k8sDeployErr).Error("K8s task deployment failed")
662+
for _, desc := range k8sDescriptors {
663+
if desc.TaskRole.GetTaskTraits().Critical {
664+
deploymentSuccess = false
665+
undeployableCriticalDescriptors = append(undeployableCriticalDescriptors, desc)
666+
undeployableDescriptors = append(undeployableDescriptors, desc)
667+
} else {
668+
undeployableNonCriticalDescriptors = append(undeployableNonCriticalDescriptors, desc)
669+
undeployableDescriptors = append(undeployableDescriptors, desc)
670+
}
671+
}
672+
} else {
673+
logWithId.Infof("succeeded to deploy %d kubernetes tasks", len(k8sDeployed))
674+
for taskPtr, desc := range k8sDeployed {
675+
taskPtr.SetParent(desc.TaskRole)
676+
if !taskPtr.IsLocked() {
677+
log.WithField("task", taskPtr.taskId).Warning("K8s task not locked after deployment")
678+
deploymentSuccess = false
679+
}
680+
allDeployedTasks[taskPtr] = desc
681+
desc.TaskRole.UpdateStatus(ACTIVE)
682+
}
683+
}
684+
620685
log.Infof("Succeeded to deploy %d/%d tasks", len(allDeployedTasks), len(tasksToRun))
621686

622687
{
@@ -667,6 +732,13 @@ func (m *Manager) acquireTasks(envId uid.ID, taskDescriptors Descriptors) (err e
667732
}
668733

669734
func (m *Manager) releaseTasks(envId uid.ID, tasks Tasks) {
735+
if m.k8sClient != nil {
736+
if err := m.killK8sEnvironment(context.Background(), envId); err != nil {
737+
log.WithError(err).WithField("partition", envId.String()).
738+
Warn("failed to delete K8s Environment CRD during release")
739+
}
740+
}
741+
670742
taskReleaseErrors := make(map[string]error)
671743
taskIdsReleased := make([]string, 0)
672744

@@ -701,18 +773,21 @@ func (m *Manager) releaseTask(envId uid.ID, task *Task) error {
701773
}
702774

703775
func (m *Manager) configureTasks(envId uid.ID, tasks Tasks) error {
704-
notify := make(chan controlcommands.MesosCommandResponse)
705-
receivers, err := tasks.GetMesosCommandTargets()
706-
if err != nil {
707-
return err
708-
}
709-
710-
if tasks == nil || len(tasks) == 0 {
711-
return fmt.Errorf("empty task list to configure for environment %s", envId.String())
776+
var k8sTasks Tasks
777+
var mesosTasks Tasks
778+
for _, t := range tasks {
779+
if t.GetControlMode() == controlmode.KUBERNETES_DIRECT ||
780+
t.GetControlMode() == controlmode.KUBERNETES_FAIRMQ {
781+
k8sTasks = append(k8sTasks, t)
782+
} else {
783+
mesosTasks = append(mesosTasks, t)
784+
}
712785
}
713786

714787
// We fetch each task's local bindMap to generate a global bindMap for the whole Tasks slice,
715788
// i.e. a map of the paths of registered inbound channels and their ports.
789+
// We use the full task list (including K8s tasks) so that Mesos tasks can resolve
790+
// connect targets that point at K8s task channels.
716791
bindMap := make(channel.BindMap)
717792
for _, task := range tasks {
718793
if task.GetParent() == nil { // Crash reported here by Roberto 6/2022
@@ -743,11 +818,27 @@ func (m *Manager) configureTasks(envId uid.ID, tasks Tasks) error {
743818
log.WithFields(logrus.Fields{"bindMap": pp.Sprint(bindMap), "envId": envId.String()}).
744819
Debug("generated inbound bindMap for environment configuration")
745820

821+
if len(k8sTasks) > 0 && m.k8sClient != nil {
822+
if err := m.configureK8sTasks(context.Background(), envId, k8sTasks, bindMap); err != nil {
823+
return err
824+
}
825+
}
826+
827+
if len(mesosTasks) == 0 {
828+
return nil
829+
}
830+
831+
notify := make(chan controlcommands.MesosCommandResponse)
832+
receivers, err := mesosTasks.GetMesosCommandTargets()
833+
if err != nil {
834+
return err
835+
}
836+
746837
src := sm.STANDBY.String()
747838
evt := "CONFIGURE"
748839
dest := sm.CONFIGURED.String()
749840
args := make(controlcommands.PropertyMapsMap)
750-
args, err = tasks.BuildPropertyMaps(bindMap)
841+
args, err = mesosTasks.BuildPropertyMaps(bindMap)
751842
if err != nil {
752843
return err
753844
}
@@ -811,6 +902,28 @@ func (m *Manager) configureTasks(envId uid.ID, tasks Tasks) error {
811902
}
812903

813904
func (m *Manager) transitionTasks(envId uid.ID, tasks Tasks, src string, event string, dest string, commonArgs controlcommands.PropertyMap) error {
905+
var mesosTasks Tasks
906+
907+
for _, t := range tasks {
908+
if t.GetControlMode() != controlmode.KUBERNETES_DIRECT &&
909+
t.GetControlMode() != controlmode.KUBERNETES_FAIRMQ {
910+
mesosTasks = append(mesosTasks, t)
911+
}
912+
}
913+
914+
if len(mesosTasks) == 0 {
915+
return nil
916+
}
917+
918+
if len(tasks) != len(mesosTasks) {
919+
log.WithField("partition", envId).Infof("Transitioning k8s environment from %s, to %s, event: %s", src, dest, event)
920+
if err := m.transitionAndWaitK8sEnvState(context.Background(), envId, strings.ToLower(dest)); err != nil {
921+
return err
922+
}
923+
}
924+
925+
tasks = mesosTasks
926+
814927
notify := make(chan controlcommands.MesosCommandResponse)
815928
receivers, err := tasks.GetMesosCommandTargets()
816929
if err != nil {
@@ -1128,6 +1241,17 @@ func (m *Manager) KillTasks(taskIds []string) (killed Tasks, running Tasks, err
11281241
}
11291242

11301243
func (m *Manager) doKillTask(task *Task) error {
1244+
if task.GetControlMode() == controlmode.KUBERNETES_DIRECT ||
1245+
task.GetControlMode() == controlmode.KUBERNETES_FAIRMQ {
1246+
if m.k8sClient != nil {
1247+
if err := m.killK8sTask(context.Background(), task); err != nil {
1248+
return err
1249+
}
1250+
}
1251+
// K8s delete is synchronous — send the ack immediately so KillTasks doesn't block.
1252+
_ = m.ackKilledTasks.TrySendAck(task.taskId)
1253+
return nil
1254+
}
11311255
return m.schedulerState.killTask(context.TODO(), task.GetMesosCommandTarget())
11321256
}
11331257

@@ -1178,6 +1302,10 @@ func (m *Manager) Start(ctx context.Context) {
11781302

11791303
m.schedulerState.Start(ctx)
11801304

1305+
if m.k8sClient != nil {
1306+
go m.watchK8sTasks(ctx)
1307+
}
1308+
11811309
go func() {
11821310
for {
11831311
select {

0 commit comments

Comments
 (0)