github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/engine/analytics/analytics_reporter_test.go (about) 1 package analytics 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 12 "github.com/tilt-dev/clusterid" 13 tiltanalytics "github.com/tilt-dev/tilt/internal/analytics" 14 "github.com/tilt-dev/tilt/internal/container" 15 "github.com/tilt-dev/tilt/internal/feature" 16 "github.com/tilt-dev/tilt/internal/k8s" 17 "github.com/tilt-dev/tilt/internal/store" 18 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 19 "github.com/tilt-dev/tilt/pkg/model" 20 "github.com/tilt-dev/wmclient/pkg/analytics" 21 ) 22 23 var ( 24 lu = v1alpha1.LiveUpdateSpec{Syncs: []v1alpha1.LiveUpdateSync{{ContainerPath: "/"}}} // non-empty LiveUpdate 25 26 imgTargDB = model.ImageTarget{BuildDetails: model.DockerBuild{}} 27 imgTargDBWithLU = model.ImageTarget{LiveUpdateSpec: lu, BuildDetails: model.DockerBuild{}} 28 29 kTarg = model.K8sTarget{} 30 dTarg = model.DockerComposeTarget{} 31 lTarg = model.LocalTarget{} 32 lSrvTarg = model.LocalTarget{ServeCmd: model.Cmd{Argv: []string{"echo", "hi"}}} 33 ) 34 35 var ( 36 r1 = "gcr.io/some-project-162817/one" 37 r2 = "gcr.io/some-project-162817/two" 38 r3 = "gcr.io/some-project-162817/three" 39 r4 = "gcr.io/some-project-162817/four" 40 41 iTargWithRef1 = iTargetForRef(r1).WithLiveUpdateSpec("one", lu).WithBuildDetails(model.DockerBuild{}) 42 iTargWithRef2 = iTargetForRef(r2).WithLiveUpdateSpec("two", lu).WithBuildDetails(model.DockerBuild{}) 43 iTargWithRef3 = iTargetForRef(r3).WithLiveUpdateSpec("three", lu).WithBuildDetails(model.DockerBuild{}) 44 iTargWithRef4NoLU = iTargetForRef(r4) 45 ) 46 47 func TestAnalyticsReporter_Everything(t *testing.T) { 48 tf := newAnalyticsReporterTestFixture(t) 49 50 tf.addManifest( 51 tf.nextManifest(). 52 WithLabels(map[string]string{"k8s": "k8s"}). 53 WithImageTarget(imgTargDB). 54 WithDeployTarget(kTarg)) // k8s 55 56 tf.addManifest(tf.nextManifest().WithImageTarget(imgTargDBWithLU)) // liveupdate 57 58 tf.addManifest( 59 tf.nextManifest(). 60 WithLabels(map[string]string{"k8s": "k8s1"}). 61 WithDeployTarget(kTarg)) // k8s, unbuilt 62 tf.addManifest( 63 tf.nextManifest(). 64 WithLabels(map[string]string{"k8s": "k8s2"}). 65 WithDeployTarget(kTarg)) // k8s, unbuilt 66 tf.addManifest( 67 tf.nextManifest(). 68 WithLabels(map[string]string{"k8s": "k8s3"}). 69 WithDeployTarget(kTarg)) // k8s, unbuilt 70 71 tf.addManifest( 72 tf.nextManifest(). 73 WithLabels(map[string]string{"dc": "dc1"}). 74 WithDeployTarget(dTarg)) // dc 75 tf.addManifest( 76 tf.nextManifest(). 77 WithLabels(map[string]string{"dc": "dc2"}). 78 WithDeployTarget(dTarg)) // dc 79 80 tf.addManifest(tf.nextManifest().WithImageTarget(imgTargDBWithLU).WithDeployTarget(dTarg)) // dc, liveupdate 81 tf.addManifest(tf.nextManifest().WithImageTargets( 82 []model.ImageTarget{imgTargDBWithLU, imgTargDBWithLU})) // liveupdate, multipleimageliveupdate 83 84 tf.addManifest(tf.nextManifest().WithDeployTarget(lTarg)) 85 tf.addManifest(tf.nextManifest().WithDeployTarget(lTarg)) 86 tf.addManifest(tf.nextManifest().WithDeployTarget(lSrvTarg)) 87 tf.addManifest(tf.nextManifest(), func(mt *store.ManifestTarget) { 88 mt.State.DisableState = v1alpha1.DisableStateDisabled 89 }) 90 91 state := tf.st.LockMutableStateForTesting() 92 state.TiltStartTime = time.Now() 93 94 state.CompletedBuildCount = 3 95 96 tf.st.UnlockMutableState() 97 tf.kClient.Registry = &v1alpha1.RegistryHosting{ 98 Host: "localhost:5000", 99 HostFromContainerRuntime: "registry:5000", 100 } 101 102 tf.run() 103 104 expectedTags := map[string]string{ 105 "builds.completed_count": "3", 106 "resource.count": "13", 107 "resource.dockercompose.count": "3", 108 "resource.unbuiltresources.count": "3", 109 "resource.liveupdate.count": "3", 110 "resource.k8s.count": "4", 111 "resource.sameimagemultiplecontainerliveupdate.count": "0", // tests for this below 112 "resource.multipleimageliveupdate.count": "1", 113 "resource.local.count": "3", 114 "resource.localserve.count": "1", 115 "resource.enabled.count": "12", 116 "tiltfile.error": "false", 117 "up.starttime": state.TiltStartTime.Format(time.RFC3339), 118 "env": k8s.AnalyticsEnv(clusterid.ProductDockerDesktop), 119 "term_mode": "0", 120 "k8s.runtime": "docker", 121 "k8s.registry.host": "1", 122 "k8s.registry.hostFromCluster": "1", 123 "label.count": "2", 124 "feature.testflag_enabled.enabled": "true", 125 } 126 127 tf.assertStats(t, expectedTags) 128 } 129 130 func TestAnalyticsReporter_SameImageMultiContainer(t *testing.T) { 131 tf := newAnalyticsReporterTestFixture(t) 132 133 injectCountsA := map[string]int{ 134 r1: 1, 135 r2: 2, 136 } 137 k8sTargA := kTarg.WithRefInjectCounts(injectCountsA) 138 tf.addManifest(tf.nextManifest(). 139 WithImageTarget(iTargWithRef1). 140 WithImageTarget(iTargWithRef2). 141 WithDeployTarget(k8sTargA)) 142 143 injectCountsB := map[string]int{ 144 r2: 2, 145 r3: 3, 146 } 147 k8sTargB := kTarg.WithRefInjectCounts(injectCountsB) 148 tf.addManifest(tf.nextManifest(). 149 WithImageTarget(iTargWithRef2). 150 WithImageTarget(iTargWithRef3). 151 WithDeployTarget(k8sTargB)) 152 153 tf.run() 154 155 assert.Equal(t, "2", tf.ma.Counts[0].Tags["resource.sameimagemultiplecontainerliveupdate.count"]) 156 } 157 158 func TestAnalyticsReporter_SameImageMultiContainer_NoIncr(t *testing.T) { 159 tf := newAnalyticsReporterTestFixture(t) 160 161 injectCounts := map[string]int{ 162 r1: 1, 163 r4: 2, 164 } 165 k8sTarg := kTarg.WithRefInjectCounts(injectCounts) 166 tf.addManifest(tf.nextManifest(). 167 WithImageTarget(iTargWithRef1). 168 WithImageTarget(iTargWithRef4NoLU). // injects multiple times, but no LU so won't record stat for it 169 WithDeployTarget(k8sTarg)) 170 171 tf.run() 172 173 assert.Equal(t, "0", tf.ma.Counts[0].Tags["resource.sameimagemultiplecontainerliveupdate.count"]) 174 } 175 176 func TestAnalyticsReporter_TiltfileError(t *testing.T) { 177 tf := newAnalyticsReporterTestFixture(t) 178 179 tf.addManifest(tf.nextManifest().WithImageTarget(model.ImageTarget{BuildDetails: model.DockerBuild{}})) 180 tf.addManifest(tf.nextManifest().WithDeployTarget(model.K8sTarget{})) 181 tf.addManifest(tf.nextManifest().WithDeployTarget(model.K8sTarget{})) 182 tf.addManifest(tf.nextManifest().WithDeployTarget(model.K8sTarget{})) 183 tf.addManifest(tf.nextManifest().WithDeployTarget(model.DockerComposeTarget{})) 184 tf.addManifest(tf.nextManifest().WithDeployTarget(model.DockerComposeTarget{})) 185 tf.addManifest(tf.nextManifest().WithDeployTarget(model.DockerComposeTarget{})) 186 tf.addManifest(tf.nextManifest().WithDeployTarget(model.DockerComposeTarget{})) 187 188 state := tf.st.LockMutableStateForTesting() 189 state.TiltStartTime = time.Now() 190 191 state.CompletedBuildCount = 3 192 193 state.TiltfileStates[model.MainTiltfileManifestName].AddCompletedBuild(model.BuildRecord{Error: errors.New("foo")}) 194 195 tf.st.UnlockMutableState() 196 197 tf.run() 198 199 expectedTags := map[string]string{ 200 "builds.completed_count": "3", 201 "tiltfile.error": "true", 202 "up.starttime": state.TiltStartTime.Format(time.RFC3339), 203 "env": k8s.AnalyticsEnv(clusterid.ProductDockerDesktop), 204 "term_mode": "0", 205 "k8s.runtime": "docker", 206 "feature.testflag_enabled.enabled": "true", 207 } 208 209 tf.assertStats(t, expectedTags) 210 } 211 212 type analyticsReporterTestFixture struct { 213 manifestCount int 214 ar *AnalyticsReporter 215 ma *analytics.MemoryAnalytics 216 kClient *k8s.FakeK8sClient 217 st *store.TestingStore 218 } 219 220 func newAnalyticsReporterTestFixture(t testing.TB) *analyticsReporterTestFixture { 221 st := store.NewTestingStore() 222 opter := tiltanalytics.NewFakeOpter(analytics.OptIn) 223 ma, a := tiltanalytics.NewMemoryTiltAnalyticsForTest(opter) 224 kClient := k8s.NewFakeK8sClient(t) 225 features := feature.Defaults{ 226 "testflag_disabled": feature.Value{Enabled: false}, 227 "testflag_enabled": feature.Value{Enabled: true}, 228 "obsoleteflag_enabled": feature.Value{Status: feature.Obsolete, Enabled: true}, 229 "obsoleteflag_disabled": feature.Value{Status: feature.Obsolete, Enabled: false}, 230 } 231 232 state := st.LockMutableStateForTesting() 233 state.Features = feature.FromDefaults(features).ToEnabled() 234 st.UnlockMutableState() 235 236 ar := ProvideAnalyticsReporter(a, st, kClient, clusterid.ProductDockerDesktop, features) 237 return &analyticsReporterTestFixture{ 238 manifestCount: 0, 239 ar: ar, 240 ma: ma, 241 kClient: kClient, 242 st: st, 243 } 244 } 245 246 type addManifestOpt func(mt *store.ManifestTarget) 247 248 func (artf *analyticsReporterTestFixture) addManifest(m model.Manifest, opts ...addManifestOpt) { 249 state := artf.st.LockMutableStateForTesting() 250 mt := store.NewManifestTarget(m) 251 mt.State.DisableState = v1alpha1.DisableStateEnabled 252 for _, opt := range opts { 253 opt(mt) 254 } 255 state.UpsertManifestTarget(mt) 256 artf.st.UnlockMutableState() 257 } 258 259 func (artf *analyticsReporterTestFixture) nextManifest() model.Manifest { 260 artf.manifestCount++ 261 return model.Manifest{Name: model.ManifestName(fmt.Sprintf("manifest%d", artf.manifestCount))} 262 } 263 264 func (artf *analyticsReporterTestFixture) run() { 265 artf.ar.report(context.Background()) 266 267 artf.ar.a.Flush(500 * time.Second) 268 } 269 270 func (artf *analyticsReporterTestFixture) assertStats(t *testing.T, expectedTags map[string]string) { 271 expectedCounts := []analytics.CountEvent{{Name: "up.running", N: 1, Tags: expectedTags}} 272 assert.Equal(t, expectedCounts, artf.ma.Counts) 273 } 274 275 func iTargetForRef(ref string) model.ImageTarget { 276 named := container.MustParseNamed(ref) 277 selector := container.NameSelector(named) 278 return model.MustNewImageTarget(selector) 279 }