-
Notifications
You must be signed in to change notification settings - Fork 1
feat: grafana component #183
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import * as pulumi from '@pulumi/pulumi'; | ||
| import { Grafana } from './grafana'; | ||
|
|
||
| export class GrafanaBuilder { | ||
| private name: string; | ||
| private prometheusConfig?: Grafana.PrometheusConfig; | ||
| private tags?: Grafana.Args['tags']; | ||
|
|
||
| constructor(name: string) { | ||
| this.name = name; | ||
| } | ||
|
|
||
| public withPrometheus(config: Grafana.PrometheusConfig): this { | ||
| this.prometheusConfig = config; | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| public withTags(tags: Grafana.Args['tags']): this { | ||
| this.tags = tags; | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| public build(opts: pulumi.ComponentResourceOptions = {}): Grafana { | ||
| return new Grafana( | ||
| this.name, | ||
| { | ||
| prometheus: this.prometheusConfig, | ||
| tags: this.tags, | ||
| }, | ||
| opts, | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| import * as aws from '@pulumi/aws'; | ||
| import * as pulumi from '@pulumi/pulumi'; | ||
| import * as grafana from '@pulumiverse/grafana'; | ||
|
|
||
| // Fixed AWS account ID owned by Grafana Cloud, used to assume roles in customer accounts. | ||
| const GRAFANA_CLOUD_AWS_ACCOUNT_ID = '008923505280'; | ||
|
|
||
| export namespace Grafana { | ||
| export type PrometheusConfig = { | ||
| prometheusEndpoint: pulumi.Input<string>; | ||
| region: pulumi.Input<string>; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should region be optional and fallback to the current AWS region used by the stack? |
||
| }; | ||
|
|
||
| export type Args = { | ||
| prometheus?: PrometheusConfig; | ||
| tags?: pulumi.Input<{ | ||
| [key: string]: pulumi.Input<string>; | ||
| }>; | ||
| }; | ||
| } | ||
|
|
||
| export class Grafana extends pulumi.ComponentResource { | ||
| prometheusDataSource?: grafana.oss.DataSource; | ||
|
|
||
| constructor( | ||
| name: string, | ||
| args: Grafana.Args, | ||
| opts: pulumi.ComponentResourceOptions = {}, | ||
| ) { | ||
| super('studion:grafana:Grafana', name, {}, opts); | ||
|
|
||
| if (args.prometheus) { | ||
| const ampRole = this.createAmpRole(name, args.tags); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Set
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the future we would have more data sources than just prometheus so I would recommend:
|
||
| this.createPrometheusDataSource(name, args.prometheus, ampRole); | ||
| } | ||
|
|
||
| this.registerOutputs(); | ||
| } | ||
|
|
||
| private getStackSlug(): string { | ||
| const grafanaUrl = process.env.GRAFANA_URL; | ||
|
|
||
| if (!grafanaUrl) { | ||
| throw new Error('GRAFANA_URL environment variable is not set.'); | ||
| } | ||
|
Comment on lines
+43
to
+45
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stack config should be the first option, env is the fallback, e.g. NOTE: This should apply to other props mentioned in PR description since they are provider options. |
||
|
|
||
| return new URL(grafanaUrl).hostname.split('.')[0]; | ||
| } | ||
|
|
||
| private createAmpRole(name: string, tags: Grafana.Args['tags']) { | ||
| const stackSlug = this.getStackSlug(); | ||
| const grafanaStack = grafana.cloud.getStack({ slug: stackSlug }); | ||
|
|
||
| const ampRole = new aws.iam.Role( | ||
| `${name}-amp-role`, | ||
| { | ||
| assumeRolePolicy: pulumi.jsonStringify({ | ||
| Version: '2012-10-17', | ||
| Statement: [ | ||
| { | ||
| Effect: 'Allow', | ||
| Principal: { | ||
| AWS: `arn:aws:iam::${GRAFANA_CLOUD_AWS_ACCOUNT_ID}:root`, | ||
| }, | ||
| Action: 'sts:AssumeRole', | ||
| Condition: { | ||
| StringEquals: { | ||
| 'sts:ExternalId': pulumi.output(grafanaStack).id, | ||
| }, | ||
| }, | ||
| }, | ||
| ], | ||
| }), | ||
| tags, | ||
| }, | ||
| { parent: this }, | ||
| ); | ||
|
|
||
| new aws.iam.RolePolicy( | ||
| `${name}-amp-policy`, | ||
| { | ||
| role: ampRole.id, | ||
| policy: JSON.stringify({ | ||
| Version: '2012-10-17', | ||
| Statement: [ | ||
| { | ||
| Effect: 'Allow', | ||
| Action: [ | ||
| 'aps:GetSeries', | ||
| 'aps:GetLabels', | ||
| 'aps:GetMetricMetadata', | ||
| 'aps:QueryMetrics', | ||
| ], | ||
| Resource: '*', | ||
| }, | ||
| ], | ||
| }), | ||
| }, | ||
| { parent: this }, | ||
| ); | ||
|
|
||
| return ampRole; | ||
| } | ||
|
|
||
| private createPrometheusDataSource( | ||
| name: string, | ||
| config: Grafana.PrometheusConfig, | ||
| ampRole: aws.iam.Role, | ||
| ) { | ||
| const stackSlug = this.getStackSlug(); | ||
|
|
||
| const plugin = new grafana.cloud.PluginInstallation( | ||
| `${name}-prometheus-plugin`, | ||
| { | ||
| stackSlug, | ||
| slug: 'grafana-amazonprometheus-datasource', | ||
| version: 'latest', | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we make this configurable and use the |
||
| }, | ||
| { parent: this }, | ||
| ); | ||
|
|
||
| this.prometheusDataSource = new grafana.oss.DataSource( | ||
| `${name}-prometheus-datasource`, | ||
| { | ||
| type: 'grafana-amazonprometheus-datasource', | ||
| url: config.prometheusEndpoint, | ||
| jsonDataEncoded: pulumi.jsonStringify({ | ||
| sigV4Auth: true, | ||
| sigV4AuthType: 'grafana_assume_role', | ||
| sigV4Region: config.region, | ||
| sigV4AssumeRoleArn: ampRole.arn, | ||
| }), | ||
| }, | ||
| { dependsOn: [plugin], parent: this }, | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,3 @@ | ||
| export * as dashboard from './dashboards'; | ||
| export { Grafana } from './grafana'; | ||
| export { GrafanaBuilder } from './builder'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see this is exposed in GH, but the docs do not mention there is just one fixed account ID.
Remove the constant and make it a config option.