Skip to content

Conversation

@3bbbeau
Copy link

@3bbbeau 3bbbeau commented Mar 5, 2025

Summary

This is intended to be a quality of life feature to allow ArgoCD ApplicationSet resources to minimize boilerplate when dealing with NewRelic APM resources across multiple environments.

Currently

  • An application programmatically uses the NR SDK to report to NewRelic
  • NewRelic APM Application ID has to then be retrieved following the first run of an application from the NR Dashboard and added to an ArgoCD Application via the annotation, across multiple environments.
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  annotations:
    notifications.argoproj.io/subscribe.<trigger-name>.newrelic: 123456789

Desired

Using ArgoCD ApplicationSet resources, the annotation can be either provided via the application id as the above example, or instead by name, e.g. MyApp-{{ .values.env }}:

---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  annotations:
    notifications.argoproj.io/subscribe.<trigger-name>.newrelic: MyApp-Dev

Logic

If dest.Recipient can be parsed to an int, then the application id was directly provided and logic remains as before.

If dest.Recipient cannot be parsed to an integer, then it was passed by name (since all NewRelic APM IDs are defined as integers within the schema) and we call /v2/applications.json to query using filter[name]

If /v2/applications.json returns multiple application IDs then an error is returned, as we can't determine which application id to use to place the deployment marker.

@3bbbeau 3bbbeau force-pushed the feat/newrelic-appid-by-name branch 9 times, most recently from 063b8d4 to 2d77a47 Compare March 5, 2025 12:53
}

func (s newrelicService) getApplicationId(client *http.Client, appName string) (string, error) {
applicationsApi := fmt.Sprintf("%s/v2/applications.json?filter[name]=%s", s.opts.ApiURL, appName)
Copy link
Member

Choose a reason for hiding this comment

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

I'd use the url library to construct this, to avoid appName potentially injecting arbitrary stuff

Copy link
Author

@3bbbeau 3bbbeau Jan 26, 2026

Choose a reason for hiding this comment

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

}
}
}
markerApi := fmt.Sprintf(s.opts.ApiURL+"/v2/applications/%s/deployments.json", appId)
Copy link
Member

Choose a reason for hiding this comment

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

+1, use url package to construct URLs

Copy link
Author

@3bbbeau 3bbbeau Jan 26, 2026

Choose a reason for hiding this comment

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

Comment on lines 185 to 187
var appId = dest.Recipient
if dest.Recipient != "" {
_, err := strconv.Atoi(dest.Recipient)
Copy link
Member

Choose a reason for hiding this comment

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

Instead of reusing Recipient, could you use a new RecipientName field? Clarifies the UX and avoids any weirdness around, for example, a name that happens to also be a number.

Copy link
Author

Choose a reason for hiding this comment

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

Thank you for taking a look at this. I first interpreted your comment as changing the Destination struct but I'm not actually sure where you mean without making this change of wider significance.

I had a look at the other services to see how they had handled service-specific changes in recipient fields:

// It's a channel
if strings.HasPrefix(dest.Recipient, "#") || strings.HasPrefix(dest.Recipient, "@") {
message.Channel = dest.Recipient
} else {
message.RoomID = dest.Recipient

if strings.HasPrefix(dest.Recipient, "-") {
chatID, err := strconv.ParseInt(dest.Recipient, 10, 64)
if err != nil {
return err
}
// Init message with ParseMode is 'Markdown'
msg := tgbotapi.NewMessage(chatID, notification.Message)
msg.ParseMode = "Markdown"
_, err = bot.Send(msg)
if err != nil {
return err
}
} else {
// Init message with ParseMode is 'Markdown'
msg := tgbotapi.NewMessageToChannel("@"+dest.Recipient, notification.Message)
msg.ParseMode = "Markdown"


Could we do something similar with name: 12345678 and id: 12345678 and default to the latter given no prefix for backwards compatibility? That would leave the Destination struct alone and address the issue of New Relic APM names that happen to be numbers.

@pasha-codefresh
Copy link
Member

Hey @3bbbeau could you please address comments?

@3bbbeau 3bbbeau force-pushed the feat/newrelic-appid-by-name branch from 0613604 to cb85658 Compare January 26, 2026 13:17
@codecov
Copy link

codecov bot commented Jan 26, 2026

Codecov Report

❌ Patch coverage is 65.90909% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.12%. Comparing base (da04400) to head (1582b2f).
⚠️ Report is 23 commits behind head on master.

Files with missing lines Patch % Lines
pkg/services/newrelic.go 65.90% 9 Missing and 6 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #361      +/-   ##
==========================================
+ Coverage   55.41%   60.12%   +4.71%     
==========================================
  Files          46       48       +2     
  Lines        4125     3742     -383     
==========================================
- Hits         2286     2250      -36     
+ Misses       1511     1131     -380     
- Partials      328      361      +33     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This is intended to be a quality of life addition to allow
`ApplicationSet` resources to minimize boilerplate when dealing with
NewRelic APM resources across multiple environments.
- An application programmatically [uses the NR SDK to report to
  NewRelic](https://github.com/newrelic/go-agent/blob/master/v3/examples/server/main.go#L267-L274)
- NewRelic APM Application ID has to then be retrieved following the
  first run of an application from the NR Dashboard and added to an
  ArgoCD `Application` via the annotation, across multiple environments.
Using ArgoCD `ApplicationSet` resources, the annotation can be either
provided via app_id, or by name, e.g. `MyApp-{{ .values.env }}`
If `dest.Recipient` can be parsed to an int, then app_id is provided
and logic remains as before.
If `dest.Recipient` cannot be parsed to an integer, then it was passed
by name and we call `/v2/applications.json` to query using
`filter[name]`
If `/v2/applications.json` returns multiple application IDs then an
error is returned, as we can't determine which app_id to use to place
the deployment marker.

Signed-off-by: 3bbbeau <[email protected]>
Guard against potential injection from appName

Signed-off-by: 3bbbeau <[email protected]>
@3bbbeau 3bbbeau force-pushed the feat/newrelic-appid-by-name branch from cb85658 to eedfe1e Compare January 26, 2026 13:40
- error wrapping %w
- http.NoBody over nil
- unused parameter 'r'
- assert errors with require

Signed-off-by: 3bbbeau <[email protected]>
@3bbbeau 3bbbeau force-pushed the feat/newrelic-appid-by-name branch from eedfe1e to 1582b2f Compare January 26, 2026 13:40
@3bbbeau
Copy link
Author

3bbbeau commented Jan 26, 2026

@pasha-codefresh Hey, I've addressed all but #361 (comment) which I'm not sure I understand broader implementation details

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.

3 participants