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

Separate and export BuildReceiverIntegrations #3553

Conversation

alexweav
Copy link
Contributor

@alexweav alexweav commented Oct 9, 2023

This allows BuildReceiverIntegrations to be re-used from multiple places.

Initially, this change is done to support: #3491
in which integrations must be built for a purpose other than the usual notification pipeline, and not from the alertmanager config.

That PR simply duplicated the code, but this led to inconsistencies where the notification pipeline and receiver test code would drift. This change instead allows the same logic to be used in multiple places, allowing the above PR to eliminate redundant code in Alertmanager.

This also means downstream consumers of notifiers can re-use this builder code - they currently tend to duplicate it, leading to inconsistency. Added a couple extension points for this:

  • Consumers can now pass in prometheus/common/config HTTP client options, they will be applied to all relevant notifiers. These notifier arguments are currently largely unused in the prometheus codebase.
  • Added a wrapper func allowing consumers to modify notifiers as needed.

@alexweav alexweav force-pushed the alexweav/extract-reuse-buildReceiverIntegrations branch from 5774ce0 to 6695f7d Compare October 9, 2023 22:49
Signed-off-by: Alex Weaver <[email protected]>

// BuildReceiverIntegrations builds a list of integration notifiers off of a
// receiver config.
func BuildReceiverIntegrations(nc config.Receiver, tmpl *template.Template, logger log.Logger, wrap Wrapper, httpOpts ...commoncfg.HTTPClientOption) ([]notify.Integration, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Largely unchanged, except for the wrap and httpOpts parameters and their handling.


func (s sendResolved) SendResolved() bool { return bool(s) }

func TestBuildReceiverIntegrations(t *testing.T) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copied verbatim from main_test.go

})
}

t.Run("invokes notifier wrapper func", func(t *testing.T) {
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 the only new test code.

Comment on lines 56 to 58
if wrap != nil {
n = wrap(name, n)
}
Copy link
Contributor Author

@alexweav alexweav Oct 9, 2023

Choose a reason for hiding this comment

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

I'd like to drop this abstraction - its only real use, is that the wrapper code can correlate Name->Notifier and do special things based on the notifier type. The notifiers cannot otherwise be disambiguated based on what is returned by this function.

A couple alternative approaches:

  • Add a function func Type() string to the notify.Notifier interface, that lets client code see what type a notifier is.
  • Return a map of type->Notifier rather than an opaque slice of notifiers from this function.

The current approach is what grafana/mimir does: https://github.com/grafana/mimir/blob/7746f752c23fd5499fca59268d694a3345cfb29e/pkg/alertmanager/alertmanager.go#L466

Do any others have thoughts here?

Copy link
Member

Choose a reason for hiding this comment

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

I like the first approach of modifying the Notifier interface - I think it's quite useful at the expense of a breaking change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Experimented with this idea a bit -

this is a deep change, for a PR that's trying to refactor something else. There's no common embedded object among the receivers, so we either need to introduce one, or add the method to each receiver individually. It's not complicated, but it touches a lot of code.

I also think the map approach might have merit and would like to experiment a bit.

I'm happy to do this, but perhaps as a separate PR that's not just a simple function export? I'll drop the wrap() bit from here, and we can figure out the best extension point as part of its own change.

Copy link
Member

Choose a reason for hiding this comment

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

Agreed - I like this approach.

// See the License for the specific language governing permissions and
// limitations under the License.

package receiver
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 had to be moved to a new package due to package import cycles.

Happy to choose any other location for this.

Copy link
Contributor Author

@alexweav alexweav Oct 11, 2023

Choose a reason for hiding this comment

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

Fun fact - this function actually did used to be exported, up until the notify package was split up.

There's a relevant comment thread here: #1929 (comment)

Copy link
Member

Choose a reason for hiding this comment

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

I like how every package inside notify (with a very obvious exception in test) is an integration. Can this not be part of the notify package or a completely separate package?

Copy link
Contributor Author

@alexweav alexweav Oct 16, 2023

Choose a reason for hiding this comment

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

Can't be part of notify due to package dependencies. All the integrations depend on notify, and then this code depends on all the integrations, so we'd have an import cycle.

I moved it to a subpackage of config instead (config/receiver), since it's logically glue code between config and notify. WDYT?

I can't make it part ofconfig directly for the same reason as notify - import cycles. It needs to be a subpackage somewhere.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I'm happy to keep this in config.

Copy link
Member

@gotjosh gotjosh left a comment

Choose a reason for hiding this comment

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

Looks good so far, but please see my comments.

Copy link
Member

@gotjosh gotjosh left a comment

Choose a reason for hiding this comment

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

LGTM

@gotjosh gotjosh merged commit 412f062 into prometheus:main Oct 17, 2023
7 checks passed
alexweav added a commit to grafana/prometheus-alertmanager that referenced this pull request Oct 27, 2023
* Move and export BuildReceiverIntegrations

Signed-off-by: Alex Weaver <[email protected]>

---------

Signed-off-by: Alex Weaver <[email protected]>
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.

2 participants