2024-05-02 09:52:28 +02:00
// A class that translates the informations held in the graph object
// via its lists of components into an argo file, using the a list of
// link ID to build the dag
package main
import (
"fmt"
2024-05-03 10:58:06 +02:00
"os"
2024-05-02 09:52:28 +02:00
"slices"
2024-05-06 17:01:48 +02:00
"strings"
2024-05-02 09:52:28 +02:00
"github.com/beego/beego/v2/core/logs"
2024-05-07 19:45:20 +02:00
"github.com/nwtgck/go-fakelish"
2024-05-02 09:52:28 +02:00
"gopkg.in/yaml.v3"
)
type ArgoBuilder struct {
2024-05-03 10:58:06 +02:00
graph Graph
2024-05-02 09:52:28 +02:00
branches [ ] [ ] string
2024-05-03 10:58:06 +02:00
Workflow Workflow
2024-05-02 09:52:28 +02:00
}
2024-05-03 10:58:06 +02:00
type Workflow struct {
2024-05-07 19:45:20 +02:00
ApiVersion string ` yaml:"apiVersion" `
Kind string ` yaml:"kind" `
Metadata struct {
GenerateName string ` yaml:"generateName" `
} ` yaml:"metadata" `
Spec Spec ` yaml:"spec,omitempty" `
2024-05-02 09:52:28 +02:00
}
2024-05-07 19:45:20 +02:00
type Spec struct {
Entrypoint string ` yaml:"entrypoint" `
Arguments [ ] Parameter ` yaml:"arguments,omitempty" `
Volumes [ ] VolumeClaimTemplate ` yaml:"volumeClaimTemplates,omitempty" `
Templates [ ] Template ` yaml:"templates" `
}
2024-05-02 09:52:28 +02:00
func ( b * ArgoBuilder ) CreateDAG ( ) bool {
fmt . Println ( "list of branches : " , b . branches )
2024-05-06 17:01:48 +02:00
b . createTemplates ( )
2024-05-02 09:52:28 +02:00
b . createDAGstep ( )
2024-05-07 19:45:20 +02:00
b . createVolumes ( )
b . Workflow . Spec . Entrypoint = "dag"
b . Workflow . ApiVersion = "argoproj.io/v1alpha1"
b . Workflow . Kind = "Workflow"
b . Workflow . Metadata . GenerateName = "oc-test-" + generateName ( )
2024-05-03 10:58:06 +02:00
yamlified , err := yaml . Marshal ( b . Workflow )
2024-05-02 09:52:28 +02:00
if err != nil {
2024-05-03 10:58:06 +02:00
logs . Error ( "Could not transform object to yaml file" )
2024-05-06 17:01:48 +02:00
return false
2024-05-02 09:52:28 +02:00
}
fmt . Println ( string ( yamlified ) )
2024-05-03 10:58:06 +02:00
err = os . WriteFile ( "argo.yml" , [ ] byte ( yamlified ) , 0660 )
2024-05-07 19:45:20 +02:00
if err != nil {
2024-05-03 10:58:06 +02:00
logs . Error ( "Could not write the yaml file" )
2024-05-06 17:01:48 +02:00
return false
2024-05-03 10:58:06 +02:00
}
2024-05-02 09:52:28 +02:00
return true
}
2024-05-06 17:01:48 +02:00
func ( b * ArgoBuilder ) createTemplates ( ) {
2024-05-02 09:52:28 +02:00
2024-05-06 17:01:48 +02:00
for _ , comp := range b . graph . Computings {
temp_container := Container { Image : comp . Name }
temp_container . Command = getComputingCommands ( comp . Command )
temp_container . Args = getComputingArgs ( comp . Arguments )
input_names := getComputingEnvironmentName ( comp . Environment )
var inputs_container [ ] Parameter
for _ , name := range input_names {
inputs_container = append ( inputs_container , Parameter { Name : name } )
}
new_temp := Template { Name : comp . Name + "_" + comp . ID , Container : temp_container }
new_temp . Inputs . Parameters = inputs_container
2024-05-07 19:45:20 +02:00
new_temp . Container . VolumeMounts = append ( new_temp . Container . VolumeMounts , VolumeMount { Name : "workdir" , MountPath : "/mnt/vol" } ) // TODO : replace this with a search of the storage / data source name
b . Workflow . Spec . Templates = append ( b . Workflow . Spec . Templates , new_temp )
2024-05-06 17:01:48 +02:00
}
}
2024-05-02 09:52:28 +02:00
func ( b * ArgoBuilder ) createDAGstep ( ) {
2024-05-03 10:58:06 +02:00
new_dag := Dag { }
2024-05-02 09:52:28 +02:00
for _ , comp := range b . graph . Computings {
unique_name := comp . Name + "_" + comp . ID
2024-05-03 10:58:06 +02:00
step := Task { Name : unique_name , Template : unique_name }
2024-05-06 17:01:48 +02:00
comp_envs := getComputingEnvironment ( comp . Environment )
2024-05-07 11:43:12 +02:00
2024-05-06 17:01:48 +02:00
for name , value := range comp_envs {
step . Arguments . Parameters = append ( step . Arguments . Parameters , Parameter { Name : name , Value : value } )
}
2024-05-03 10:58:06 +02:00
2024-05-02 09:52:28 +02:00
// For each branch, check if the computing has a dependency
for _ , branch := range b . branches {
if b . componentInBranch ( comp . ID , branch ) {
// retrieves the name (computing.name_computing.ID)
dependency := b . getDependency ( comp . ID , branch )
if dependency != "" && ! slices . Contains ( step . Dependencies , dependency ) {
step . Dependencies = append ( step . Dependencies , dependency )
}
}
}
2024-05-03 10:58:06 +02:00
new_dag . Tasks = append ( new_dag . Tasks , step )
2024-05-02 09:52:28 +02:00
}
2024-05-03 10:58:06 +02:00
2024-05-07 19:45:20 +02:00
b . Workflow . Spec . Templates = append ( b . Workflow . Spec . Templates , Template { Name : "dag" , Dag : new_dag } )
}
2024-05-03 10:58:06 +02:00
2024-05-07 19:45:20 +02:00
func ( b * ArgoBuilder ) createVolumes ( ) {
// For testing purposes we only declare one volume, mounted in each computing
new_volume := VolumeClaimTemplate { }
new_volume . Metadata . Name = "workdir"
new_volume . Spec . AccessModes = [ ] string { "ReadWriteOnce" }
new_volume . Spec . Resources . Requests . Storage = "1Gi"
b . Workflow . Spec . Volumes = append ( b . Workflow . Spec . Volumes , new_volume )
2024-05-02 09:52:28 +02:00
}
2024-05-07 19:45:20 +02:00
func ( b * ArgoBuilder ) getDependency ( current_computing_id string , branch [ ] string ) string {
2024-05-02 09:52:28 +02:00
for i := len ( branch ) - 1 ; i >= 0 ; i -- {
current_link := b . graph . Links [ branch [ i ] ]
current_computing_found := false
if current_link . Source == current_computing_id || current_link . Destination == current_computing_id {
current_computing_found = true
}
if current_computing_found {
return b . findPreviousComputing ( current_computing_id , branch , i - 1 )
}
}
return ""
}
func ( b * ArgoBuilder ) componentInBranch ( component_id string , branch [ ] string ) bool {
for _ , link := range branch {
if b . graph . Links [ link ] . Source == component_id || b . graph . Links [ link ] . Destination == component_id {
return true
}
}
return false
}
func ( b * ArgoBuilder ) findPreviousComputing ( computing_id string , branch [ ] string , index int ) string {
for i := index ; i >= 0 ; i -- {
previousLink := b . graph . Links [ branch [ i ] ]
if previousLink . Source != computing_id && b . graph . getComponentType ( previousLink . Source ) == "computing" {
name := b . graph . getComponentName ( previousLink . Source ) + "_" + previousLink . Source
return name
}
if previousLink . Destination != computing_id && b . graph . getComponentType ( previousLink . Destination ) == "computing" {
name := b . graph . getComponentName ( previousLink . Destination ) + "_" + previousLink . Destination
return name
}
}
return ""
2024-05-06 17:01:48 +02:00
}
func getComputingCommands ( user_input string ) ( list_command [ ] string ) {
if len ( user_input ) == 0 {
return
}
list_command = strings . Split ( user_input , "," )
return
}
func getComputingArgs ( user_input [ ] string ) ( list_args [ ] string ) {
if len ( user_input ) == 0 {
return
}
for _ , arg := range user_input {
list_args = append ( list_args , "'" + arg + "'" )
}
return
}
// Currently implements code to overcome problems in data structure
func getComputingEnvironment ( user_input [ ] string ) ( map_env map [ string ] string ) {
if len ( user_input ) == 0 {
return
}
if ( len ( user_input ) == 1 ) {
user_input = strings . Split ( user_input [ 0 ] , "," )
}
map_env = make ( map [ string ] string , 0 )
for _ , str := range user_input {
new_pair := strings . Split ( str , "=" )
if ( len ( new_pair ) != 2 ) {
logs . Error ( "Error extracting the environment variable from " , str )
panic ( 0 )
}
2024-05-07 11:43:12 +02:00
map_env [ new_pair [ 0 ] ] = new_pair [ 1 ]
2024-05-06 17:01:48 +02:00
}
return
}
func getComputingEnvironmentName ( user_input [ ] string ) ( list_names [ ] string ) {
env_map := getComputingEnvironment ( user_input )
2024-05-07 11:43:12 +02:00
for name := range env_map {
2024-05-06 17:01:48 +02:00
list_names = append ( list_names , name )
}
2024-05-07 11:43:12 +02:00
2024-05-06 17:01:48 +02:00
return
2024-05-07 19:45:20 +02:00
}
func generateName ( ) ( Name string ) {
Name = fakelish . GenerateFakeWord ( 5 , 8 ) + "-" + fakelish . GenerateFakeWord ( 5 , 8 )
return
}
func testDoubleIndent ( w Workflow ) {
for _ , temp := range w . Spec . Templates {
if temp . Name == "dag" {
tasks := temp . Dag . Tasks
fmt . Println ( "name" )
printYAML ( tasks [ 0 ] . Name )
fmt . Println ( "template" )
printYAML ( tasks [ 0 ] . Template )
fmt . Println ( "dependencies" )
printYAML ( tasks [ 0 ] . Dependencies )
fmt . Println ( "arguments" )
printYAML ( tasks [ 0 ] . Arguments )
}
}
}
func printYAML ( data interface { } ) {
yamlData , err := yaml . Marshal ( data )
if err != nil {
fmt . Printf ( "Error marshalling YAML: %v\n" , err )
return
}
fmt . Println ( string ( yamlData ) )
2024-05-02 09:52:28 +02:00
}