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

feat: support notification template #1775

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

chenlujjj
Copy link
Contributor

@chenlujjj chenlujjj commented Nov 26, 2024

related issue: #1736

@theSuess
Copy link
Member

Thanks for the contribution! Can you use the common fields introduced in #1765? I'd also like to wait for #1770 to be merged just so we won't need to do this work again for notification templates as well

@weisdd
Copy link
Collaborator

weisdd commented Nov 27, 2024

I'm also in favour of having this PR rewritten on top of changes introduced in #1765 and #1770.

@chenlujjj
Copy link
Contributor Author

Sure, I'll do that.

@chenlujjj chenlujjj force-pushed the feat/notificationtemplate branch 2 times, most recently from f604c8d to 0956145 Compare November 28, 2024 16:15
@CarlosLanderas
Copy link

Wow!!! This is awesome @chenlujjj!

We really need this and I was actually thinking about opening an issue to ask if I can contribute it myself.

Awesome thanks!

@CarlosLanderas
Copy link

Any ETA for this nice feature to be released?

Many thanks!

@chenlujjj
Copy link
Contributor Author

Hi @CarlosLanderas ,I'm waiting for #1770 to be merged, so that I can rewrite this PR on top of it.

@CarlosLanderas
Copy link

Perfect @chenlujjj , thanks!

@theSuess
Copy link
Member

@chenlujjj we just merged #1770 so if you have the time to rebase ontop of that, that'd be great!

@chenlujjj chenlujjj force-pushed the feat/notificationtemplate branch from 0956145 to d81aeae Compare December 16, 2024 10:38
@chenlujjj chenlujjj requested a review from hubeadmin as a code owner December 16, 2024 10:38
@chenlujjj chenlujjj force-pushed the feat/notificationtemplate branch from d81aeae to af84b6b Compare December 16, 2024 11:39
Copy link
Contributor

@Baarsgaard Baarsgaard left a comment

Choose a reason for hiding this comment

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

Hi @chenlujjj looks good overall.
I spotted a few places in the Reconcile function where you're doing more than necessary.
Some of the code can be deleted or replaced with a couple of helper functions.

The other comments are mostly minor changes!

I wrote these diffs in the Github UI, there is likely an error in one or more of them

controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
api/v1beta1/grafananotificationtemplate_types.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
Copy link
Contributor

@Baarsgaard Baarsgaard left a comment

Choose a reason for hiding this comment

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

@theSuess, @weisdd,
What do you think about this change where we can essentially remove the ResyncPeriodHasElapsed check?

controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
@chenlujjj chenlujjj force-pushed the feat/notificationtemplate branch 4 times, most recently from c22bb96 to 87b78e6 Compare December 17, 2024 06:40
@chenlujjj
Copy link
Contributor Author

Thank you @Baarsgaard , I've updated the code based on your review suggestions and implementation of grafana folder controller.

Copy link
Contributor

@Baarsgaard Baarsgaard left a comment

Choose a reason for hiding this comment

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

Significantly less comments this time 😄 good work!

controllers/__debug_bin1929994336 Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
@chenlujjj chenlujjj force-pushed the feat/notificationtemplate branch 3 times, most recently from 4d31b7c to e42ad78 Compare December 23, 2024 08:13
@chenlujjj
Copy link
Contributor Author

chenlujjj commented Dec 23, 2024

@Baarsgaard Thanks for your patient review. I resolved all the comments.

I've got one question: in the following code

	if err != nil || len(instances) == 0 {
		setNoMatchingInstancesCondition(&notificationTemplate.Status.Conditions, notificationTemplate.Generation, err)
		meta.RemoveStatusCondition(&notificationTemplate.Status.Conditions, conditionNotificationTemplateSynchronized)
		notificationTemplate.Status.NoMatchingInstances = true
		controllerLog.Error(err, "could not find matching instances", "name", notificationTemplate.Name, "namespace", notificationTemplate.Namespace)
		return ctrl.Result{RequeueAfter: RequeueDelay}, err
	}

does it matter to return ctrl.Result{RequeueAfter: RequeueDelay}, err or return ctrl.Result{RequeueAfter: RequeueDelay}, nil ? I saw different implementations in different resource controllers.

@chenlujjj
Copy link
Contributor Author

@Baarsgaard Could you please take another review, thank you.

Copy link
Contributor

@Baarsgaard Baarsgaard left a comment

Choose a reason for hiding this comment

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

Hi @chenlujjj sorry for the delay, here's another couple of comments 😅
Additionally, I'm very sorry for the PRs that I open that end up blocking yours 🙇‍♂️

api/v1beta1/grafananotificationtemplate_types.go Outdated Show resolved Hide resolved
api/v1beta1/grafananotificationtemplate_types.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
api/v1beta1/grafananotificationtemplate_types.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
controllers/grafananotificationtemplate_controller.go Outdated Show resolved Hide resolved
@chenlujjj chenlujjj force-pushed the feat/notificationtemplate branch 2 times, most recently from be3b5ac to 6b001c4 Compare December 29, 2024 11:23
@Baarsgaard
Copy link
Contributor

@chenlujjj Both #1801 and #1804 were merged, you can now continue, and I will not be opening any more PRs that interfere with this!

@chenlujjj chenlujjj force-pushed the feat/notificationtemplate branch 2 times, most recently from 415e156 to 776792e Compare December 30, 2024 16:13
@chenlujjj chenlujjj force-pushed the feat/notificationtemplate branch from 776792e to d2b665a Compare December 30, 2024 16:15
@chenlujjj
Copy link
Contributor Author

Thank you @Baarsgaard .
Hi @theSuess @weisdd Can anyone of you have a review ?

Copy link
Collaborator

@weisdd weisdd left a comment

Choose a reason for hiding this comment

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

Sorry for delays in reviews, during the holiday season many of maintainers are absent.

Overall, the code looks good to me, though we still need to make a few adjustments. Aside from the comments I've added, we also need to:

  • add an e2e-test for the resource;
  • add an example (check examples folder).

if err != nil {
return ctrl.Result{}, fmt.Errorf("could not find matching instances: %w", err)
} else {
return ctrl.Result{}, fmt.Errorf("could not find matching instances")
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should not return an error here. - Having zero matching instances is a valid case, e.g. due to timing (the notification template gets reconciled earlier than Grafana), Grafana CR will be added later, etc. - In such scenario, we only need to update condition, but should not return errors in my opinion.

Copy link
Contributor

@Baarsgaard Baarsgaard Jan 1, 2025

Choose a reason for hiding this comment

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

I took a deeper look at GetScopedMatchingInstances and the error is only defined when the client returns an error on listing grafana instances.
What do you think of:

if err != nil {
	return ctrl.Result{}, fmt.Errorf("could not find matching instances: %w", err)
} else {
	r.Log.Info("could not find matching instances")
	return ctrl.Result{Requeue: false}, nil
}

This way the log is still clear on what is happening and actual errors are logged.
Alternatively, we can change the function signature of GetScopedMatchingInstances to never return an error and only return empty arrays?
Then handle all the logging of errors within GetScopedMatchingInstances?

instances := GetScopedMatchingInstances(controllerLog, ctx, r.Client, notificationTemplate)
if len(instances) == 0 {
	setNoMatchingInstancesCondition(&notificationTemplate.Status.Conditions, notificationTemplate.Generation, err)
	meta.RemoveStatusCondition(&notificationTemplate.Status.Conditions, conditionNotificationTemplateSynchronized)
	r.Log.Info("could not find matching instances")
	return ctrl.Result{Requeue: false}, nil
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

In my view:

  • We should not silence errors. If the client hits an error, the operator should return the error and make another reconciliation attempt;
  • We should not handle error- and zero length cases in one if-block, - even in an abstract case, I would put it as a code-style issue;
  • Usage of if err != nil {} ... else is not encouraged in Go either. - Normally, you would have something like if err != nil { return err } and then, on a separate line, the next independent block.

I think notification policy controller offers a more or less optimal example:

	instances, err := GetMatchingInstances(ctx, r.Client, notificationPolicy.Spec.InstanceSelector)
	if err != nil {
		setNoMatchingInstance(&notificationPolicy.Status.Conditions, notificationPolicy.Generation, "ErrFetchingInstances", fmt.Sprintf("error occurred during fetching of instances: %s", err.Error()))
		meta.RemoveStatusCondition(&notificationPolicy.Status.Conditions, conditionNotificationPolicySynchronized)
		r.Log.Error(err, "could not find matching instances")
		return ctrl.Result{RequeueAfter: RequeueDelay}, err
	}

	if len(instances.Items) == 0 {
		meta.RemoveStatusCondition(&notificationPolicy.Status.Conditions, conditionNotificationPolicySynchronized)
		setNoMatchingInstance(&notificationPolicy.Status.Conditions, notificationPolicy.Generation, "EmptyAPIReply", "Instances could not be fetched, reconciliation will be retried")
		return ctrl.Result{}, nil
	}

Though, the last log line can be improved (we can do that later, in batch).

Copy link
Contributor

@Baarsgaard Baarsgaard Jan 1, 2025

Choose a reason for hiding this comment

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

The idiomatic go code would then, in this case, be?:

instances, err := GetScopedMatchingInstances(controllerLog, ctx, r.Client, notificationTemplate)
if err != nil {
	setNoMatchingInstancesCondition(&notificationTemplate.Status.Conditions, notificationTemplate.Generation, err)
	meta.RemoveStatusCondition(&notificationTemplate.Status.Conditions, conditionNotificationTemplateSynchronized)
	return ctrl.Result{}, fmt.Errorf("could not find matching instances: %w", err)
}
if len(instances) == 0 {
	setNoMatchingInstancesCondition(&notificationTemplate.Status.Conditions, notificationTemplate.Generation, err)
	meta.RemoveStatusCondition(&notificationTemplate.Status.Conditions, conditionNotificationTemplateSynchronized)
	return ctrl.Result{RequeueAfter: RequeueDelay}, nil
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah. As you can see, it's much cleaner and easier to follow :) I would only add a new line before if len(instances) == 0 for better readability.

Comment on lines +195 to +198
_, err = r.getNotificationTemplateByName(ctx, instance, notificationTemplate)
if err != nil {
return fmt.Errorf("getting notification template by name: %w", err)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
_, err = r.getNotificationTemplateByName(ctx, instance, notificationTemplate)
if err != nil {
return fmt.Errorf("getting notification template by name: %w", err)
}

If a template is absent, it should not be considered an error, otherwise we'll have a never-ending reconciliation. Grafana API returns 204 (No Content) when someone attempts to remove non-existing template, it's treated as success by the Grafana API client we're using.

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.

5 participants