Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
3 changes: 2 additions & 1 deletion cli/cliui/provisionerjob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import (
"testing"
"time"

"github.com/coder/coder/v2/testutil"
"github.com/stretchr/testify/assert"

"github.com/coder/coder/v2/testutil"

"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/codersdk"
Expand Down
5 changes: 5 additions & 0 deletions cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ func enablePrometheus(
), nil
}

//nolint:gocognit // TODO(dannyk): reduce complexity of this function
func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, io.Closer, error)) *serpent.Command {
if newAPI == nil {
newAPI = func(_ context.Context, o *coderd.Options) (*coderd.API, io.Closer, error) {
Expand Down Expand Up @@ -892,6 +893,10 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
return xerrors.Errorf("register agents prometheus metric: %w", err)
}
defer closeAgentsFunc()

if err = prometheusmetrics.Experiments(logger, options.PrometheusRegistry, options.DeploymentValues.Experiments.Value(), codersdk.ExperimentsAll); err != nil {
return xerrors.Errorf("register experiments metric: %w", err)
}
}

client := codersdk.New(localURL)
Expand Down
26 changes: 26 additions & 0 deletions coderd/prometheusmetrics/prometheusmetrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,32 @@ func AgentStats(ctx context.Context, logger slog.Logger, registerer prometheus.R
}, nil
}

// Experiments registers a metric which indicates whether each experiment is enabled or not.
func Experiments(_ slog.Logger, registerer prometheus.Registerer, exps []string, all codersdk.Experiments) error {
experimentsGauge := prometheus.NewGaugeVec(prometheus.GaugeOpts{
Namespace: "coderd",
Name: "experiments",
Help: "Indicates whether each experiment is enabled (1) or not (0)",
}, []string{"experiment"})
if err := registerer.Register(experimentsGauge); err != nil {
return err
}

for _, exp := range all {
var val float64
for _, enabled := range exps {
if string(exp) == enabled {
val = 1
break
}
}

experimentsGauge.WithLabelValues(string(exp)).Set(val)
}

return nil
}

// filterAcceptableAgentLabels handles a slightly messy situation whereby `prometheus-aggregate-agent-stats-by` can control on
// which labels agent stats are aggregated, but for these specific metrics in this file there is no `template` label value,
// and therefore we have to exclude it from the list of acceptable labels.
Expand Down
37 changes: 37 additions & 0 deletions coderd/prometheusmetrics/prometheusmetrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,43 @@ func TestAgentStats(t *testing.T) {
assert.EqualValues(t, golden, collected)
}

func TestExperimentsMetric(t *testing.T) {
t.Parallel()

log := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
reg := prometheus.NewRegistry()

const (
a codersdk.Experiment = "a"
b codersdk.Experiment = "b"
c codersdk.Experiment = "c"
)
allExps := codersdk.Experiments{a, b, c}
require.NoError(t, prometheusmetrics.Experiments(log, reg, []string{string(b), string(c)}, allExps))

expectation := map[codersdk.Experiment]float64{
a: 0,
b: 1,
c: 1,
}

out, err := reg.Gather()
require.NoError(t, err)
require.Lenf(t, out, 1, "unexpected number of registered metrics")

for _, metric := range out[0].GetMetric() {
require.Equal(t, "coderd_experiments", out[0].GetName())

labels := metric.GetLabel()
require.Lenf(t, labels, 1, "unexpected number of labels")

experiment := labels[0].GetValue()
expected, found := expectation[codersdk.Experiment(experiment)]
require.Truef(t, found, "did not find experiment %q in expectations", experiment)
require.EqualValues(t, expected, metric.GetGauge().GetValue())
}
}

func prepareWorkspaceAndAgent(t *testing.T, client *codersdk.Client, user codersdk.CreateFirstUserResponse, workspaceNum int) *agentsdk.Client {
authToken := uuid.NewString()

Expand Down