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 {
2024-05-14 15:15:30 +02:00
image_name := strings . Split ( comp . Command , " " ) [ 0 ]
temp_container := Container { Image : image_name } // TODO : edit computing model to store the container name:version
temp_container . Command = getComputingCommands ( comp . Command )
temp_container . Args = getComputingArgs ( comp . Arguments , comp . Command )
2024-05-06 17:01:48 +02:00
input_names := getComputingEnvironmentName ( comp . Environment )
var inputs_container [ ] Parameter
for _ , name := range input_names {
inputs_container = append ( inputs_container , Parameter { Name : name } )
}
2024-05-14 15:15:30 +02:00
argo_name := getArgoName ( comp . Name , comp . ID )
new_temp := Template { Name : argo_name , Container : temp_container }
2024-05-06 17:01:48 +02:00
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 {
2024-05-14 15:15:30 +02:00
unique_name := getArgoName ( 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 ) {
2024-05-14 15:15:30 +02:00
// retrieves the name (computing.name-computing.ID)
2024-05-02 09:52:28 +02:00
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" {
2024-05-14 15:15:30 +02:00
name := getArgoName ( b . graph . getComponentName ( previousLink . Source ) , previousLink . Source )
2024-05-02 09:52:28 +02:00
return name
}
if previousLink . Destination != computing_id && b . graph . getComponentType ( previousLink . Destination ) == "computing" {
2024-05-14 15:15:30 +02:00
name := getArgoName ( b . graph . getComponentName ( previousLink . Destination ) , previousLink . Destination )
2024-05-02 09:52:28 +02:00
return name
}
}
return ""
2024-05-06 17:01:48 +02:00
}
func getComputingCommands ( user_input string ) ( list_command [ ] string ) {
2024-05-14 15:15:30 +02:00
user_input = removeImageName ( user_input )
2024-05-06 17:01:48 +02:00
if len ( user_input ) == 0 {
return
}
2024-05-14 15:15:30 +02:00
list_command = strings . Split ( user_input , " " )
for i := range list_command {
list_command [ i ] = list_command [ i ]
}
2024-05-06 17:01:48 +02:00
return
}
2024-05-14 15:15:30 +02:00
func getComputingArgs ( user_input [ ] string , command string ) ( list_args [ ] string ) {
2024-05-06 17:01:48 +02:00
if len ( user_input ) == 0 {
return
}
2024-05-14 15:15:30 +02:00
// quickfix that might need improvement
if ( strings . Contains ( command , "sh -c" ) ) {
list_args = append ( list_args , strings . Join ( user_input , " " ) )
return
}
2024-05-06 17:01:48 +02:00
for _ , arg := range user_input {
2024-05-14 15:15:30 +02:00
list_args = append ( list_args , arg )
2024-05-06 17:01:48 +02:00
}
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
}
2024-05-14 15:15:30 +02:00
func getArgoName ( raw_name string , component_id string ) ( formatedName string ) {
formatedName = strings . ReplaceAll ( raw_name , " " , "-" )
formatedName += "-" + component_id
formatedName = strings . ToLower ( formatedName )
return
2024-05-07 19:45:20 +02:00
}
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-14 15:15:30 +02:00
}
func removeImageName ( user_input string ) string {
// First command is the name of the container for now
if len ( strings . Split ( user_input , " " ) ) == 1 {
return ""
}
slice_input := strings . Split ( user_input , " " )
new_slice := slice_input [ 1 : ]
user_input = strings . Join ( new_slice , " " )
return user_input
2024-05-02 09:52:28 +02:00
}