From 3d1dee2fe8ac0ef04317c361fe02ba47577eed61 Mon Sep 17 00:00:00 2001 From: Nick Santos Date: Mon, 31 Aug 2020 13:54:31 -0400 Subject: [PATCH] metrics: add an experimental API for enabling hosted opentelemetry/opencensus metrics (#3736) --- internal/engine/configs/actions.go | 2 + internal/engine/configs/configs_controller.go | 1 + internal/engine/upper.go | 9 ++- internal/engine/upper_test.go | 33 +++++++++++ internal/store/engine_state.go | 2 + internal/tiltfile/metrics/metrics.go | 55 +++++++++++++++++++ internal/tiltfile/metrics/metrics_test.go | 41 ++++++++++++++ internal/tiltfile/tiltfile.go | 5 ++ internal/tiltfile/tiltfile_state.go | 2 + pkg/model/metrics.go | 9 +++ 10 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 internal/tiltfile/metrics/metrics.go create mode 100644 internal/tiltfile/metrics/metrics_test.go create mode 100644 pkg/model/metrics.go diff --git a/internal/engine/configs/actions.go b/internal/engine/configs/actions.go index 819cbf48a9..b7ac944026 100644 --- a/internal/engine/configs/actions.go +++ b/internal/engine/configs/actions.go @@ -19,6 +19,7 @@ type ConfigsReloadStartedAction struct { func (ConfigsReloadStartedAction) Action() {} type ConfigsReloadedAction struct { + // TODO(nick): Embed TiltfileLoadResult instead of copying fields. Manifests []model.Manifest TiltIgnoreContents string ConfigFiles []string @@ -29,6 +30,7 @@ type ConfigsReloadedAction struct { Features map[string]bool TeamID string TelemetrySettings model.TelemetrySettings + MetricsSettings model.MetricsSettings Secrets model.SecretSet DockerPruneSettings model.DockerPruneSettings AnalyticsTiltfileOpt analytics.Opt diff --git a/internal/engine/configs/configs_controller.go b/internal/engine/configs/configs_controller.go index 24aa70ac46..03ef8a60be 100644 --- a/internal/engine/configs/configs_controller.go +++ b/internal/engine/configs/configs_controller.go @@ -166,6 +166,7 @@ func (cc *ConfigsController) loadTiltfile(ctx context.Context, st store.RStore, Features: tlr.FeatureFlags, TeamID: tlr.TeamID, TelemetrySettings: tlr.TelemetrySettings, + MetricsSettings: tlr.MetricsSettings, Secrets: tlr.Secrets, AnalyticsTiltfileOpt: tlr.AnalyticsOpt, DockerPruneSettings: tlr.DockerPruneSettings, diff --git a/internal/engine/upper.go b/internal/engine/upper.go index 4b87d98b4a..65751ebc18 100644 --- a/internal/engine/upper.go +++ b/internal/engine/upper.go @@ -564,15 +564,20 @@ func handleConfigsReloaded( state.LogStore.ScrubSecretsStartingAt(newSecrets, event.CheckpointAtExecStart) // Add tiltignore if it exists, even if execution failed. - if event.TiltIgnoreContents != "" || event.Err != nil { + if event.TiltIgnoreContents != "" || event.Err == nil { state.TiltIgnoreContents = event.TiltIgnoreContents } // Add team id if it exists, even if execution failed. - if event.TeamID != "" || event.Err != nil { + if event.TeamID != "" || event.Err == nil { state.TeamID = event.TeamID } + // Add metrics if it exists, even if execution failed. + if event.MetricsSettings.Enabled || event.Err == nil { + state.MetricsSettings = event.MetricsSettings + } + // if the ConfigsReloadedAction came from a unit test, there might not be a current build if !b.Empty() { b.FinishTime = event.FinishTime diff --git a/internal/engine/upper_test.go b/internal/engine/upper_test.go index 142df72e7b..908a831cc2 100644 --- a/internal/engine/upper_test.go +++ b/internal/engine/upper_test.go @@ -3084,6 +3084,39 @@ fail('goodnight moon') }) } +func TestEnableMetrics(t *testing.T) { + f := newTestFixture(t) + defer f.TearDown() + + f.WriteFile("Tiltfile", ` +experimental_metrics_settings(enabled=True) +fail('goodnight moon') +`) + + f.loadAndStart() + + f.WaitUntil("Tiltfile loaded", func(state store.EngineState) bool { + return len(state.TiltfileState.BuildHistory) == 1 + }) + f.withState(func(state store.EngineState) { + assert.True(t, state.MetricsSettings.Enabled) + }) + + f.WriteFile("Tiltfile", ` +experimental_metrics_settings(enabled=False) +k8s_yaml('snack.yaml') +`) + f.WriteFile("snack.yaml", simpleYAML) + f.fsWatcher.Events <- watch.NewFileEvent(f.JoinPath("Tiltfile")) + + f.WaitUntil("Tiltfile reloaded", func(state store.EngineState) bool { + return len(state.TiltfileState.BuildHistory) == 2 + }) + f.withState(func(state store.EngineState) { + assert.False(t, state.MetricsSettings.Enabled) + }) +} + func TestSecretScrubbed(t *testing.T) { f := newTestFixture(t) defer f.TearDown() diff --git a/internal/store/engine_state.go b/internal/store/engine_state.go index a9667d6541..707939cc04 100644 --- a/internal/store/engine_state.go +++ b/internal/store/engine_state.go @@ -110,6 +110,8 @@ type EngineState struct { TelemetrySettings model.TelemetrySettings + MetricsSettings model.MetricsSettings + UserConfigState model.UserConfigState } diff --git a/internal/tiltfile/metrics/metrics.go b/internal/tiltfile/metrics/metrics.go new file mode 100644 index 0000000000..ca90cfb2d7 --- /dev/null +++ b/internal/tiltfile/metrics/metrics.go @@ -0,0 +1,55 @@ +package metrics + +import ( + "go.starlark.net/starlark" + + "github.com/tilt-dev/tilt/internal/tiltfile/starkit" + "github.com/tilt-dev/tilt/pkg/model" +) + +type Extension struct{} + +func NewExtension() Extension { + return Extension{} +} + +func (e Extension) NewState() interface{} { + return model.MetricsSettings{ + Address: "opentelemetry.tilt.dev:443", + } +} + +func (Extension) OnStart(env *starkit.Environment) error { + return env.AddBuiltin("experimental_metrics_settings", setMetricsSettings) +} + +func setMetricsSettings(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + err := starkit.SetState(thread, func(settings model.MetricsSettings) (model.MetricsSettings, error) { + err := starkit.UnpackArgs(thread, fn.Name(), args, kwargs, + "enabled?", &settings.Enabled, + "address?", &settings.Address, + "insecure?", &settings.Insecure) + if err != nil { + return model.MetricsSettings{}, err + } + return settings, nil + }) + + return starlark.None, err +} + +var _ starkit.StatefulExtension = Extension{} + +func MustState(model starkit.Model) model.MetricsSettings { + state, err := GetState(model) + if err != nil { + panic(err) + } + return state +} + +func GetState(m starkit.Model) (model.MetricsSettings, error) { + var state model.MetricsSettings + err := m.Load(&state) + return state, err +} diff --git a/internal/tiltfile/metrics/metrics_test.go b/internal/tiltfile/metrics/metrics_test.go new file mode 100644 index 0000000000..4204b64afe --- /dev/null +++ b/internal/tiltfile/metrics/metrics_test.go @@ -0,0 +1,41 @@ +package metrics + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/tilt-dev/tilt/internal/tiltfile/starkit" +) + +func TestMetricsEnabled(t *testing.T) { + f := newFixture(t) + defer f.TearDown() + f.File("Tiltfile", "experimental_metrics_settings(enabled=True)") + result, err := f.ExecFile("Tiltfile") + + assert.NoError(t, err) + assert.True(t, MustState(result).Enabled) + assert.Equal(t, "opentelemetry.tilt.dev:443", MustState(result).Address) + assert.False(t, MustState(result).Insecure) +} + +func TestMetricsAddress(t *testing.T) { + f := newFixture(t) + defer f.TearDown() + + f.File("Tiltfile", ` +experimental_metrics_settings(enabled=True) +experimental_metrics_settings(address='localhost:5678') +experimental_metrics_settings(insecure=True) +`) + result, err := f.ExecFile("Tiltfile") + assert.NoError(t, err) + assert.True(t, MustState(result).Enabled) + assert.Equal(t, "localhost:5678", MustState(result).Address) + assert.True(t, MustState(result).Insecure) +} + +func newFixture(tb testing.TB) *starkit.Fixture { + return starkit.NewFixture(tb, NewExtension()) +} diff --git a/internal/tiltfile/tiltfile.go b/internal/tiltfile/tiltfile.go index e417318836..524bb0510c 100644 --- a/internal/tiltfile/tiltfile.go +++ b/internal/tiltfile/tiltfile.go @@ -10,6 +10,7 @@ import ( "time" "github.com/tilt-dev/tilt/internal/tiltfile/config" + "github.com/tilt-dev/tilt/internal/tiltfile/metrics" "github.com/tilt-dev/tilt/internal/tiltfile/starkit" wmanalytics "github.com/tilt-dev/wmclient/pkg/analytics" @@ -51,6 +52,7 @@ type TiltfileLoadResult struct { FeatureFlags map[string]bool TeamID string TelemetrySettings model.TelemetrySettings + MetricsSettings model.MetricsSettings Secrets model.SecretSet Error error DockerPruneSettings model.DockerPruneSettings @@ -208,6 +210,9 @@ func (tfl tiltfileLoader) Load(ctx context.Context, filename string, userConfigS telemetrySettings, _ := telemetry.GetState(result) tlr.TelemetrySettings = telemetrySettings + metricsSettings, _ := metrics.GetState(result) + tlr.MetricsSettings = metricsSettings + us, _ := updatesettings.GetState(result) tlr.UpdateSettings = us diff --git a/internal/tiltfile/tiltfile_state.go b/internal/tiltfile/tiltfile_state.go index e849937233..ea78f65bf8 100644 --- a/internal/tiltfile/tiltfile_state.go +++ b/internal/tiltfile/tiltfile_state.go @@ -27,6 +27,7 @@ import ( "github.com/tilt-dev/tilt/internal/tiltfile/io" tiltfile_k8s "github.com/tilt-dev/tilt/internal/tiltfile/k8s" "github.com/tilt-dev/tilt/internal/tiltfile/k8scontext" + "github.com/tilt-dev/tilt/internal/tiltfile/metrics" "github.com/tilt-dev/tilt/internal/tiltfile/os" "github.com/tilt-dev/tilt/internal/tiltfile/secretsettings" "github.com/tilt-dev/tilt/internal/tiltfile/shlex" @@ -198,6 +199,7 @@ func (s *tiltfileState) loadManifests(absFilename string, userConfigState model. s.configExt, starlarkstruct.NewExtension(), telemetry.NewExtension(), + metrics.NewExtension(), updatesettings.NewExtension(), secretsettings.NewExtension(), encoding.NewExtension(), diff --git a/pkg/model/metrics.go b/pkg/model/metrics.go new file mode 100644 index 0000000000..f3c6dcc3f7 --- /dev/null +++ b/pkg/model/metrics.go @@ -0,0 +1,9 @@ +package model + +// Metrics settings generally map to exporter options +// https://pkg.go.dev/contrib.go.opencensus.io/exporter/ocagent?tab=doc#ExporterOption +type MetricsSettings struct { + Enabled bool + Address string + Insecure bool +}