-
Notifications
You must be signed in to change notification settings - Fork 0
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
Load ConfigFile from Environment Variables #148
base: main
Are you sure you want to change the base?
Conversation
This PR's motivation is to address this comment #144 (comment) which might be a precondition for the integration tests. |
If it already uses reflection on the config struct, why did you choose to generate a YAML file which is then later parsed into that struct rather than setting the values using reflection? Also, it would be great if the code would be reusable so that Icinga DB could be adapted to use this as well in the future. If I'm not missing something, this would only require making the environment variable prefix and destination type function parameters. |
Because I wanted to achieve the exact same behavior as by passing a YAML configuration. This starts with detecting the right native type for each field and trying to convert the environment variable's value - which is at first always a string - to it and ends at custom parsing for everything from different With this architecture, a small amount of code translates environment variables to the already known configuration format.
Yes, the current code only requires that Footnotes |
The basic problem here is taking a Your code already contains some special handling for integers (probably inspired by the Icinga DB container entrypoint): icinga-notifications/internal/daemon/config.go Lines 123 to 128 in 83ce59a
Which is basically guessing based on the value, if the destination type would be taken into account, the code could make a better informed decision about which conversion would be needed. Icinga DB already happens to need and have a Note: I couldn't test if the current code would handle database:
",inline":
tls: "true" |
Exactly. Unfortunately, even some (more or less) scalar types from the standard library do not implement the At this point, I would like to link again to go-yaml's additional type check: Furthermore, there are all possible compound data types (structs, arrays and slices, maps) and interfaces.
This is, honestly, just
As I stated earlier, this would require more testing to ensure that this code behaves like go-yaml's code. Furthermore, when there are changes to either the future icinga-go-library or go-yaml code, things might break. IMO, when using a (partial) YAML configuration as an intermediate representation, the logical coupling is smaller as when try to mimic go-yaml's behavior, especially where they made decisions in the absence of standards.
The field names were derived from the Go fields as, by naming convention, Go does not use underscores as separators but camel case. I was expecting to find underscores in the I'll try to generalize the code as mentioned in #148 (comment), make some tests for other types, e.g., booleans, derive the names from the |
Next to populating ConfigFile through default values and by a YAML configuration, environment variable support was added. The code works by reflecting on the existing nested ConfigFile struct. The environment variable key is an underscore separated string of uppercase struct fields.
After multiple iterations, both the code as well as the logic has now changed quite a bit. I reflected on my prior statement that creating a temporary YAML configuration would be a good idea and now used the available runtime type information to decode each value into a given reference to the struct directly. Furthermore, I added rudimentary support for Please feel free to take another look, @julianbrost. |
Have you considered existing libraries such as https://github.com/caarlos0/env? |
Yes. IIRC, I have even tried this library or at least some other Go library for this purpose. The problem is, however, it requires a fixed Furthermore, I aimed to get both the same names as well as the same decoding behavior as with the already used YAML library. As outlined in earlier comments, there is already some highly opinionated behavior and having to mimic another library's decisions - also with future or forward compatibility - just sounds tedious. |
That shouldn't be a general issue. This code would probably be destined for icinga-go-library where those database config struct will also end up. (It would probably be a problem if the library wouldn't allow a custom variable prefix per project.)
Is that a general issue or is it just saying that because of that, it's not possible to build a workaround that wouldn't require adding tags to the structs?
In general, please elaborate more on which libraries you looked into and what limitations made you rule them out in particular. We might find that some can easily be worked around, otherwise we have a good picture of why implementing it on our own is the better choice. |
I have tried two different libraries. My first attempt was using https://github.com/sethvargo/go-envconfig. Unfortunately, my testing code is gone as I have not committed it. However, with @lippserd's suggestion I achieved more, as outlined below. (But GitHub just ate my half-written comparison of what worked and what did not.) Then, I tried https://github.com/caarlos0/env.
In a nutshell, with some tweaks it would be possible to use the https://github.com/caarlos0/env library. However, in the meantime since creating this PR, the premise has changed. Thus, would it be possible to (re-)define the concrete scope of this PR, which was initially created to address a comment of @julianbrost, allowing to configure Icinga Notifications through environment variables for integration tests. We have moved a considerable distance away from the initial motivation, inflating the dependencies before getting anywhere near the tests. Now, to something completely different: password and other credentials in environment variables. I am not quite sure if this is desirable from a security perspective. First, at least the same, or a more privileged, user can access the environment through One can argue that currently the subprocesses are having the same privileges as the daemon anyway and we have control over logs. But, however, this seems like something which might get forgotten along the path and that traditional file permissions are an easier concept. |
Not if we unset such first. https://github.com/caarlos0/env?tab=readme-ov-file#unset-environment-variable-after-reading-it |
Next to populating ConfigFile through default values and by a YAML configuration, environment variable support was added.
The code works by reflecting on the existing nested ConfigFile struct. The environment variable key is an underscore separated string of uppercase struct fields.