-
Notifications
You must be signed in to change notification settings - Fork 852
executor: expand args in build logs #6329
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: main
Are you sure you want to change the base?
Conversation
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: flouthoc The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
@vrothberg @nalind PTAL |
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.
Changes and tests LGTM.
I didn't check, but maybe there's a similar behavior with ENV?
Thanks so much for the quick PR 🙏
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.
This produces some weird corner cases, both with this patch and with docker build:
FROM busybox
ARG CMD=pwd
RUN CMD=ls; echo $CMD
imagebuildah/executor.go
Outdated
| argsCopy[i], err = imagebuilder.ProcessWord(args[i].(string), userArgs) | ||
| if err != nil { | ||
| argsCopy[i] = args[i] | ||
| fmt.Printf("while replacing arg variables with values for format in logging %q: %w\n", args[i], err) |
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.
Why is this error message sent to stdout rather than one of the output destinations that's been specified for logging?
imagebuildah/executor.go
Outdated
| headingArgs := argsMapToSlice(stage.Builder.HeadingArgs) | ||
| userArgs := argsMapToSlice(stage.Builder.Args) | ||
| userArgs = append(builtinArgs, append(userArgs, headingArgs...)...) | ||
| argsCopy := args |
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.
If this is meant to create a copy of args, use slices.Clone(), otherwise they're both still pointing at the same array.
tests/bud.bats
Outdated
| _EOF | ||
|
|
||
| run_buildah build $WITH_POLICY_JSON -t ${target}-undefined -f $contextdir/Containerfile2 $contextdir | ||
| expect_output --substring "RUN echo " |
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.
This would match whether a value was printed or not.
Similar behavior exists in |
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.
This produces some weird corner cases, both with this patch and with
docker build:FROM busybox ARG CMD=pwd RUN CMD=ls; echo $CMDSimilar behavior exists in
buildkitas well I think this is bug in bothbuildahanddocker. Should we fix it here ?
You don't event need build args to trigger this:
RUN test=1; echo ${test:-abc}
And worse if you just have an unset non existing build arg buildah just removes from the log (uses empty string) which is also super confusing,
The issue states
I would love Buildah to expand the variables as well. I couldn't wrap my head around an issue where the build succeeded but didn't provide the build artifact I expected. Trying docker build helped me debug the issue and correct the Containerfile.
Given that I would argue this change doesn't really improve the situation, it trades one problem for another. But if docker behaves like that and logs incorrectly then maybe matching its bugs makes sense? I don't personally care much either way.
But I think the correct thing is to only replace the vars that buildah actually replaced when executing the command which ties back to my comment about replacing the RUN string in to many different places.
imagebuildah/executor.go
Outdated
| headingArgs := argsMapToSlice(stage.Builder.HeadingArgs) | ||
| userArgs := argsMapToSlice(stage.Builder.Args) | ||
| userArgs = append(builtinArgs, append(userArgs, headingArgs...)...) | ||
| argsCopy := slices.Clone(args) |
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.
why is this cloning the full args array if we throw away all values, that is just causing unnecessary memory copies.
To preallocate use make([]string, 0, len(args)).
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.
Done.
imagebuildah/executor.go
Outdated
| userArgs = append(builtinArgs, append(userArgs, headingArgs...)...) | ||
| argsCopy := slices.Clone(args) | ||
| for i := range args { | ||
| argsCopy[i], err = imagebuilder.ProcessWord(args[i].(string), userArgs) |
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.
casting to string seems unsafe, the function signature accepts any, This means all non strings values are discarded basically.
If the log can only deal with string the function signature should be updated to only accept strings.
In principle I am also quite confused in how many different places ProcessWord() gets called, buildah seems to call this and replace values as one off in may palaces. I think it would be much cleaner to replace and and then keep the replaced values but I don't understand enough about the buildah design here.
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.
Am I looking at the right function? ProcessWord() appears to take a string and a slice of strings, so type assertions shouldn't even be necessary.
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 talking about the log function itself which defines args as []any type. Everything that gets passed which is not a string is basically lost here due this type cast.
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.
Ah, I follow now. The assertion isn't checked, so I think that case would panic. In practice, we only pass it strings.
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 processing only args with string type, I avoided changing function signature as it involves making changes in public API of some structs so will do that in a new PR. Practically all args are strings so this check is just preventing from panic.
imagebuildah/executor.go
Outdated
| argsCopy[i], err = imagebuilder.ProcessWord(args[i].(string), userArgs) | ||
| if err != nil { | ||
| argsCopy[i] = args[i] | ||
| fmt.Fprintf(stageExecutor.executor.err, "while replacing arg variables with values for format in logging %q: %w\n", args[i], err) |
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.
you cannot use %w for normal formatting, only in fmt.Errorf()
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.
Done.
I am keeping behavior same as buildkit for consistency and created an upstream issue with moby/buildkit#6139 to track it there. |
Expand user,builtin and heading args before logging build steps. Closes: containers#6327 Signed-off-by: flouthoc <[email protected]>
Signed-off-by: flouthoc <[email protected]>
| if arg, ok := args[i].(string); ok { | ||
| if containsVariable(arg) { | ||
| processedArg, err := imagebuilder.ProcessWord(arg, userArgs) | ||
| if err != nil { | ||
| processedArg = arg | ||
| fmt.Fprintf(stageExecutor.executor.err, "while replacing arg variables with values for format in logging %q: %v\n", arg, err) | ||
| } | ||
| processedArgs = append(processedArgs, processedArg) | ||
| } else { | ||
| processedArgs = append(processedArgs, arg) | ||
| } | ||
| } |
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.
That still discards any arg which is not a string, the else block must be one lever out because it needs to keep the non string args otherwise the formatting string will be messed up badly.
| re := regexp.MustCompile(`\$\{?[a-zA-Z_][a-zA-Z0-9_]*\}?`) | ||
| return re.MatchString(arg) |
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.
That compiles the regex every single time which is quite inefficient. That should use something like regexp.Delayed() in the global scope.
But is the regex really right? At the very least it ignores escaping rules \$var is not a variable. ProcessWord() has already has a parser so this would need to match exactly what it is doing. I think it would be better to reuse the ProcessWord code and expose some function for this instead, yeah sure more complicated but the only sane way to avoid any differences between the var replacement code (ProcessWord) and this custom regex
|
A friendly reminder that this PR had no activity for 30 days. |
Expand user,builtin and heading args before logging build steps.
Closes: #6327
What type of PR is this?
What this PR does / why we need it:
How to verify it
Which issue(s) this PR fixes:
Special notes for your reviewer:
Does this PR introduce a user-facing change?