Compare commits
11 Commits
main
...
feature/ad
Author | SHA1 | Date | |
---|---|---|---|
|
cd804fbeb5 | ||
|
27fd603e36 | ||
|
c31184e2ec | ||
|
5d8143c93e | ||
|
77a9b0770e | ||
|
9a17623cab | ||
|
4963284056 | ||
|
df09585cc9 | ||
|
aa20edaf25 | ||
42ee6abcb6 | |||
08ade1af66 |
126
main.go
126
main.go
@ -12,6 +12,7 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"oc-monitord/conf"
|
||||
"oc-monitord/models"
|
||||
@ -44,6 +45,7 @@ import (
|
||||
|
||||
var logger zerolog.Logger
|
||||
var wf_logger zerolog.Logger
|
||||
var pods_logger zerolog.Logger
|
||||
var parser argparse.Parser
|
||||
var workflowName string
|
||||
|
||||
@ -84,36 +86,36 @@ func main() {
|
||||
|
||||
err := new_wf.LoadFrom(conf.GetConfig().WorkflowID, conf.GetConfig().PeerID)
|
||||
if err != nil {
|
||||
|
||||
|
||||
logger.Error().Msg("Could not retrieve workflow " + conf.GetConfig().WorkflowID + " from oc-catalog API")
|
||||
}
|
||||
|
||||
builder, argo_file_path, stepMax, err := new_wf.ExportToArgo(exec.ExecutionsID, conf.GetConfig().Timeout)
|
||||
builder, stepMax, err := new_wf.ExportToArgo(exec.ExecutionsID, conf.GetConfig().Timeout)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not create the Argo file for " + conf.GetConfig().WorkflowID)
|
||||
logger.Error().Msg(err.Error())
|
||||
}
|
||||
logger.Debug().Msg("Created :" + argo_file_path)
|
||||
|
||||
argo_file_path, err := builder.CompleteBuild(exec.ExecutionsID)
|
||||
if err != nil {
|
||||
logger.Error().Msg(err.Error())
|
||||
}
|
||||
|
||||
workflowName = getContainerName(argo_file_path)
|
||||
|
||||
wf_logger = logger.With().Str("argo_name", workflowName).Str("workflow_id", conf.GetConfig().WorkflowID).Str("workflow_execution_id", conf.GetConfig().ExecutionID).Logger()
|
||||
wf_logger.Debug().Msg("Testing argo name")
|
||||
|
||||
err = builder.CompleteBuild(exec.ExecutionsID)
|
||||
if err != nil {
|
||||
logger.Error().Msg(err.Error())
|
||||
}
|
||||
_ = stepMax
|
||||
// if conf.GetConfig().KubeHost == "" {
|
||||
// // Not in a k8s environment, get conf from parameters
|
||||
// fmt.Println("Executes outside of k8s")
|
||||
// executeOutside(argo_file_path, stepMax)
|
||||
// } else {
|
||||
// // Executed in a k8s environment
|
||||
// fmt.Println("Executes inside a k8s")
|
||||
// executeInside(exec.GetID(), "argo", argo_file_path, stepMax)
|
||||
// }
|
||||
|
||||
if conf.GetConfig().KubeHost == "" {
|
||||
// Not in a k8s environment, get conf from parameters
|
||||
fmt.Println("Executes outside of k8s")
|
||||
executeOutside(argo_file_path, stepMax, builder.Workflow)
|
||||
} else {
|
||||
// Executed in a k8s environment
|
||||
fmt.Println("Executes inside a k8s")
|
||||
executeInside(exec.GetID(), "argo", argo_file_path, stepMax)
|
||||
}
|
||||
}
|
||||
|
||||
// So far we only log the output from
|
||||
@ -137,34 +139,76 @@ func executeInside(execID string, ns string, argo_file_path string, stepMax int)
|
||||
|
||||
}
|
||||
|
||||
func executeOutside(argo_file_path string, stepMax int) {
|
||||
// var stdout, stderr, stdout_logs, stderr_logs io.ReadCloser
|
||||
var stdout, stderr io.ReadCloser
|
||||
func executeOutside(argo_file_path string, stepMax int, workflow workflow_builder.Workflow) {
|
||||
// var stdoutSubmit, stderrSubmit, stdout_logs, stderr_logs io.ReadCloser
|
||||
var stdoutSubmit, stderrSubmit io.ReadCloser
|
||||
var stdoutLogs, stderrLogs io.ReadCloser
|
||||
// var stderr io.ReadCloser
|
||||
var wg sync.WaitGroup
|
||||
var err error
|
||||
cmd := exec.Command("argo", "submit", "--log", argo_file_path, "--serviceaccount=argo", "-n", "argo")
|
||||
if stdout, err = cmd.StdoutPipe(); err != nil {
|
||||
|
||||
logger.Debug().Msg("executing :" + "argo submit --watch " + argo_file_path + " --serviceaccount sa-" + conf.GetConfig().ExecutionID + " -n " + conf.GetConfig().ExecutionID )
|
||||
|
||||
cmdSubmit := exec.Command("argo", "submit", "--watch", argo_file_path, "--serviceaccount", "sa-"+conf.GetConfig().ExecutionID, "-n", conf.GetConfig().ExecutionID)
|
||||
if stdoutSubmit, err = cmdSubmit.StdoutPipe(); err != nil {
|
||||
wf_logger.Error().Msg("Could not retrieve stdoutpipe " + err.Error())
|
||||
return
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
split := strings.Split(argo_file_path, "_")
|
||||
argoLogs := models.NewArgoLogs(split[0], "argo", stepMax)
|
||||
argoLogs.StartStepRecording(argoLogs.NewWatch(), wf_logger)
|
||||
argoLogs.IsStreaming = true
|
||||
go logWorkflow(argo_file_path, stepMax, stdout, argoLogs.NewWatch(), argoLogs.NewWatch(), argoLogs, []string{}, &wg)
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
wf_logger.Error().Msg("Could not execute argo submit")
|
||||
wf_logger.Error().Msg(err.Error() + bufio.NewScanner(stderr).Text())
|
||||
// //======== Code block that implemented a method that logs both locally and container executed wf
|
||||
// // Need to be improved, did not log well for local executions
|
||||
// split := strings.Split(argo_file_path, "_")
|
||||
// argoLogs := models.NewArgoLogs(split[0], conf.GetConfig().ExecutionID, stepMax)
|
||||
// argoLogs.StartStepRecording(argoLogs.NewWatch(), wf_logger)
|
||||
// argoLogs.IsStreaming = true // Used to determine wether or not the logs are read from a docker container or on localhost
|
||||
// // go logWorkflow(argo_file_path, stepMax, stdout, argoLogs.NewWatch(), argoLogs.NewWatch(), argoLogs, []string{}, &wg)
|
||||
// // =======
|
||||
|
||||
var steps []string
|
||||
for _, template := range workflow.Spec.Templates {
|
||||
steps = append(steps, template.Name)
|
||||
}
|
||||
|
||||
cmdLogs := exec.Command("argo", "logs", "oc-monitor-"+workflowName, "-n", conf.GetConfig().ExecutionID, "--follow","--no-color")
|
||||
if stdoutLogs, err = cmdLogs.StdoutPipe(); err != nil {
|
||||
wf_logger.Error().Msg("Could not retrieve stdoutpipe for 'argo logs'" + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
go models.LogLocalWorkflow(workflowName, stdoutSubmit, &wg)
|
||||
go models.LogPods(workflowName, stdoutLogs, steps, &wg)
|
||||
|
||||
fmt.Println("Starting argo submit")
|
||||
if err := cmdSubmit.Start(); err != nil {
|
||||
wf_logger.Error().Msg("Could not start argo submit")
|
||||
wf_logger.Error().Msg(err.Error() + bufio.NewScanner(stderrSubmit).Text())
|
||||
updateStatus("fatal", "")
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
fmt.Println("Running argo logs")
|
||||
if err := cmdLogs.Run(); err != nil {
|
||||
wf_logger.Error().Msg("Could not run '" + strings.Join(cmdLogs.Args, " ") + "'")
|
||||
|
||||
wf_logger.Fatal().Msg(err.Error() + bufio.NewScanner(stderrLogs).Text())
|
||||
|
||||
}
|
||||
|
||||
fmt.Println("Waiting argo submit")
|
||||
if err := cmdSubmit.Wait(); err != nil {
|
||||
wf_logger.Error().Msg("Could not execute argo submit")
|
||||
wf_logger.Error().Msg(err.Error() + bufio.NewScanner(stderrSubmit).Text())
|
||||
updateStatus("fatal", "")
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// !!!! BUGGED !!!!
|
||||
// Should be refactored to create a function dedicated to logging output from execution in a container
|
||||
// LogLocalWorkflow() has been implemented to be used when oc-monitord is executed locally
|
||||
|
||||
// We could improve this function by creating an object with the same attribute as the output
|
||||
// and only send a new log if the current object has different values than the previous
|
||||
func logWorkflow(argo_file_path string, stepMax int, pipe io.ReadCloser,
|
||||
@ -181,12 +225,8 @@ func logWorkflow(argo_file_path string, stepMax int, pipe io.ReadCloser,
|
||||
wg.Add(1)
|
||||
}
|
||||
seeit++
|
||||
} else if count == 0 {
|
||||
if argoLogs.IsStreaming {
|
||||
continue
|
||||
} else {
|
||||
break
|
||||
}
|
||||
} else if count == 0 && !argoLogs.IsStreaming {
|
||||
break
|
||||
}
|
||||
if count == 1 {
|
||||
see = log
|
||||
@ -200,7 +240,7 @@ func logWorkflow(argo_file_path string, stepMax int, pipe io.ReadCloser,
|
||||
current_watch.Logs = append(current_watch.Logs, strings.ReplaceAll(log, "\"", ""))
|
||||
}
|
||||
count++
|
||||
if strings.Contains(log, "sub-process exited") {
|
||||
if strings.Contains(log, "sub-process exited") || argoLogs.IsStreaming {
|
||||
current_watch = argoLogs.StopStepRecording(current_watch)
|
||||
argoLogs.Seen = append(argoLogs.Seen, see)
|
||||
if checkStatus(current_watch, previous_watch, argoLogs) {
|
||||
@ -221,6 +261,9 @@ func logWorkflow(argo_file_path string, stepMax int, pipe io.ReadCloser,
|
||||
}
|
||||
previous_watch = current_watch
|
||||
current_watch = &models.ArgoWatch{}
|
||||
if argoLogs.IsStreaming {
|
||||
current_watch.Logs = []string{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -320,6 +363,7 @@ func getContainerName(argo_file string) string {
|
||||
re := regexp.MustCompile(regex)
|
||||
|
||||
container_name := re.FindString(argo_file)
|
||||
|
||||
return container_name
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// An object to monitor the logs generated by a specific pod from a workflow execution
|
||||
type ArgoWatch struct {
|
||||
Name string
|
||||
Namespace string
|
||||
@ -47,6 +48,7 @@ func NewArgoLogs(name string, namespace string, stepMax int) *ArgoLogs {
|
||||
}
|
||||
}
|
||||
|
||||
// An object to monitor and log the output of an argo submit
|
||||
type ArgoLogs struct {
|
||||
Name string
|
||||
Namespace string
|
||||
@ -143,3 +145,17 @@ func (a *ArgoLogs) StopStepRecording(current *ArgoWatch) *ArgoWatch {
|
||||
current.Status = status
|
||||
return current
|
||||
}
|
||||
|
||||
type ArgoPodLog struct {
|
||||
PodName string
|
||||
Step string
|
||||
Message string
|
||||
}
|
||||
|
||||
func NewArgoPodLog(name string, step string, msg string) ArgoPodLog {
|
||||
return ArgoPodLog{
|
||||
PodName: name,
|
||||
Step: step,
|
||||
Message: msg,
|
||||
}
|
||||
}
|
142
models/local_argo_logs.go
Normal file
142
models/local_argo_logs.go
Normal file
@ -0,0 +1,142 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"oc-monitord/conf"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"cloud.o-forge.io/core/oc-lib/logs"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var logger zerolog.Logger
|
||||
var wfLogger zerolog.Logger
|
||||
|
||||
|
||||
// Take the slice of string that make up one round of stderr outputs from the --watch option in argo submit
|
||||
func NewLocalArgoWatch(inputs []string) *ArgoWatch {
|
||||
var workflow ArgoWatch
|
||||
|
||||
for _, input := range inputs {
|
||||
line := strings.TrimSpace(input)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(line, "Name:"):
|
||||
workflow.Name = parseValue(line)
|
||||
case strings.HasPrefix(line, "Namespace:"):
|
||||
workflow.Namespace = parseValue(line)
|
||||
case strings.HasPrefix(line, "Status:"):
|
||||
workflow.Status = parseValue(line)
|
||||
case strings.HasPrefix(line, "PodRunning"):
|
||||
workflow.PodRunning = parseBoolValue(line)
|
||||
case strings.HasPrefix(line, "Completed"):
|
||||
workflow.Completed = parseBoolValue(line)
|
||||
case strings.HasPrefix(line, "Created:"):
|
||||
workflow.Created = parseValue(line)
|
||||
case strings.HasPrefix(line, "Started:"):
|
||||
workflow.Started = parseValue(line)
|
||||
case strings.HasPrefix(line, "Duration:"):
|
||||
workflow.Duration = parseValue(line)
|
||||
case strings.HasPrefix(line, "Progress:"):
|
||||
workflow.Progress = parseValue(line)
|
||||
}
|
||||
}
|
||||
|
||||
return &workflow
|
||||
}
|
||||
|
||||
|
||||
|
||||
func parseValue(line string) string {
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) < 2 {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(parts[1])
|
||||
}
|
||||
|
||||
func parseBoolValue(line string) bool {
|
||||
value := parseValue(line)
|
||||
return value == "True"
|
||||
}
|
||||
|
||||
func LogLocalWorkflow(wfName string, pipe io.ReadCloser, wg *sync.WaitGroup) {
|
||||
logger = logs.GetLogger()
|
||||
|
||||
logger.Debug().Msg("created wf_logger")
|
||||
fmt.Println("created wf_logger")
|
||||
wfLogger = logger.With().Str("argo_name", wfName).Str("workflow_id", conf.GetConfig().WorkflowID).Str("workflow_execution_id", conf.GetConfig().ExecutionID).Logger()
|
||||
|
||||
var current_watch, previous_watch ArgoWatch
|
||||
|
||||
watch_output := make([]string, 0)
|
||||
scanner := bufio.NewScanner(pipe)
|
||||
for scanner.Scan() {
|
||||
log := scanner.Text()
|
||||
watch_output = append(watch_output, log)
|
||||
|
||||
// Log the progress of the WF
|
||||
if strings.HasPrefix(log, "Progress:") {
|
||||
|
||||
current_watch = *NewLocalArgoWatch(watch_output)
|
||||
workflowName := current_watch.Name
|
||||
if !current_watch.Equals(&previous_watch) {
|
||||
wg.Add(1)
|
||||
// checkStatus(current_watch.Status, previous_watch.Status)
|
||||
jsonified, err := json.Marshal(current_watch)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not create watch log for " + workflowName)
|
||||
}
|
||||
wfLogger.Info().Msg(string(jsonified))
|
||||
previous_watch = current_watch
|
||||
current_watch = ArgoWatch{}
|
||||
wg.Done()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Debug, no logs sent
|
||||
func LogPods(wfName string, pipe io.ReadCloser, steps []string, wg *sync.WaitGroup) {
|
||||
scanner := bufio.NewScanner(pipe)
|
||||
for scanner.Scan() {
|
||||
var podLogger zerolog.Logger
|
||||
fmt.Println("new line")
|
||||
wg.Add(1)
|
||||
|
||||
line := scanner.Text()
|
||||
podName := strings.Split(line, ":")[0]
|
||||
podLogger = wfLogger.With().Str("step_name", getStepName(podName, steps)).Logger()
|
||||
log := strings.Split(line,podName+":")[1]
|
||||
podLog := NewArgoPodLog(wfName,podName,log)
|
||||
jsonifiedLog, err := json.Marshal(podLog)
|
||||
if err != nil {
|
||||
podLogger.Fatal().Msg(err.Error())
|
||||
}
|
||||
|
||||
podLogger.Info().Msg(string(jsonifiedLog))
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getStepName(podName string, steps []string) string {
|
||||
|
||||
for _, step := range(steps) {
|
||||
if strings.Contains(podName,step){
|
||||
return step
|
||||
}
|
||||
}
|
||||
|
||||
return "error"
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ type Dag struct {
|
||||
|
||||
type TemplateMetadata struct {
|
||||
Labels map[string]string `yaml:"labels,omitempty"`
|
||||
Annotations map[string]string `yaml:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
type Secret struct {
|
||||
@ -139,3 +140,13 @@ func (template *Template) ReplacePerEnv(arg string, envs []models.Param) string
|
||||
}
|
||||
return arg
|
||||
}
|
||||
|
||||
// Add the metadata that allow Admiralty to pick up an Argo Workflow that needs to be reparted
|
||||
// The value of "clustername" is the peerId, which must be replaced by the node name's for this specific execution
|
||||
func (t *Template) AddAdmiraltyAnnotations(peerId string){
|
||||
if t.Metadata.Annotations == nil {
|
||||
t.Metadata.Annotations = make(map[string]string)
|
||||
}
|
||||
t.Metadata.Annotations["multicluster.admiralty.io/elect"] = ""
|
||||
t.Metadata.Annotations["multicluster.admiralty.io/clustername"] = peerId
|
||||
}
|
@ -4,18 +4,25 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
oclib "cloud.o-forge.io/core/oc-lib"
|
||||
"cloud.o-forge.io/core/oc-lib/logs"
|
||||
"cloud.o-forge.io/core/oc-lib/models/peer"
|
||||
"cloud.o-forge.io/core/oc-lib/tools"
|
||||
tools "cloud.o-forge.io/core/oc-lib/tools"
|
||||
)
|
||||
|
||||
|
||||
type AdmiraltySetter struct {
|
||||
Id string // ID to identify the execution, correspond to workflow_executions id
|
||||
Id string // ID to identify the execution, correspond to workflow_executions id
|
||||
NodeName string // Allows to retrieve the name of the node used for this execution on each peer {"peerId": "nodeName"}
|
||||
}
|
||||
|
||||
func (s *AdmiraltySetter) InitializeAdmiralty(localPeerID string,remotePeerID string) error {
|
||||
|
||||
logger = logs.GetLogger()
|
||||
|
||||
data := oclib.NewRequest(oclib.LibDataEnum(oclib.PEER),"",localPeerID,nil,nil).LoadOne(remotePeerID)
|
||||
if data.Code != 200 {
|
||||
logger.Error().Msg("Error while trying to instantiate remote peer " + remotePeerID)
|
||||
@ -49,19 +56,24 @@ func (s *AdmiraltySetter) InitializeAdmiralty(localPeerID string,remotePeerID st
|
||||
},
|
||||
},
|
||||
)
|
||||
fmt.Println("Creating source in", remotePeerID, " ns-" + s.Id)
|
||||
_ = s.callRemoteExecution(remotePeer, http.StatusCreated,caller, s.Id, tools.ADMIRALTY_SOURCE, tools.POST, nil)
|
||||
|
||||
logger.Info().Msg(" Creating the Admiralty Source on " + remotePeerID + " ns-" + s.Id + "\n\n")
|
||||
_ = s.callRemoteExecution(remotePeer, []int{http.StatusCreated, http.StatusConflict},caller, s.Id, tools.ADMIRALTY_SOURCE, tools.POST, nil, true)
|
||||
logger.Info().Msg(" Retrieving kubeconfig with the secret on " + remotePeerID + " ns-" + s.Id + "\n\n")
|
||||
kubeconfig := s.getKubeconfig(remotePeer, caller)
|
||||
_ = s.callRemoteExecution(localPeer, http.StatusCreated, caller,s.Id, tools.ADMIRALTY_SECRET, tools.POST,kubeconfig)
|
||||
_ = s.callRemoteExecution(localPeer,http.StatusCreated,caller,s.Id,tools.ADMIRALTY_TARGET,tools.POST, nil)
|
||||
_ = s.callRemoteExecution(localPeer,http.StatusOK,caller,s.Id,tools.ADMIRALTY_NODES,tools.GET, nil)
|
||||
logger.Info().Msg(" Creating a secret from the kubeconfig " + localPeerID + " ns-" + s.Id + "\n\n")
|
||||
_ = s.callRemoteExecution(localPeer, []int{http.StatusCreated}, caller,s.Id, tools.ADMIRALTY_SECRET, tools.POST,kubeconfig, true)
|
||||
logger.Info().Msg(" Creating the Admiralty Target on " + localPeerID + " ns-" + s.Id + "\n\n")
|
||||
_ = s.callRemoteExecution(localPeer,[]int{http.StatusCreated, http.StatusConflict},caller,s.Id,tools.ADMIRALTY_TARGET,tools.POST, nil, true)
|
||||
logger.Info().Msg(" Checking for the creation of the admiralty node on " + localPeerID + " ns-" + s.Id + "\n\n")
|
||||
s.checkNodeStatus(localPeer,caller)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AdmiraltySetter) getKubeconfig(peer *peer.Peer, caller *tools.HTTPCaller) map[string]string {
|
||||
var kubedata map[string]string
|
||||
_ = s.callRemoteExecution(peer, http.StatusOK, caller, s.Id, tools.ADMIRALTY_KUBECONFIG, tools.GET, nil)
|
||||
_ = s.callRemoteExecution(peer, []int{http.StatusOK}, caller, s.Id, tools.ADMIRALTY_KUBECONFIG, tools.GET, nil, true)
|
||||
if caller.LastResults["body"] == nil || len(caller.LastResults["body"].([]byte)) == 0 {
|
||||
fmt.Println("Something went wrong when retrieving data from Get call for kubeconfig")
|
||||
panic(0)
|
||||
@ -75,7 +87,7 @@ func (s *AdmiraltySetter) getKubeconfig(peer *peer.Peer, caller *tools.HTTPCalle
|
||||
return kubedata
|
||||
}
|
||||
|
||||
func (*AdmiraltySetter) callRemoteExecution(peer *peer.Peer, expectedCode int,caller *tools.HTTPCaller, dataID string, dt tools.DataType, method tools.METHOD, body interface{}) *peer.PeerExecution {
|
||||
func (*AdmiraltySetter) callRemoteExecution(peer *peer.Peer, expectedCode []int,caller *tools.HTTPCaller, dataID string, dt tools.DataType, method tools.METHOD, body interface{}, panicCode bool) *peer.PeerExecution {
|
||||
resp, err := peer.LaunchPeerExecution(peer.UUID, dataID, dt, method, body, caller)
|
||||
if err != nil {
|
||||
fmt.Println("Error when executing on peer at", peer.Url)
|
||||
@ -83,11 +95,49 @@ func (*AdmiraltySetter) callRemoteExecution(peer *peer.Peer, expectedCode int,ca
|
||||
panic(0)
|
||||
}
|
||||
|
||||
if caller.LastResults["code"].(int) != expectedCode {
|
||||
if !slices.Contains(expectedCode, caller.LastResults["code"].(int)) {
|
||||
fmt.Println("Didn't receive the expected code :", caller.LastResults["code"], "when expecting", expectedCode)
|
||||
fmt.Println(string(caller.LastResults["body"].(byte)))
|
||||
panic(0)
|
||||
if _, ok := caller.LastResults["body"]; ok {
|
||||
logger.Info().Msg(string(caller.LastResults["body"].([]byte)))
|
||||
// fmt.Println(string(caller.LastResults["body"].([]byte)))
|
||||
}
|
||||
if panicCode {
|
||||
panic(0)
|
||||
}
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func (s *AdmiraltySetter) storeNodeName(caller *tools.HTTPCaller){
|
||||
var data map[string]interface{}
|
||||
if resp, ok := caller.LastResults["body"]; ok {
|
||||
json.Unmarshal(resp.([]byte), &data)
|
||||
}
|
||||
|
||||
if node, ok := data["node"]; ok {
|
||||
metadata := node.(map[string]interface{})["metadata"]
|
||||
name := metadata.(map[string]interface{})["name"].(string)
|
||||
s.NodeName = name
|
||||
} else {
|
||||
fmt.Println("Could not retrieve data about the recently created node")
|
||||
panic(0)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AdmiraltySetter) checkNodeStatus(localPeer *peer.Peer, caller *tools.HTTPCaller){
|
||||
for i := range(5) {
|
||||
time.Sleep(5 * time.Second) // let some time for kube to generate the node
|
||||
_ = s.callRemoteExecution(localPeer,[]int{http.StatusOK},caller,s.Id,tools.ADMIRALTY_NODES,tools.GET, nil, false)
|
||||
if caller.LastResults["code"] == 200 {
|
||||
s.storeNodeName(caller)
|
||||
return
|
||||
}
|
||||
if i == 5 {
|
||||
logger.Error().Msg("Node on " + localPeer.Name + " was never found, panicking !")
|
||||
panic(0)
|
||||
}
|
||||
logger.Info().Msg("Could not verify that node is up. Retrying...")
|
||||
}
|
||||
|
||||
}
|
@ -14,6 +14,7 @@ import (
|
||||
"time"
|
||||
|
||||
oclib "cloud.o-forge.io/core/oc-lib"
|
||||
"cloud.o-forge.io/core/oc-lib/logs"
|
||||
"cloud.o-forge.io/core/oc-lib/models/common/enum"
|
||||
"cloud.o-forge.io/core/oc-lib/models/resources"
|
||||
w "cloud.o-forge.io/core/oc-lib/models/workflow"
|
||||
@ -61,7 +62,8 @@ type Spec struct {
|
||||
|
||||
// TODO: found on a processing instance linked to storage
|
||||
// add s3, gcs, azure, etc if needed on a link between processing and storage
|
||||
func (b *ArgoBuilder) CreateDAG(namespace string, write bool) (string, int, []string, []string, error) {
|
||||
func (b *ArgoBuilder) CreateDAG(namespace string, write bool) ( int, []string, []string, error) {
|
||||
logger = logs.GetLogger()
|
||||
fmt.Println("Creating DAG", b.OriginWorkflow.Graph.Items)
|
||||
// handle services by checking if there is only one processing with hostname and port
|
||||
firstItems, lastItems, volumes := b.createTemplates(namespace)
|
||||
@ -74,26 +76,11 @@ func (b *ArgoBuilder) CreateDAG(namespace string, write bool) (string, int, []st
|
||||
b.Workflow.ApiVersion = "argoproj.io/v1alpha1"
|
||||
b.Workflow.Kind = "Workflow"
|
||||
if !write {
|
||||
return "", len(b.Workflow.getDag().Tasks), firstItems, lastItems, nil
|
||||
return len(b.Workflow.getDag().Tasks), firstItems, lastItems, nil
|
||||
}
|
||||
random_name := fakelish.GenerateFakeWord(5, 8) + "-" + fakelish.GenerateFakeWord(5, 8)
|
||||
b.Workflow.Metadata.Name = "oc-monitor-" + random_name
|
||||
logger = oclib.GetLogger()
|
||||
yamlified, err := yaml.Marshal(b.Workflow)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not transform object to yaml file")
|
||||
return "", 0, firstItems, lastItems, err
|
||||
}
|
||||
// Give a unique name to each argo file with its timestamp DD:MM:YYYY_hhmmss
|
||||
current_timestamp := time.Now().Format("02_01_2006_150405")
|
||||
file_name := random_name + "_" + current_timestamp + ".yml"
|
||||
workflows_dir := "./argo_workflows/"
|
||||
err = os.WriteFile(workflows_dir+file_name, []byte(yamlified), 0660)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not write the yaml file")
|
||||
return "", 0, firstItems, lastItems, err
|
||||
}
|
||||
return workflows_dir + file_name, len(b.Workflow.getDag().Tasks), firstItems, lastItems, nil
|
||||
|
||||
|
||||
return len(b.Workflow.getDag().Tasks), firstItems, lastItems, nil
|
||||
}
|
||||
|
||||
func (b *ArgoBuilder) createTemplates(namespace string) ([]string, []string, []VolumeMount) {
|
||||
@ -122,7 +109,7 @@ func (b *ArgoBuilder) createTemplates(namespace string) ([]string, []string, []V
|
||||
continue
|
||||
}
|
||||
subBuilder := ArgoBuilder{OriginWorkflow: realWorkflow.(*w.Workflow), Timeout: b.Timeout}
|
||||
_, _, fi, li, err := subBuilder.CreateDAG(namespace, false)
|
||||
_, fi, li, err := subBuilder.CreateDAG(namespace, false)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Error creating the subworkflow : " + err.Error())
|
||||
continue
|
||||
@ -192,6 +179,7 @@ func (b *ArgoBuilder) createArgoTemplates(namespace string,
|
||||
template.CreateContainer(processing, b.Workflow.getDag())
|
||||
if isReparted {
|
||||
b.RemotePeers = append(b.RemotePeers, peerId)
|
||||
template.AddAdmiraltyAnnotations(peerId)
|
||||
}
|
||||
// get datacenter from the processing
|
||||
if processing.IsService {
|
||||
@ -433,12 +421,44 @@ func (b *ArgoBuilder) retrieveProcessingCompute(graphID string) *resources.Compu
|
||||
|
||||
|
||||
// Execute the last actions once the YAML file for the Argo Workflow is created
|
||||
func (b *ArgoBuilder) CompleteBuild(executionsId string) error {
|
||||
func (b *ArgoBuilder) CompleteBuild(executionsId string) (string, error) {
|
||||
fmt.Println("DEV :: Completing build")
|
||||
setter := AdmiraltySetter{Id: executionsId}
|
||||
// Setup admiralty for each node
|
||||
for _, peer := range b.RemotePeers {
|
||||
fmt.Println("DEV :: Launching Admiralty Setup for ", peer)
|
||||
setter := AdmiraltySetter{Id: executionsId}
|
||||
setter.InitializeAdmiralty(conf.GetConfig().PeerID,peer)
|
||||
}
|
||||
return nil
|
||||
|
||||
// Update the name of the admiralty node to use
|
||||
for _, template := range b.Workflow.Spec.Templates {
|
||||
if len(template.Metadata.Annotations) > 0 {
|
||||
if resp, ok := template.Metadata.Annotations["multicluster.admiralty.io/clustername"]; ok {
|
||||
fmt.Println(resp)
|
||||
template.Metadata.Annotations["multicluster.admiralty.io/clustername"] = "target-" + conf.GetConfig().ExecutionID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the YAML file
|
||||
random_name := fakelish.GenerateFakeWord(5, 8) + "-" + fakelish.GenerateFakeWord(5, 8)
|
||||
b.Workflow.Metadata.Name = "oc-monitor-" + random_name
|
||||
logger = oclib.GetLogger()
|
||||
yamlified, err := yaml.Marshal(b.Workflow)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not transform object to yaml file")
|
||||
return "", err
|
||||
}
|
||||
// Give a unique name to each argo file with its timestamp DD:MM:YYYY_hhmmss
|
||||
current_timestamp := time.Now().Format("02_01_2006_150405")
|
||||
file_name := random_name + "_" + current_timestamp + ".yml"
|
||||
workflows_dir := "./argo_workflows/"
|
||||
err = os.WriteFile(workflows_dir+file_name, []byte(yamlified), 0660)
|
||||
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not write the yaml file")
|
||||
return "", err
|
||||
}
|
||||
|
||||
return workflows_dir + file_name, nil
|
||||
}
|
@ -41,20 +41,20 @@ func (w *WorflowDB) getWorkflow(workflow_id string, peerID string) (workflow *wo
|
||||
return new_wf, nil
|
||||
}
|
||||
|
||||
func (w *WorflowDB) ExportToArgo(namespace string, timeout int) (*ArgoBuilder,string, int, error) {
|
||||
func (w *WorflowDB) ExportToArgo(namespace string, timeout int) (*ArgoBuilder, int, error) {
|
||||
logger := oclib.GetLogger()
|
||||
fmt.Println("Exporting to Argo", w.Workflow)
|
||||
if len(w.Workflow.Name) == 0 || w.Workflow.Graph == nil {
|
||||
return nil, "", 0, fmt.Errorf("can't export a graph that has not been loaded yet")
|
||||
return nil, 0, fmt.Errorf("can't export a graph that has not been loaded yet")
|
||||
}
|
||||
|
||||
argoBuilder := ArgoBuilder{OriginWorkflow: w.Workflow, Timeout: timeout}
|
||||
filename, stepMax, _, _, err := argoBuilder.CreateDAG(namespace, true)
|
||||
stepMax, _, _, err := argoBuilder.CreateDAG(namespace, true)
|
||||
if err != nil {
|
||||
logger.Error().Msg("Could not create the argo file for " + w.Workflow.Name)
|
||||
return nil, "", 0, err
|
||||
return nil, 0, err
|
||||
}
|
||||
return &argoBuilder, filename, stepMax, nil
|
||||
return &argoBuilder, stepMax, nil
|
||||
}
|
||||
|
||||
// TODO implement this function
|
||||
|
Loading…
Reference in New Issue
Block a user