Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions docs/modules/traits/pages/init-containers.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,17 @@ The following configuration options are available:

| init-containers.initTasks
| []string
| A list of init tasks to be executed with format `<name>;<container-image>;<container-command>`.
| A list of init tasks to be executed.
Each task accepts the format `<name>;<container-image>;<container-command>` or key=value format
`name=<name>;image=<image>;command=<command>;request-cpu=<quantity>;limit-cpu=<quantity>;request-memory=<quantity>;limit-memory=<quantity>`.
Resource keys (request-cpu, limit-cpu, request-memory, limit-memory) are optional and accept Kubernetes resource quantities.

| init-containers.sideCarTasks
| []string
| A list of sidecar tasks to be executed with format `<name>;<container-image>;<container-command>`.
| A list of sidecar tasks to be executed.
Each task accepts the format `<name>;<container-image>;<container-command>` or key=value format
`name=<name>;image=<image>;command=<command>;request-cpu=<quantity>;limit-cpu=<quantity>;request-memory=<quantity>;limit-memory=<quantity>`.
Resource keys (request-cpu, limit-cpu, request-memory, limit-memory) are optional and accept Kubernetes resource quantities.

|===

Expand Down
10 changes: 8 additions & 2 deletions pkg/apis/camel/v1/trait/init_containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,14 @@ package trait
type InitContainersTrait struct {
Trait `json:",inline" property:",squash"`

// A list of init tasks to be executed with format `<name>;<container-image>;<container-command>`.
// A list of init tasks to be executed.
// Each task accepts the format `<name>;<container-image>;<container-command>` or key=value format
// `name=<name>;image=<image>;command=<command>;request-cpu=<quantity>;limit-cpu=<quantity>;request-memory=<quantity>;limit-memory=<quantity>`.
// Resource keys (request-cpu, limit-cpu, request-memory, limit-memory) are optional and accept Kubernetes resource quantities.
InitTasks []string `json:"initTasks,omitempty" property:"init-tasks"`
// A list of sidecar tasks to be executed with format `<name>;<container-image>;<container-command>`.
// A list of sidecar tasks to be executed.
// Each task accepts the format `<name>;<container-image>;<container-command>` or key=value format
// `name=<name>;image=<image>;command=<command>;request-cpu=<quantity>;limit-cpu=<quantity>;request-memory=<quantity>;limit-memory=<quantity>`.
// Resource keys (request-cpu, limit-cpu, request-memory, limit-memory) are optional and accept Kubernetes resource quantities.
SidecarTasks []string `json:"sideCarTasks,omitempty" property:"sidecar-tasks"`
}
128 changes: 105 additions & 23 deletions pkg/trait/init_containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait"
"github.com/apache/camel-k/v2/pkg/util/defaults"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/utils/ptr"
)

Expand All @@ -38,11 +39,15 @@
)

type containerTask struct {
name string
image string
command string
isSidecar bool
env []corev1.EnvVar
name string
image string
command string
isSidecar bool
requestCPU string
requestMemory string
limitCPU string
limitMemory string
env []corev1.EnvVar
}

type initContainersTrait struct {
Expand Down Expand Up @@ -183,6 +188,24 @@
if task.isSidecar {
initCont.RestartPolicy = ptr.To(corev1.ContainerRestartPolicyAlways)
}
if task.requestCPU != "" || task.requestMemory != "" || task.limitCPU != "" || task.limitMemory != "" {
initCont.Resources = corev1.ResourceRequirements{
Requests: corev1.ResourceList{},
Limits: corev1.ResourceList{},
}
if task.requestCPU != "" {
initCont.Resources.Requests[corev1.ResourceCPU] = resource.MustParse(task.requestCPU)
}
if task.requestMemory != "" {
initCont.Resources.Requests[corev1.ResourceMemory] = resource.MustParse(task.requestMemory)
}
if task.limitCPU != "" {
initCont.Resources.Limits[corev1.ResourceCPU] = resource.MustParse(task.limitCPU)
}
if task.limitMemory != "" {
initCont.Resources.Limits[corev1.ResourceMemory] = resource.MustParse(task.limitMemory)
}
}
*containers = append(*containers, initCont)
}
}
Expand All @@ -194,31 +217,90 @@
t.tasks = make([]containerTask, len(t.InitTasks)+len(t.SidecarTasks))
i := 0
for _, task := range t.InitTasks {
split := strings.SplitN(task, ";", 3)
if len(split) != 3 {
return false, fmt.Errorf(`could not parse init container task "%s": format expected "name;container-image;command"`, task)
}
t.tasks[i] = containerTask{
name: split[0],
image: split[1],
command: split[2],
isSidecar: false,
parsed, err := parseSingleTask(task, false)
if err != nil {
return false, fmt.Errorf("could not parse init container task %q: %w", task, err)
}
t.tasks[i] = parsed
i++
}
for _, task := range t.SidecarTasks {
split := strings.SplitN(task, ";", 3)
if len(split) != 3 {
return false, fmt.Errorf(`could not parse sidecar container task "%s": format expected "name;container-image;command"`, task)
}
t.tasks[i] = containerTask{
name: split[0],
image: split[1],
command: split[2],
isSidecar: true,
parsed, err := parseSingleTask(task, true)
if err != nil {
return false, fmt.Errorf("could not parse sidecar container task %q: %w", task, err)
}
t.tasks[i] = parsed
i++
}

return true, nil
}

func parseSingleTask(task string, isSidecar bool) (containerTask, error) {
segments := strings.Split(task, ";")

var result containerTask
result.isSidecar = isSidecar

var commandParts []string

for _, seg := range segments {
if len(strings.TrimSpace(seg)) == 0 {
continue
}
if strings.Contains(seg, "=") {

Check failure on line 251 in pkg/trait/init_containers.go

View workflow job for this annotation

GitHub Actions / validate

`if strings.Contains(seg, "=")` has complex nested blocks (complexity: 8) (nestif)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to add a nolint statement to avoid the warn

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: added //nolint:nestif, fixed the if-else chain, and added tests for mixed format (my-task;my-image;my-command;request-cpu=123m). PTAL.

kv := strings.SplitN(seg, "=", 2)
key, value := strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])
switch key {
case "name":
result.name = value
case "image":
result.image = value
case "command":
commandParts = append(commandParts, value)
case "request-cpu":
if _, err := resource.ParseQuantity(value); err != nil {
return containerTask{}, fmt.Errorf("invalid request-cpu value %q: %w", value, err)
}
result.requestCPU = value
case "request-memory":
if _, err := resource.ParseQuantity(value); err != nil {
return containerTask{}, fmt.Errorf("invalid request-memory value %q: %w", value, err)
}
result.requestMemory = value
case "limit-cpu":
if _, err := resource.ParseQuantity(value); err != nil {
return containerTask{}, fmt.Errorf("invalid limit-cpu value %q: %w", value, err)
}
result.limitCPU = value
case "limit-memory":
if _, err := resource.ParseQuantity(value); err != nil {
return containerTask{}, fmt.Errorf("invalid limit-memory value %q: %w", value, err)
}
result.limitMemory = value
default:
// Forward compatibility: unknown keys are appended to command
commandParts = append(commandParts, seg)
}
} else {
// Positional segment: fill first unset field
if result.name == "" {

Check failure on line 287 in pkg/trait/init_containers.go

View workflow job for this annotation

GitHub Actions / validate

ifElseChain: rewrite if-else to switch statement (gocritic)
result.name = seg
} else if result.image == "" {
result.image = seg
} else {
commandParts = append(commandParts, seg)
}
}
}

if len(commandParts) > 0 {
result.command = strings.Join(commandParts, ";")
}

if result.name == "" || result.image == "" {
return containerTask{}, fmt.Errorf("name and image are required (format: %q)", "name;image;command or name=...;image=...")
}

return result, nil
}
Loading
Loading