Skip to content

Commit 71f4856

Browse files
authored
fix(plumber): improve job matrix validation - env. var dups (#417)
## 📝 Description Adds a check for duplicate environment variable names in job matrix validation. More details in [the task](renderedtext/tasks#8170). ## ✅ Checklist - [x] I have tested this change - [ ] This change requires documentation update
1 parent 0eff0a7 commit 71f4856

File tree

3 files changed

+629
-199
lines changed

3 files changed

+629
-199
lines changed

plumber/ppl/lib/ppl/definition_reviser/job_matrix_validator.ex

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@ defmodule Ppl.DefinitionReviser.JobMatrixValidator do
22
@moduledoc """
33
This module serves to validate that all job matrix values are provided
44
as a list of strings either explicitly or after evaluation by SPC command line tool.
5+
6+
It also validates that there are no duplicate environment variable names in the job matrix.
7+
It also validates that the total product size of the matrix (product of number of values of each environment variable) is not too large.
58
"""
69

710
alias Util.ToTuple
811

12+
@max_size 100
13+
914
def validate(definition) do
1015
with {:ok, definition} <- do_validate_job_matrix_values(definition, "blocks"),
1116
{:ok, definition} <- do_validate_job_matrix_values(definition, "after_pipeline") do
1217
ToTuple.ok(definition)
18+
else
19+
{:error, _} = error -> error
1320
end
1421
end
1522

@@ -43,9 +50,29 @@ defmodule Ppl.DefinitionReviser.JobMatrixValidator do
4350
jobs = get_in(block, ["build", "jobs"]) |> List.wrap()
4451
block_name = get_in(block, ["name"])
4552

46-
case Enum.find_value(jobs, &validate_job_matrices(block_name, &1)) do
47-
nil -> {:ok, [block] ++ block_acc}
48-
{:error, error} -> {:error, error}
53+
# Calculate total matrix size across all jobs in the block
54+
total_result =
55+
Enum.reduce_while(jobs, {:ok, 0}, fn job, {:ok, total_size} ->
56+
case validate_job_matrices(block_name, job) do
57+
nil -> {:cont, {:ok, total_size}}
58+
{:ok, matrix_size} -> {:cont, {:ok, total_size + matrix_size}}
59+
{:error, _} = error -> {:halt, error}
60+
end
61+
end)
62+
63+
case total_result do
64+
{:ok, total_size} ->
65+
if total_size > @max_size do
66+
{:error,
67+
{:malformed,
68+
"Total matrix size exceeds maximum allowed size (#{@max_size}) in block '#{block_name}'. " <>
69+
"The matrix product size is calculated as the product of the number of values for each environment variable."}}
70+
else
71+
{:ok, [block] ++ block_acc}
72+
end
73+
74+
{:error, _} = error ->
75+
error
4976
end
5077
end
5178

@@ -56,10 +83,39 @@ defmodule Ppl.DefinitionReviser.JobMatrixValidator do
5683
job_name = Map.get(job, "name")
5784

5885
if Map.has_key?(job, "matrix") and is_list(matrix_values) do
59-
Enum.find_value(matrix_values, &check_matrix_values(block_name, job_name, &1))
86+
case validate_job_matrix(block_name, job_name, matrix_values, job) do
87+
{:ok, matrix_size} -> {:ok, matrix_size}
88+
{:error, _} = error -> error
89+
nil -> nil
90+
end
6091
end
6192
end
6293

94+
defp validate_job_matrix(block_name, job_name, matrix_values, job) do
95+
with nil <- check_for_duplicate_env_vars(block_name, job_name, matrix_values),
96+
nil <- Enum.find_value(matrix_values, &check_matrix_values(block_name, job_name, &1)),
97+
{:ok, matrix_size} <- check_matrix_product_size(block_name, job_name, matrix_values, job) do
98+
{:ok, matrix_size}
99+
else
100+
{:error, _} = error -> error
101+
end
102+
end
103+
104+
defp check_for_duplicate_env_vars(block_name, job_name, matrix_values) do
105+
env_var_names_count =
106+
Enum.reduce(matrix_values, %{}, fn matrix_entry, acc ->
107+
env_var = Map.get(matrix_entry, "env_var")
108+
Map.update(acc, env_var, 1, &(&1 + 1))
109+
end)
110+
111+
Enum.find_value(env_var_names_count, fn {env_var_name, count} ->
112+
if count > 1 do
113+
{:error,
114+
{:malformed, duplicate_env_var_error_message(block_name, job_name, env_var_name)}}
115+
end
116+
end)
117+
end
118+
63119
defp check_matrix_values(block_name, job_name, matrix_entry) do
64120
env_var = get_in(matrix_entry, ["env_var"])
65121
values = get_in(matrix_entry, ["values"])
@@ -72,4 +128,29 @@ defmodule Ppl.DefinitionReviser.JobMatrixValidator do
72128
defp error_mesasge(block_name, job_name, env_var) do
73129
"Matrix values for env_var '#{env_var}' (block '#{block_name}', job '#{job_name}' must be a non-empty list of strings."
74130
end
131+
132+
defp duplicate_env_var_error_message(block_name, job_name, env_var_name) do
133+
"Duplicate environment variable(s): '#{env_var_name}' in job matrix (block '#{block_name}', job '#{job_name}')."
134+
end
135+
136+
def check_matrix_product_size(block_name, job_name, matrix_values, _job) do
137+
matrix_size =
138+
Enum.reduce(matrix_values, 1, fn matrix_entry, acc ->
139+
values = get_in(matrix_entry, ["values"])
140+
if is_list(values), do: min(acc * length(values), @max_size + 1), else: acc
141+
end)
142+
143+
if matrix_size > @max_size do
144+
{:error,
145+
{:malformed,
146+
matrix_product_size_error_message(block_name, job_name, matrix_size, @max_size)}}
147+
else
148+
{:ok, matrix_size}
149+
end
150+
end
151+
152+
defp matrix_product_size_error_message(block_name, job_name, size, max_size) do
153+
"Matrix product size exceeds maximum allowed size (#{max_size}) in job matrix (block '#{block_name}', job '#{job_name}'). " <>
154+
"The matrix product size is calculated as the product of the number of values for each environment variable."
155+
end
75156
end

0 commit comments

Comments
 (0)