Skip to content
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

Preserve plugin config env var names with consecutive underscores #2116

Merged

Conversation

triarius
Copy link
Contributor

@triarius triarius commented May 25, 2023

When an env var name, that was derived from a plugin config, had consecutive underscores, the consecutive underscores were replaced by a single one. (For later reference, this will be called the underscore collapsing transformation). Now, both versions of the variable before and after this transformation are exported.

We also log a warning about this and log both the deprecated variable name and its replacement.
We need to keep the deprecated version around, as these environment variables are how plugins read their config without depending on a tool like jq to parse the config.
So removing them would mean that plugins could be inadvertently misconfigured, and they would have to be rewritten to start using the replacement environment variables instead.

Implementing this was not that straightforward, as the function that creates the environment slice from the plugin config is recursive and did not take a logger or return errors. It is recursive as that is the most straightforward way to turn a tree like object such as

parameter:
  ci:
    _api_key: /path
    db:
      host: localhost
      port: 5432

Into the list of environment variables, similar to:

PARAMETER_CI_API_KEY=/path
PARAMETER_CI_DB_HOST=localhost
PARAMETER_CI_DB_PORT=5432

I could have adapted it to do something when it hits a leaf node and the path to root had consecutive underscores, but I think that would complicate the recursion too much.

Instead, I just removed the underscore collapsing transformation and added a pass over the result that "fans-out" environment variable names with consecutive underscores into two environment variables with and without the underscore collapsing transformation applied.

So in the example above, there will now be an additional environment variable:

PARAMETER_CI__API_KEY=/path
PARAMETER_CI_API_KEY=/path
PARAMETER_CI_DB_HOST=localhost
PARAMETER_CI_DB_PORT=5432

Also, for passing the errors back to bootstrap, I made two custom error types. It's arguably a little overengineered, but I tried to find a way to use error wrapping and/or errors.Join to do the job, but they weren't powerful enough, or I was holding them wrong. There could be some generics libraries we could import to eliminate a lot of the code written for the error types. I would be glad to do this.

Fixes: #2100

Screenshots

Before

2023-05-26T15:14:46,282867146+10:00

After

2023-05-26T15:14:26,392941516+10:00

When an env var name, that was derived from a plugin config, had consecutive
underscores, the consecutive underscores were replaced by a single one.
Now, both versions of the variable before and after this transformation
are exported.
Previously we ignored errors from this method, but now we just log them.
Either way, execution continues, exposing us to the possibility of a nil
pointer dereference. Since go is not expressive enough to eliminate the
possibility of a nil pointer dereference, we have to be extra careful
to ensure we don't propagate nil pointer in this manner.
@triarius triarius force-pushed the pdp-1022-environment-variable-name-transformation-question branch from 22ff9e4 to f29a37c Compare May 25, 2023 22:38
@triarius triarius force-pushed the pdp-1022-environment-variable-name-transformation-question branch from d474bca to 1051cb3 Compare May 26, 2023 05:11
@triarius triarius marked this pull request as ready for review May 26, 2023 05:15
@triarius triarius requested review from a team, moskyb, DrJosh9000 and pda May 26, 2023 05:16
Comment on lines 19 to 23
nonIDCharacterRE = regexp.MustCompile(`[^a-zA-Z0-9]`)
consecutiveHyphenRE = regexp.MustCompile(`-+`)
dashOrSpace = regexp.MustCompile(`-|\s`)
whitespaceRE = regexp.MustCompile(`\s+`)
consecutiveUnderscoreRE = regexp.MustCompile(`_+`)
Copy link
Contributor

Choose a reason for hiding this comment

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

good renames 👍

noConsecutiveUnderScoreKey := consecutiveUnderscoreRE.ReplaceAllString(k, "_")
if k != noConsecutiveUnderScoreKey {
envSlice = append(envSlice, fmt.Sprintf("%s=%s", noConsecutiveUnderScoreKey, v))
err = errors.Join(err, &DeprecatedNameError{old: noConsecutiveUnderScoreKey, new: k})
Copy link
Contributor

Choose a reason for hiding this comment

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

:O TIL errors.Join made it into the stdlib

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In 1.20. But it wasn't powerful enough for me here :(

Copy link
Contributor Author

@triarius triarius May 28, 2023

Choose a reason for hiding this comment

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

For testing, I needed a way to make sure that the expected set of errors is exactly the set of the actual errors. But I could only see how to errors.Is on an error created with errors.Join in a way that allows you to conclude that the expected set of errors is a subset of the actual errors.

bootstrap/bootstrap.go Outdated Show resolved Hide resolved
`[ { "%s": %s } ]`,
"github.com/buildkite-plugins/docker-compose-buildkite-plugin",
configJSON,
)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just cosmetic.

cfgJSON1,
"github.com/buildkite-plugins/docker-compose-buildkite-plugin",
cfgJSON2,
)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just cosmetic.

@triarius triarius force-pushed the pdp-1022-environment-variable-name-transformation-question branch 2 times, most recently from 2d3527c to 089db2a Compare May 28, 2023 11:03
@triarius triarius force-pushed the pdp-1022-environment-variable-name-transformation-question branch 4 times, most recently from 60ff367 to ed9ae19 Compare May 28, 2023 15:04
The deprecation message isn't something that is actionable by people who
create builds (builders). It's only relevant if the plugin is depending on the
deprecated variable name, and the builder would not normally be aware of
this. Furthermore, it's often the case that the plugin will work fine
even though the environment variable name is "deprecated", so this does
not need to be a warning.
func (e *DeprecatedNameErrors) Error() string {
builder := strings.Builder{}
for i, err := range e.Errors() {
_, _ = builder.WriteString(err.Error())
Copy link
Contributor

Choose a reason for hiding this comment

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

Builder.WriteString (and WriteRune) are documented as returning nil error always, so the underscores seem extra unnecessary here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think it's too distracting to leave it this way. It makes the life of static analysis tools easier.

agent/plugin/plugin.go Outdated Show resolved Hide resolved
// Errors returns the contained set of errors in sorted order
func (e *DeprecatedNameErrors) Errors() []DeprecatedNameError {
if e == nil {
return []DeprecatedNameError{}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not return nil?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, and I made it handle when e.errs is nil too.

@triarius triarius force-pushed the pdp-1022-environment-variable-name-transformation-question branch from 4d36759 to 18ad336 Compare May 30, 2023 12:19
Copy link
Contributor

@moskyb moskyb left a comment

Choose a reason for hiding this comment

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

one tiny whitespace thing, but other than that this LGTM 🎉

bootstrap/bootstrap.go Outdated Show resolved Hide resolved
Co-authored-by: Ben Moskovitz <ben@mosk.nz>
@triarius triarius enabled auto-merge May 31, 2023 00:52
@triarius triarius disabled auto-merge May 31, 2023 00:53
Other languages have much better ways to do this
Copy link
Member

@pda pda left a comment

Choose a reason for hiding this comment

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

I haven't looked closely at the implementation, but I like what it does 👍🏼
And it looks like there's good test coverage.

"You may be able avoid this by removing consecutive underscore, hypen, or whitespace " +
"characters in your plugin configuration.",
)
b.shell.Logger.Printf("%s", strings.Join([]string{
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to understand, I'm guessing the problem with using a single string literal is how long it would be?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep. Wrapped lines are irritating for many reasons

@triarius
Copy link
Contributor Author

The log message has changed somewhat after all the review. Here's what it looks like now in a very similar context to what was in the original issue. I've expanded the relevant sections, they are not expanded by default.

2023-05-31T23:43:44,606079698+10:00

@triarius triarius merged commit d10a813 into main May 31, 2023
1 check passed
@triarius triarius deleted the pdp-1022-environment-variable-name-transformation-question branch May 31, 2023 23:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

environment variable name transformation question
4 participants