go

module
v0.0.0-...-83e2e3a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 23, 2025 License: Apache-2.0

README

Grafana Foundation SDK – Go

A set of tools, types and builder libraries for building and manipulating Grafana objects in Go.

[!NOTE] This branch contains types and builders generated for Grafana v11.2.x. Other supported versions of Grafana can be found at this repository's root.

Installing

go get github.com/grafana/grafana-foundation-sdk/go@v11.2.x+cog-v0.0.x

Example usage

More examples can be found at the repository root.

Building a dashboard
package main

import (
    "encoding/json"
    "fmt"

    "github.com/grafana/grafana-foundation-sdk/go/common"
    "github.com/grafana/grafana-foundation-sdk/go/dashboard"
    "github.com/grafana/grafana-foundation-sdk/go/prometheus"
    "github.com/grafana/grafana-foundation-sdk/go/timeseries"
)

func main() {
    builder := dashboard.NewDashboardBuilder("Sample dashboard").
        Uid("generated-from-go").
        Tags([]string{"generated", "from", "go"}).
        Refresh("1m").
        Time("now-30m", "now").
        Timezone(common.TimeZoneBrowser).
        WithRow(dashboard.NewRowBuilder("Overview")).
        WithPanel(
            timeseries.NewPanelBuilder().
                Title("Network Received").
                Unit("bps").
                Min(0).
                WithTarget(
                    prometheus.NewDataqueryBuilder().
                        Expr(`rate(node_network_receive_bytes_total{job="integrations/raspberrypi-node", device!="lo"}[$__rate_interval]) * 8`).
                        LegendFormat("{{ device }}"),
                ),
        )

    sampleDashboard, err := builder.Build()
    if err != nil {
        panic(err)
    }
    dashboardJson, err := json.MarshalIndent(sampleDashboard, "", "  ")
    if err != nil {
        panic(err)
    }

    fmt.Println(string(dashboardJson))
}
Unmarshaling a dashboard
package main

import (
    "encoding/json"
    "fmt"
    "os"

    "github.com/grafana/grafana-foundation-sdk/go/cog/plugins"
    "github.com/grafana/grafana-foundation-sdk/go/dashboard"
)

func main() {
    // Required to correctly unmarshal panels and dataqueries
    plugins.RegisterDefaultPlugins()

    dashboardJSON, err := os.ReadFile("dashboard.json")
    if err != nil {
        panic(err)
    }

    sampleDashboard := &dashboard.Dashboard{}
    if err := json.Unmarshal(dashboardJSON, sampleDashboard); err != nil {
        panic(fmt.Sprintf("%s", err))
    }

    fmt.Printf("%#v\n", sampleDashboard)
}
Defining a custom query type

While the SDK ships with support for all core datasources and their query types, it can be extended for private/third-party plugins.

To do so, define a type and a builder for the custom query:

package main

import (
    "encoding/json"

    "github.com/grafana/grafana-foundation-sdk/go/cog"
	"github.com/grafana/grafana-foundation-sdk/go/cog/variants"
)

type CustomQuery struct {
    // RefId and Hide are expected on all queries
    RefId        *string `json:"refId,omitempty"`
    Hide         *bool   `json:"hide,omitempty"`

    // Query is specific to the CustomQuery type
    Query        string  `json:"query,omitempty"`
}

// Let cog know that CustomQuery is a Dataquery variant
func (resource CustomQuery) ImplementsDataqueryVariant() {}

func CustomQueryVariantConfig() variants.DataqueryConfig {
    return variants.DataqueryConfig{
        Identifier: "custom", // datasource plugin ID
        DataqueryUnmarshaler: func(raw []byte) (variants.Dataquery, error) {
            dataquery := &CustomQuery{}

            if err := json.Unmarshal(raw, dataquery); err != nil {
                return nil, err
            }

            return dataquery, nil
        },
    }
}

// Compile-time check to ensure that CustomQueryBuilder indeed is
// a builder for variants.Dataquery
var _ cog.Builder[variants.Dataquery] = (*CustomQueryBuilder)(nil)

type CustomQueryBuilder struct {
    internal *CustomQuery
}

func NewCustomQueryBuilder(query string) *CustomQueryBuilder {
    return &CustomQueryBuilder{
        internal: &CustomQuery{Query: query},
    }
}

func (builder *CustomQueryBuilder) Build() (variants.Dataquery, error) {
    return *builder.internal, nil
}

func (builder *CustomQueryBuilder) RefId(refId string) *CustomQueryBuilder {
    builder.internal.RefId = &refId
    return builder
}

func (builder *CustomQueryBuilder) Hide(hide bool) *CustomQueryBuilder {
    builder.internal.Hide = &hide
    return builder
}

Register the type with cog, and use it as usual to build a dashboard:

package main

import (
    "encoding/json"
    "fmt"

    "github.com/grafana/grafana-foundation-sdk/go/cog"
    "github.com/grafana/grafana-foundation-sdk/go/cog/plugins"
    "github.com/grafana/grafana-foundation-sdk/go/dashboard"
    "github.com/grafana/grafana-foundation-sdk/go/timeseries"
)

func main() {
    // Required to correctly unmarshal panels and dataqueries
    plugins.RegisterDefaultPlugins()

    // This lets cog know about the newly created query type and how to unmarshal it.
    cog.NewRuntime().RegisterDataqueryVariant(CustomQueryVariantConfig())

    sampleDashboard, err := dashboard.NewDashboardBuilder("Custom query type").
        Uid("test-custom-query-type").
        Refresh("1m").
        Time("now-30m", "now").
        WithRow(dashboard.NewRowBuilder("Overview")).
        WithPanel(
            timeseries.NewPanelBuilder().
                Title("Sample panel").
                WithTarget(
                    NewCustomQueryBuilder("query here"),
                ),
        ).
        Build()
    if err != nil {
        panic(err)
    }

    dashboardJson, err := json.MarshalIndent(sampleDashboard, "", "  ")
    if err != nil {
        panic(err)
    }

    fmt.Println(string(dashboardJson))
}
Defining a custom panel type

While the SDK ships with support for all core panels, it can be extended for private/third-party plugins.

To do so, define a type and a builder for the custom panel's options:

package main

import (
    "encoding/json"

    "github.com/grafana/grafana-foundation-sdk/go/cog"
	"github.com/grafana/grafana-foundation-sdk/go/cog/variants"
    "github.com/grafana/grafana-foundation-sdk/go/dashboard"
)

type CustomPanelOptions struct {
    MakeBeautiful bool `json:"makeBeautiful"`
}

func CustomPanelVariantConfig() variants.PanelcfgConfig {
    return variants.PanelcfgConfig{
        Identifier: "custom-panel", // plugin ID
        OptionsUnmarshaler: func(raw []byte) (any, error) {
            options := CustomPanelOptions{}

            if err := json.Unmarshal(raw, &options); err != nil {
                return nil, err
            }

            return options, nil
        },
    }
}

// Compile-time check to ensure that CustomPanelBuilder indeed is
// a builder for a dashboard.Panel.
var _ cog.Builder[dashboard.Panel] = (*CustomPanelBuilder)(nil)

type CustomPanelBuilder struct {
    internal *dashboard.Panel
    errors   map[string]cog.BuildErrors
}

func NewCustomPanelBuilder() *CustomPanelBuilder {
    return &CustomPanelBuilder{
        internal: &dashboard.Panel{
            Type: "custom-panel",
        },
        errors: make(map[string]cog.BuildErrors),
    }
}

func (builder *CustomPanelBuilder) Build() (dashboard.Panel, error) {
    var errs cog.BuildErrors

    for _, err := range builder.errors {
        errs = append(errs, cog.MakeBuildErrors("CustomPanel", err)...)
    }

    if len(errs) != 0 {
        return dashboard.Panel{}, errs
    }

    return *builder.internal, nil
}

func (builder *CustomPanelBuilder) Title(title string) *CustomPanelBuilder {
    builder.internal.Title = &title

    return builder
}

func (builder *CustomPanelBuilder) WithTarget(targets cog.Builder[variants.Dataquery]) *CustomPanelBuilder {
    targetsResource, err := targets.Build()
    if err != nil {
        builder.errors["targets"] = err.(cog.BuildErrors)
        return builder
    }
    builder.internal.Targets = append(builder.internal.Targets, targetsResource)

    return builder
}

// [other panel options omitted for brevity]

func (builder *CustomPanelBuilder) MakeBeautiful() *CustomPanelBuilder {
    if builder.internal.Options == nil {
        builder.internal.Options = &CustomPanelOptions{}
    }
    builder.internal.Options.(*CustomPanelOptions).MakeBeautiful = true

    return builder
}

Register the type with cog, and use it as usual to build a dashboard:

package main

import (
    "encoding/json"
    "fmt"

    "github.com/grafana/grafana-foundation-sdk/go/cog"
    "github.com/grafana/grafana-foundation-sdk/go/cog/plugins"
    "github.com/grafana/grafana-foundation-sdk/go/dashboard"
)

func main() {
    // Required to correctly unmarshal panels and dataqueries
    plugins.RegisterDefaultPlugins()

    // This lets cog know about the newly created panel type and how to unmarshal it.
    cog.NewRuntime().RegisterPanelcfgVariant(CustomPanelVariantConfig())

    sampleDashboard, err := dashboard.NewDashboardBuilder("Custom panel type").
        Uid("test-custom-panel").
        Refresh("1m").
        Time("now-30m", "now").
        WithRow(dashboard.NewRowBuilder("Overview")).
        WithPanel(
            NewCustomPanelBuilder().
                Title("Sample panel").
                MakeBeautiful(),
        ).
        Build()
    if err != nil {
        panic(err)
    }

    dashboardJson, err := json.MarshalIndent(sampleDashboard, "", "  ")
    if err != nil {
        panic(err)
    }

    fmt.Println(string(dashboardJson))
}

Maturity

The code in this repository should be considered as "public preview". While it is used by Grafana Labs in production, it still is under active development.

[!NOTE] Bugs and issues are handled solely by Engineering teams. On-call support or SLAs are not available.

License

Apache 2.0 License

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL