-
Notifications
You must be signed in to change notification settings - Fork 24
[OCTRL-1091] Environment controller changes for ECS #828
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4100,6 +4100,8 @@ spec: | |
| type: array | ||
| name: | ||
| type: string | ||
| taskID: | ||
| type: string | ||
| required: | ||
| - argsCLI | ||
| - argsTransition | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,22 +2,30 @@ apiVersion: aliecs.alice.cern/v1alpha1 | |
| kind: TaskTemplate | ||
| metadata: | ||
| name: readout | ||
| namespace: alice-tasks | ||
| spec: | ||
| envVars: | ||
| - O2_DETECTOR | ||
| - O2_PARTITION | ||
| - OCC_CONTROL_PORT | ||
| - O2_SYSTEM | ||
| - O2_ROLE | ||
| pod: | ||
| hostNetwork: true | ||
| hostIPC: true | ||
| securityContext: | ||
| fsGroup: 1100 | ||
| supplementalGroups: [10, 1105] | ||
|
Comment on lines
+15
to
+17
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it looks like this does not correspond to any of the three changes? can you extract to a separate PR or amend the commit? |
||
| containers: | ||
| - name: readout | ||
| image: gitlab-registry.cern.ch/aliceo2group/dockerfiles/alma9-flp-node/readout:1 | ||
| command: ["/opt/o2/bin/o2-readout-exe"] | ||
| env: | ||
| - name: OCC_CONTROL_PORT | ||
| value: "47101" | ||
| securityContext: | ||
| privileged: true | ||
| runAsUser: 1100 | ||
| runAsGroup: 1100 | ||
|
Comment on lines
+27
to
+28
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. these neither |
||
| volumeMounts: | ||
| - name: host-shm | ||
| mountPath: /dev/shm | ||
|
|
@@ -53,4 +61,3 @@ spec: | |
| - name: gitlab-registry-secret | ||
| control: | ||
| mode: "direct" | ||
| # port: ${OCC_CONTROL_PORT} # to be filled in | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,6 +30,7 @@ import ( | |
| "maps" | ||
| "slices" | ||
| "strconv" | ||
| "strings" | ||
|
|
||
| v1 "k8s.io/api/core/v1" | ||
| k8serrors "k8s.io/apimachinery/pkg/api/errors" | ||
|
|
@@ -53,7 +54,7 @@ type EnvironmentReconciler struct { | |
| } | ||
|
|
||
| func (r *EnvironmentReconciler) runTasksFromReferenceOnNode(ctx context.Context, taskReferences []aliecsv1alpha1.TaskReference, | ||
| nodename string, req ctrl.Request, environment *aliecsv1alpha1.Environment, log logr.Logger, | ||
| nodename string, resolvedNodename string, req ctrl.Request, environment *aliecsv1alpha1.Environment, log logr.Logger, | ||
| ) (*ctrl.Result, error) { | ||
| for _, taskReference := range taskReferences { | ||
| log.Info("geting stored template for task", "task", taskReference.Name) | ||
|
|
@@ -85,31 +86,44 @@ func (r *EnvironmentReconciler) runTasksFromReferenceOnNode(ctx context.Context, | |
|
|
||
| // TODO: regarding error handling. Is it correct to stop while handling one task? this will fail the whole deployment, | ||
| // which might not be desirable outcome especially for non-critical tasks | ||
| if foundIdx := slices.IndexFunc(taskReference.Env, func(envVar v1.EnvVar) bool { return envVar.Name == "OCC_CONTROL_PORT" }); foundIdx == -1 { | ||
| log.Error(fmt.Errorf("didn't find OCC_CONTROL_PORT in env"), "failed to fill in env vars from template") | ||
| return &reconcile.Result{}, nil | ||
| } else { | ||
| port, err := strconv.Atoi(taskReference.Env[foundIdx].Value) | ||
| if err != nil { | ||
| log.Error(fmt.Errorf("found OCC_CONTROL_PORT isn't convertible to number "), "failed to fill in env vars from template") | ||
| return &reconcile.Result{}, nil | ||
| } | ||
| task.Spec.Control.Port = port | ||
| } | ||
| task.Spec.Control.Port = template.Spec.Control.Port | ||
|
|
||
| if task.Spec.Arguments == nil { | ||
| task.Spec.Arguments = make(map[string]string) | ||
| } | ||
|
|
||
| // TODO: check for containers! | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove todo? |
||
| task.Spec.Pod.Containers[0].Env = append(task.Spec.Pod.Containers[0].Env, taskReference.Env...) | ||
| existing := make(map[string]int, len(task.Spec.Pod.Containers[0].Env)) | ||
| for i, e := range task.Spec.Pod.Containers[0].Env { | ||
| existing[e.Name] = i | ||
| } | ||
| for _, e := range taskReference.Env { | ||
| if i, ok := existing[e.Name]; ok { | ||
| task.Spec.Pod.Containers[0].Env[i] = e | ||
| } else { | ||
| task.Spec.Pod.Containers[0].Env = append(task.Spec.Pod.Containers[0].Env, e) | ||
| } | ||
| } | ||
| task.Spec.Pod.Containers[0].Args = append(task.Spec.Pod.Containers[0].Args, taskReference.ArgsCLI...) | ||
|
|
||
| for _, envVar := range task.Spec.Pod.Containers[0].Env { | ||
| if envVar.Name == "OCC_CONTROL_PORT" { | ||
| if port, err := strconv.Atoi(envVar.Value); err == nil { | ||
| task.Spec.Control.Port = port | ||
| } | ||
| break | ||
| } | ||
| } | ||
| maps.Copy(task.Spec.Arguments, taskReference.ArgsTransition) | ||
|
|
||
| task.Spec.Pod.NodeName = nodename | ||
| task.Spec.Pod.NodeName = resolvedNodename | ||
| task.Spec.NodeName = resolvedNodename | ||
| task.Spec.State = environment.Spec.State | ||
|
|
||
| task.Labels = labels(environment, nodename) | ||
| if taskReference.TaskID != "" { | ||
| task.Labels["taskID"] = taskReference.TaskID | ||
| } | ||
|
|
||
| if err := controllerutil.SetControllerReference(environment, task, r.Scheme); err != nil { | ||
| log.Error(err, "failed to set controller reference", "task", task.Name) | ||
|
|
@@ -128,7 +142,7 @@ func (r *EnvironmentReconciler) runTasksFromReferenceOnNode(ctx context.Context, | |
| } | ||
|
|
||
| func (r *EnvironmentReconciler) runTaskFromDefinitionOnNode(ctx context.Context, taskDefs []aliecsv1alpha1.TaskDefinition, | ||
| nodename string, req ctrl.Request, environment *aliecsv1alpha1.Environment, log logr.Logger, | ||
| nodename string, resolvedNodename string, req ctrl.Request, environment *aliecsv1alpha1.Environment, log logr.Logger, | ||
| ) (*ctrl.Result, error) { | ||
| for _, taskDef := range taskDefs { | ||
| task := &aliecsv1alpha1.Task{} | ||
|
|
@@ -148,7 +162,8 @@ func (r *EnvironmentReconciler) runTaskFromDefinitionOnNode(ctx context.Context, | |
| maps.Copy(task.Spec.Arguments, taskDef.Spec.Arguments) | ||
| } | ||
|
|
||
| task.Spec.Pod.NodeName = nodename | ||
| task.Spec.Pod.NodeName = resolvedNodename | ||
| task.Spec.NodeName = resolvedNodename | ||
| task.Spec.State = environment.Spec.State | ||
|
|
||
| task.Labels = labels(environment, nodename) | ||
|
|
@@ -203,24 +218,31 @@ func (r *EnvironmentReconciler) Reconcile(ctx context.Context, req ctrl.Request) | |
| } | ||
|
|
||
| if environment.Status.State == "" { | ||
| nodeList := &v1.NodeList{} | ||
| if err := r.List(ctx, nodeList); err != nil { | ||
| return ctrl.Result{}, fmt.Errorf("listing nodes: %w", err) | ||
| } | ||
|
|
||
| for nodename, tasksReferences := range environment.TaskTemplates.Tasks { | ||
| log.Info("creating tasks for hostname from references", "hostname", nodename, "number of tasks", len(tasksReferences)) | ||
| if res, err := r.checkNodeExistence(ctx, nodename); err != nil { | ||
| return res, err | ||
| resolvedName, err := resolveNodeName(nodeList.Items, nodename) | ||
| if err != nil { | ||
| return ctrl.Result{}, err | ||
| } | ||
|
|
||
| if res, err := r.runTasksFromReferenceOnNode(ctx, tasksReferences, nodename, req, environment, log); res != nil { | ||
| if res, err := r.runTasksFromReferenceOnNode(ctx, tasksReferences, nodename, resolvedName, req, environment, log); res != nil { | ||
| return *res, err | ||
| } | ||
| } | ||
|
|
||
| for nodename, taskTemplates := range environment.Spec.Tasks { | ||
| log.Info("creating tasks for hostname from definitions", "hostname", nodename, "number of tasks", len(taskTemplates)) | ||
| if res, err := r.checkNodeExistence(ctx, nodename); err != nil { | ||
| return res, err | ||
| resolvedName, err := resolveNodeName(nodeList.Items, nodename) | ||
| if err != nil { | ||
| return ctrl.Result{}, err | ||
| } | ||
|
|
||
| if res, err := r.runTaskFromDefinitionOnNode(ctx, taskTemplates, nodename, req, environment, log); res != nil { | ||
| if res, err := r.runTaskFromDefinitionOnNode(ctx, taskTemplates, nodename, resolvedName, req, environment, log); res != nil { | ||
| return *res, err | ||
| } | ||
| } | ||
|
|
@@ -268,15 +290,28 @@ func (r *EnvironmentReconciler) Reconcile(ctx context.Context, req ctrl.Request) | |
| return ctrl.Result{}, nil | ||
| } | ||
|
|
||
| func (r *EnvironmentReconciler) checkNodeExistence(ctx context.Context, nodename string) (ctrl.Result, error) { | ||
| node := &v1.Node{} | ||
| if err := r.Get(ctx, types.NamespacedName{Name: nodename}, node); err != nil { | ||
| if k8serrors.IsNotFound(err) { | ||
| return ctrl.Result{}, fmt.Errorf("node %s not found in cluster", nodename) | ||
| // resolveNodeName resolves a short node name (e.g. "flp001") to the full name known to | ||
| // the cluster (e.g. "flp001.cern.ch") by checking whether any dot-separated component | ||
| // of a node's name matches exactly. Returns an error if zero or more than one node matches. | ||
| func resolveNodeName(nodes []v1.Node, nodename string) (string, error) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use "nodeName" or "nodename" consistently, i.e. "name" either always starting upper or lower case. IMO "nodeName" is better, since it's two words, but I leave the final decision to you. |
||
| var matches []string | ||
| for _, node := range nodes { | ||
| for _, part := range strings.Split(node.Name, ".") { | ||
| if part == nodename { | ||
| matches = append(matches, node.Name) | ||
| break | ||
| } | ||
| } | ||
| return ctrl.Result{}, err | ||
| } | ||
| return ctrl.Result{}, nil | ||
|
|
||
| switch len(matches) { | ||
| case 0: | ||
| return "", fmt.Errorf("node %q not found in cluster", nodename) | ||
| case 1: | ||
| return matches[0], nil | ||
| default: | ||
| return "", fmt.Errorf("node name %q is ambiguous, matched: %v", nodename, matches) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i appreciate being paranoic! |
||
| } | ||
| } | ||
|
|
||
| func aggregateState(tasks []aliecsv1alpha1.Task, previousState string) string { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a bit lost why ".cern.ch" is needed here, but should be absent in the environment templates.