oc-monitord/models/template.go

308 lines
9.0 KiB
Go
Raw Normal View History

package models
2024-10-11 13:44:16 +02:00
import (
2025-06-12 14:01:16 +02:00
"encoding/json"
"fmt"
2024-10-11 13:44:16 +02:00
"strings"
2025-06-12 14:01:16 +02:00
w "cloud.o-forge.io/core/oc-lib/models/workflow"
"cloud.o-forge.io/core/oc-lib/models/workflow/graph"
"cloud.o-forge.io/core/oc-lib/models/common/enum"
2025-02-05 08:36:26 +01:00
"cloud.o-forge.io/core/oc-lib/models/common/models"
2025-01-13 14:05:21 +01:00
"cloud.o-forge.io/core/oc-lib/models/resources"
2024-10-11 13:44:16 +02:00
)
type Parameter struct {
Name string `yaml:"name,omitempty"`
Value string `yaml:"value,omitempty"`
}
type Container struct {
Image string `yaml:"image"`
Command []string `yaml:"command,omitempty,flow"`
Args []string `yaml:"args,omitempty,flow"`
VolumeMounts []VolumeMount `yaml:"volumeMounts,omitempty"`
}
2024-10-11 13:44:16 +02:00
func (c *Container) AddVolumeMount(volumeMount VolumeMount, volumes []VolumeMount) []VolumeMount {
for _, vm := range c.VolumeMounts {
if vm.Name == volumeMount.Name {
return volumes
}
}
c.VolumeMounts = append(c.VolumeMounts, volumeMount)
for _, vm := range c.VolumeMounts {
for _, v := range volumes {
if vm.Name == v.Name {
return volumes
}
}
}
volumes = append(volumes, volumeMount)
return volumes
}
type Task struct {
Name string `yaml:"name"`
Template string `yaml:"template"`
Dependencies []string `yaml:"dependencies,omitempty"`
Arguments struct {
Parameters []Parameter `yaml:"parameters,omitempty"`
} `yaml:"arguments,omitempty"`
}
2025-06-12 14:01:16 +02:00
func NewTask(processingName string, graphItemID string) *Task {
unique_name := GetArgoName(processingName, graphItemID)
return &Task{
Name: unique_name,
Template: unique_name,
}
}
func (t *Task) BindToArgo(
dag *Dag,
graphItemID string,
originWf *w.Workflow,
processing *resources.ProcessingResource,
firstItems, lastItems []string,
) (*Dag, []string, []string) {
if instance := processing.GetSelectedInstance(); instance != nil {
t.addParams(instance.(*resources.ProcessingInstance).Env)
t.addParams(instance.(*resources.ProcessingInstance).Inputs)
t.addParams(instance.(*resources.ProcessingInstance).Outputs)
}
t.Dependencies = TransformDepsToArgo(originWf.GetDependencies(graphItemID))
name := ""
if originWf.Graph.Items[graphItemID].Processing != nil {
name = originWf.Graph.Items[graphItemID].Processing.GetName()
}
if originWf.Graph.Items[graphItemID].Workflow != nil {
name = originWf.Graph.Items[graphItemID].Workflow.GetName()
}
if len(t.Dependencies) == 0 && name != "" {
firstItems = append(firstItems, GetArgoName(name, graphItemID))
}
if deps := originWf.IsDependancy(graphItemID); len(deps) == 0 && name != "" {
lastItems = append(lastItems, GetArgoName(name, graphItemID))
}
dag.Tasks = append(dag.Tasks, *t)
return dag, firstItems, lastItems
}
func (t *Task) addParams(params []models.Param) {
for _, value := range params {
t.Arguments.Parameters = append(t.Arguments.Parameters, Parameter{
Name: value.Name,
Value: value.Value,
})
}
}
func (t *Task) GetDeps(name string) (int, string) {
for i, deps := range t.Dependencies {
if strings.Contains(deps, name) {
return i, deps
}
}
return 0, ""
}
type Dag struct {
Tasks []Task `yaml:"tasks,omitempty"`
}
2025-06-12 14:01:16 +02:00
func (d *Dag) GetTask(taskName string) *Task {
for _, task := range d.Tasks {
if strings.Contains(task.Name, taskName) {
return &task
}
}
return nil
}
type TemplateMetadata struct {
2025-06-12 14:01:16 +02:00
Labels map[string]string `yaml:"labels,omitempty"`
Annotations map[string]string `yaml:"annotations,omitempty"`
}
2025-02-14 12:00:29 +01:00
type Secret struct {
Name string `yaml:"name"`
Key string `yaml:"key"`
}
2025-06-12 14:01:16 +02:00
func NewSecret(name string, key string) *Secret {
return &Secret{Name: name, Key: key + "-key"}
}
2025-02-05 08:36:26 +01:00
type Key struct {
2025-02-14 12:00:29 +01:00
Key string `yaml:"key"`
Bucket string `yaml:"bucket"`
EndPoint string `yaml:"endpoint"`
Insecure bool `yaml:"insecure"`
AccessKeySecret *Secret `yaml accessKeySecret`
SecretKeySecret *Secret `yaml secretKeySecret`
2025-02-05 08:36:26 +01:00
}
type Artifact struct {
Name string `yaml:"name"`
Path string `yaml:"path"`
S3 *Key `yaml:"s3,omitempty"`
}
2025-06-12 14:01:16 +02:00
func NewArtifact(name string, rw graph.StorageProcessingGraphLink, params []models.Param, template Template) *Artifact {
if rw.Write {
name += "-" + rw.Destination + "-input-write"
} else {
name = "-" + rw.Destination + "-input-read"
}
return &Artifact{
Name: name,
Path: template.ReplacePerEnv(rw.Source, params),
}
}
func (a *Artifact) BindToArgo(storageType enum.StorageType, rw graph.StorageProcessingGraphLink, params []models.Param, template Template) {
if rw.Write {
template.Outputs.Artifacts = append(template.Inputs.Artifacts, *a)
} else {
template.Inputs.Artifacts = append(template.Outputs.Artifacts, *a)
}
}
func (a *Artifact) bindS3(rw graph.StorageProcessingGraphLink, params []models.Param, template Template) {
a.S3 = &Key{
Key: template.ReplacePerEnv(rw.Destination+"/"+rw.FileName, params),
Insecure: true, // temporary
}
/* sel := storage.GetSelectedInstance()
if sel != nil {
if sel.(*resources.StorageResourceInstance).Credentials != nil {
tool, err := tools2.NewService(conf.GetConfig().Mode)
if err != nil || tool == nil {
logger.Error().Msg("Could not create the access secret")
} else {
id, err := tool.CreateAccessSecret(namespace,
sel.(*resources.StorageResourceInstance).Credentials.Login,
sel.(*resources.StorageResourceInstance).Credentials.Pass)
if err == nil {
a.S3.AccessKeySecret = NewSecret(id, "access")
a.S3.SecretKeySecret = NewSecret(id, "secret")
}
}
}
source := sel.(*resources.StorageResourceInstance).Source
a.S3.Key = strings.ReplaceAll(strings.ReplaceAll(a.S3.Key, source+"/", ""), source, "")
splits := strings.Split(a.S3.EndPoint, "/")
if len(splits) > 1 {
a.S3.Bucket = splits[0]
a.S3.EndPoint = strings.Join(splits[1:], "/")
} else {
a.S3.Bucket = splits[0]
}
} */
}
2025-02-05 08:36:26 +01:00
type InOut struct {
Parameters []Parameter `yaml:"parameters"`
Artifacts []Artifact `yaml:"artifacts,omitempty"`
}
type Template struct {
2025-02-05 08:36:26 +01:00
Name string `yaml:"name"`
Inputs InOut `yaml:"inputs,omitempty"`
Outputs InOut `yaml:"outputs,omitempty"`
2024-10-11 13:44:16 +02:00
Container Container `yaml:"container,omitempty"`
Dag *Dag `yaml:"dag,omitempty"`
Metadata TemplateMetadata `yaml:"metadata,omitempty"`
2024-10-11 13:44:16 +02:00
Resource ServiceResource `yaml:"resource,omitempty"`
}
func (template *Template) CreateContainer(processing *resources.ProcessingResource, dag *Dag) {
2025-02-05 08:36:26 +01:00
instance := processing.GetSelectedInstance()
if instance == nil {
return
}
inst := instance.(*resources.ProcessingInstance)
container := Container{Image: inst.Access.Container.Image}
2024-10-11 13:44:16 +02:00
if container.Image == "" {
return
}
container.Command = []string{"sh", "-c"} // all is bash
2025-02-05 08:36:26 +01:00
for _, v := range inst.Env {
template.Inputs.Parameters = append(template.Inputs.Parameters, Parameter{Name: v.Name})
}
for _, v := range inst.Inputs {
template.Inputs.Parameters = append(template.Inputs.Parameters, Parameter{Name: v.Name})
2024-10-11 13:44:16 +02:00
}
2025-02-05 08:36:26 +01:00
for _, v := range inst.Inputs {
template.Outputs.Parameters = append(template.Inputs.Parameters, Parameter{Name: v.Name})
2024-10-11 13:44:16 +02:00
}
2025-02-05 08:36:26 +01:00
cmd := strings.ReplaceAll(inst.Access.Container.Command, container.Image, "")
2025-02-05 08:36:26 +01:00
for _, a := range strings.Split(cmd, " ") {
container.Args = append(container.Args, template.ReplacePerEnv(a, inst.Env))
}
for _, a := range strings.Split(inst.Access.Container.Args, " ") {
container.Args = append(container.Args, template.ReplacePerEnv(a, inst.Env))
}
container.Args = []string{strings.Join(container.Args, " ")}
2024-10-11 13:44:16 +02:00
template.Container = container
}
2025-02-05 08:36:26 +01:00
func (template *Template) ReplacePerEnv(arg string, envs []models.Param) string {
for _, v := range envs {
if strings.Contains(arg, v.Name) {
value := "{{ inputs.parameters." + v.Name + " }}"
arg = strings.ReplaceAll(arg, v.Name, value)
arg = strings.ReplaceAll(arg, "$"+v.Name, value)
arg = strings.ReplaceAll(arg, "$", "")
2024-10-11 13:44:16 +02:00
}
}
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
2025-06-12 14:01:16 +02:00
func (t *Template) AddAdmiraltyAnnotations(peerID, namespace string) error {
if t.Metadata.Annotations == nil {
t.Metadata.Annotations = make(map[string]string)
}
2025-06-12 14:01:16 +02:00
const key = "admiralty.io/multi-cluster-scheduler"
var annotation SchedulerAnnotation
// Parse existing annotation if it exists
if val, ok := t.Metadata.Annotations[key]; ok && val != "" {
if err := json.Unmarshal([]byte(val), &annotation); err != nil {
return fmt.Errorf("failed to parse existing scheduler annotation: %w", err)
}
}
// Add new affinity
annotation.Affinities = append(annotation.Affinities, affinity{
Cluster: "target-" + peerID + "-" + namespace,
Namespace: namespace,
})
// Encode back to JSON
bytes, err := json.Marshal(annotation)
if err != nil {
return fmt.Errorf("failed to encode scheduler annotation: %w", err)
}
t.Metadata.Annotations[key] = string(bytes)
return nil
}
type affinity struct {
Cluster string `json:"cluster"`
Namespace string `json:"namespace"`
}
type SchedulerAnnotation struct {
Affinities []affinity `json:"affinities"`
}