Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c9e83f6
feat: create grafana component
bornast Mar 16, 2026
1df2135
docs: add grafana aws account hardcoded value comment
bornast Mar 16, 2026
ff2d228
refactor: provider config value extraction
bornast Mar 17, 2026
02cb734
feat: remove tags prop from grafana builder and component
bornast Mar 17, 2026
452c85c
refactor: rename grafana resources
bornast Mar 18, 2026
fd465d2
feat: grafana addDashboard builder method
bornast Mar 18, 2026
37e5dde
refactor: config naming
bornast Mar 18, 2026
60d26c2
Merge branch 'feat/grafana-comp' into feat/grafana-dashboards
bornast Mar 18, 2026
509db81
feat: add name public prop to grafana component
bornast Mar 18, 2026
fd8dd9d
Merge branch 'feat/grafana-comp' into feat/grafana-dashboards
bornast Mar 18, 2026
29e7167
feat: introduce grafana connections
bornast Mar 19, 2026
9aced94
refactor: remove unnecessary lines
bornast Mar 19, 2026
b766efe
refactor: method signatures
bornast Mar 19, 2026
99376d5
feat: make grafana props readonly
bornast Mar 19, 2026
50398f2
Merge branch 'feat/grafana-comp' into feat/grafana-dashboards
bornast Mar 19, 2026
631ded8
feat: add name prop to grafana component
bornast Mar 19, 2026
8cec815
refactor: method signatures
bornast Mar 25, 2026
1626551
refactor: method signatures
bornast Mar 25, 2026
075cad6
Merge branch 'feat/grafana-comp' into feat/grafana-dashboards
bornast Mar 26, 2026
fa0dd95
feat: generic dashboard builder
bornast Mar 27, 2026
d07403c
Merge branch 'master' into feat/grafana-dashboards
bornast Mar 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/components/grafana/builder.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import * as pulumi from '@pulumi/pulumi';
import { AMPConnection, GrafanaConnection } from './connections';
import { Grafana } from './grafana';
import { GrafanaDashboard } from './dashboards/types';

export class GrafanaBuilder {
private readonly name: string;
private readonly connectionBuilders: GrafanaConnection.ConnectionBuilder[] =
[];
private readonly dashboardBuilders: GrafanaDashboard.DashboardBuilder[] = [];

constructor(name: string) {
this.name = name;
Expand All @@ -23,6 +25,12 @@ export class GrafanaBuilder {
return this;
}

public addDashboard(builder: GrafanaDashboard.DashboardBuilder): this {
this.dashboardBuilders.push(builder);

return this;
}

public build(opts: pulumi.ComponentResourceOptions = {}): Grafana {
if (!this.connectionBuilders.length) {
throw new Error(
Expand All @@ -34,6 +42,7 @@ export class GrafanaBuilder {
this.name,
{
connectionBuilders: this.connectionBuilders,
dashboardBuilders: this.dashboardBuilders,
},
opts,
);
Expand Down
42 changes: 42 additions & 0 deletions src/components/grafana/dashboards/dashboard-builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as pulumi from '@pulumi/pulumi';
import * as grafana from '@pulumiverse/grafana';
import { GrafanaConnection } from '../connections';
import { GrafanaDashboard } from './types';
import { Panel, PanelBuilder } from '../panels/types';

export class DashboardBuilder {
private title: pulumi.Input<string>;
private panelBuilders: PanelBuilder[] = [];

constructor(args: { title: pulumi.Input<string> }) {
this.title = args.title;
}

addPanel(builder: PanelBuilder): this {
this.panelBuilders.push(builder);
return this;
}

build(connections: GrafanaConnection[]): GrafanaDashboard.DashboardConfig {
const { title, panelBuilders } = this;
const panels = panelBuilders.map(build => build(connections));

return {
createResource(name, folder, opts) {
return new grafana.oss.Dashboard(
name,
{
folder: folder?.uid,
configJson: pulumi.jsonStringify({
title,
timezone: 'browser',
refresh: '10s',
Comment on lines +32 to +33
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I feel these should be configurable with these values as defaults

panels,
}),
},
{ parent: folder, ...opts },
);
},
};
}
}
4 changes: 2 additions & 2 deletions src/components/grafana/dashboards/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { default as WebServerSloDashboardBuilder } from './web-server-slo';
export * as panel from './panels';
export { DashboardBuilder } from './dashboard-builder';
export { createWebServerSloDashboard } from './web-server-slo';
76 changes: 12 additions & 64 deletions src/components/grafana/dashboards/types.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,21 @@
import * as pulumi from '@pulumi/pulumi';
import * as grafana from '@pulumiverse/grafana';
import { GrafanaConnection } from '../connections';

// TODO: Should we prefix all namespaces with `Studion`
export namespace Grafana {
// TODO: Create SLO abstraction that enables configuring:
// - panels (long-window SLI, long-window error budget)
// - alerts (long-window burn, short-window burn)
export type Threshold = {
value: number | null;
color: string;
};
export type Metric = {
label: string;
query: string;
thresholds: Threshold[];
};

export namespace GrafanaDashboard {
export type Args = {
title: pulumi.Input<string>;
provider: pulumi.Input<grafana.Provider>;
tags: pulumi.Input<pulumi.Input<string>[]>;
};

export type Panel = {
title: string;
gridPos: Panel.Position;
type: string;
datasource: string;
targets: {
expr: string;
legendFormat: string;
}[];
fieldConfig: {
defaults: {
unit?: string;
min?: number;
max?: number;
color?: {
mode: string;
};
thresholds?: {
mode: string;
steps: Threshold[];
};
custom?: {
lineInterpolation?: string;
spanNulls: boolean;
};
};
};
options?: {
colorMode?: string;
graphMode?: string;
justifyMode?: string;
textMode?: string;
reduceOptions?: {
calcs?: string[];
fields?: string;
values?: boolean;
};
};
};

export namespace Panel {
export type Position = {
x: number;
y: number;
w: number;
h: number;
};
export interface DashboardConfig {
createResource(
name: string,
folder?: grafana.oss.Folder,
opts?: pulumi.ComponentResourceOptions,
): grafana.oss.Dashboard;
}

export type DashboardBuilder = (
connections: GrafanaConnection[],
) => DashboardConfig;
}
Loading