Skip to content

Conversation

@yhabteab
Copy link
Member

@yhabteab yhabteab commented Nov 4, 2025

These commits are just left overs from my previous PRs but can independently be merged, since they don't depend on them and enhance some of the existing code flows.

@cla-bot cla-bot bot added the cla/signed CLA is signed by all contributors of a PR label Nov 4, 2025
@yhabteab yhabteab added this to the 0.2.0 milestone Nov 4, 2025
@yhabteab yhabteab requested a review from julianbrost November 4, 2025 17:05
@yhabteab yhabteab added the enhancement New feature or request label Nov 4, 2025
@oxzi oxzi force-pushed the icingadb-source branch 4 times, most recently from e2a1836 to f3870a3 Compare November 6, 2025 15:43
Base automatically changed from icingadb-source to main November 7, 2025 10:34
@yhabteab yhabteab force-pushed the minor-enhancements branch 2 times, most recently from 9a81611 to 5d0d19c Compare November 7, 2025 15:45
Copy link
Collaborator

@julianbrost julianbrost left a comment

Choose a reason for hiding this comment

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

General question: You chose to embed *Resources into other structs instead of having it as a regular member. I believe you did that so you can just write x.DB instead of something like x.res.DB. However, doing it that way has two other drawbacks in my opinion:

  • This makes code navigation a bit harder. If it's a regular member, I can find all uses easily, whereas now, answering a question like "which members of Resources are actually needed/used by RuntimeConfig?" (there's also an individual comment on that) isn't that easy.
  • It implicitly makes them part of the public interface, i.e. x.DB is exported, whereas x.res.DB isn't.

Comment on lines +54 to +56
func (c *Channel) Start(ctx context.Context, conf *daemon.ConfigFile, logger *zap.SugaredLogger) {
c.Logger = logger.With(zap.Object("channel", c))
c.daemonConfig = conf
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is this passed individually instead of as part of *Resources (as I'd kind of expect from the overall vibe of this PR)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because Resources is within the config package and imports this channel package.

// Accessing it requires a lock that is obtained with RLock() and released with RUnlock().
ConfigSet

*Resources // Contains references to commonly used objects and to itself.
Copy link
Collaborator

Choose a reason for hiding this comment

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

To itself? That sounds a bit weird. I mean technically it does given that Resources contains a *RuntimeConfig, but should this ever be accessed from here?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, why would you ever want to access it that way. But fine, I'll adjust the comment.

@yhabteab
Copy link
Member Author

  • This makes code navigation a bit harder. If it's a regular member, I can find all uses easily, whereas now, answering a question like "which members of Resources are actually needed/used by RuntimeConfig?" (there's also an individual comment on that) isn't that easy.

That's how Go works and isn't a drawback for me. If you for whatever reason end up asking that question to yourself, then you can list all usages of the fields of Resources with your IDE or grep, but that's not a reason to avoid embedding it IMHO.

It implicitly makes them part of the public interface, i.e. x.DB is exported, whereas x.res.DB isn't.

That's true, but I don't see a problem with that. This new struct contains nothing that's specific to some other type, it's just a bag of global resources. So exposing them via embedding is fine for me.

It's getting ridiculous that those fields have to be passed around
everywhere into functions and methods. This becomes even more ridiculous
with upcoming PR of mine, so I just bite the bullet and make a struct
for it. This struct is typically embedded into other structs that need
access to these common resources, and each function or method has just
to take the `RuntimeConfig` as a parameter, and this will be populated
from there.
@yhabteab
Copy link
Member Author

@cla-bot check

@julianbrost
Copy link
Collaborator

I've looked at this for a while now and I'm not yet convinced that this is really the way to go.

Sure, it allows you to pass a single argument instead of two or three in some places, but it also looks like it introduces quite some trouble with cyclic imports. At least it looks to my like the explanation to why the new Resources struct is defined inside the ./internal/config is that otherwise you'd get cyclic imports with the RuntimeConfig struct both being part of Resources as well as having a reference to Resources itself (just having the fields of Resources inside RuntimeConfig and passing that around everywhere would give a similar result in code structure). To me, that Resources type should be more of a global thing, not something that's tied to an (arbitrary) member of it. And then there's already a place where the Resources type can't be used as it would cause cyclic imports.

Regarding the commit message of 9fb2aa3:

It's getting ridiculous that those fields have to be passed around everywhere into functions and methods. This becomes even more ridiculous with upcoming PR of mine, so I just bite the bullet and make a struct for it.

As I haven't looked at that (now closed) PR in detail, I don't know what exactly changed there and to what extent it would have proved the necessity of this change, but on it's own, I'm not sure.

On the other hand, if passing dependencies is getting ridiculous, that might also be an opportunity to think about if the dependency itself is ridiculous and could be avoided.

In the changes of this PR, I already noticed a small example that shows this: Why does Channel even need to know the full configuration? It uses it to construct the incident URL, but that could also just be a provided by a method on the Incident type, avoiding the need to pass the Icinga Web URL there (the channel just needs a URL, it doesn't need to care about how it's constructed):

incidentUrl := c.daemonConfig.IcingaWeb2UrlParsed.JoinPath("/notifications/incident")
incidentUrl.RawQuery = fmt.Sprintf("id=%d", i.ID())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla/signed CLA is signed by all contributors of a PR enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants