Skip to content

Flare

The general idea is to register a callback within your component to be called each time a flare is created. This uses Fx groups under the hood, but helpers are there to abstract all the complexity.

Once the callback is created, you will have to migrate the code related to your component from pkg/flare to your component.

Creating a callback

To add data to a flare, you first need to register a callback, also known as a FlareBuilder.

Within your component, create a method with the following signature: func (c *yourComp) fillFlare(fb flaretypes.FlareBuilder) error.

This function is called every time the Agent generates a flare—whether from the CLI, RemoteConfig, or from the running Agent. Your callback takes a FlareBuilder as parameter. This object provides all the helpers functions needed to add data to a flare (adding files, copying directories, scrubbing data, and so on).

Example:

import (
    yaml "gopkg.in/yaml.v2"

    flare "github.com/DataDog/datadog-agent/comp/core/flare/def"
)

func (c *myComponent) fillFlare(fb flare.FlareBuilder) error {
    // Creating a new file
    fb.AddFile( 
        "runtime_config_dump.yaml",
        []byte("content of my file"),
    ) //nolint:errcheck 

    // Copying a file from the disk into the flare
    fb.CopyFile("/etc/datadog-agent/datadog.yaml") //nolint:errcheck
    return nil
}

Read the FlareBuilder package documentation for more information on the API.

Any errors returned by the FlareBuilder methods are logged into a file shipped within the flare. This means, in most cases, you can ignore errors returned by the FlareBuilder methods. In all cases, ship as much data as possible in a flare instead of stopping at the first error.

Returning an error from your callback does not stop the flare from being created or sent. Rather, the error is logged into the flare too.

While it's possible to register multiple callbacks from the same component, try to keep all the flare code in a single callback.

Register your callback

Now you need to register your callback to be called each time a flare is created. To do this, your component constructor needs to provide a new Provider. Use NewProvider function for this.

Example:

import (
    flare "github.com/DataDog/datadog-agent/comp/core/flare/def"
)

type Provides struct {
    // [...]

    // Declare that your component will return a flare provider
    FlareProvider flare.Provider
}

func newComponent(deps Requires) Provides {
    // [...]

    return Provides{
        // [...]

        // NewProvider will wrap your callback in order to be use as a 'Provider'
        FlareProvider: flare.NewProvider(myComponent.fillFlare),
    }, nil
}

Testing

The flare component offers a FlareBuilder mock to test your callback.

Example:

import (
    "testing"
    "github.com/DataDog/datadog-agent/comp/core/flare/helpers"
)

func TestFillFlare(t testing.T) {
    myComp := newComponent(...)

    flareBuilderMock := helpers.NewFlareBuilderMock(t)

    myComp.fillFlare(flareBuilderMock, false)

    flareBuilderMock.AssertFileExists("datadog.yaml")
    flareBuilderMock.AssertFileContent("some_file.txt", "my content")
    // ...
}

Migrating your code

Now comes the hard part: migrating the code from pkg/flare related to your component to your new callback.

The good news is that the code in pkg/flare already uses the FlareBuilder interface. So you shouldn't need to rewrite any logic. Don't forget to migrate the tests too and expand them (most of the flare features are not tested).

Keep in mind that the goal is to delete pkg/flare once the migration to component is done.