github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/rollout-service/pkg/metrics/metrics_test.go (about) 1 /*This file is part of kuberpult. 2 3 Kuberpult is free software: you can redistribute it and/or modify 4 it under the terms of the Expat(MIT) License as published by 5 the Free Software Foundation. 6 7 Kuberpult is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 MIT License for more details. 11 12 You should have received a copy of the MIT License 13 along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>. 14 15 Copyright 2023 freiheit.com*/ 16 17 package metrics 18 19 import ( 20 "context" 21 "io" 22 "net/http/httptest" 23 "testing" 24 "time" 25 26 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 27 "github.com/argoproj/gitops-engine/pkg/health" 28 pkgmetrics "github.com/freiheit-com/kuberpult/pkg/metrics" 29 "github.com/freiheit-com/kuberpult/services/rollout-service/pkg/service" 30 "github.com/freiheit-com/kuberpult/services/rollout-service/pkg/versions" 31 "github.com/google/go-cmp/cmp" 32 ) 33 34 type step struct { 35 VersionsEvent *versions.KuberpultEvent 36 ArgoEvent *service.ArgoEvent 37 Disconnect bool 38 39 ExpectedBody string 40 } 41 42 func TestMetric(t *testing.T) { 43 tcs := []struct { 44 Name string 45 Steps []step 46 }{ 47 { 48 Name: "doesnt write metrics when argocd data is missing", 49 Steps: []step{ 50 { 51 VersionsEvent: &versions.KuberpultEvent{ 52 Application: "foo", 53 Environment: "bar", 54 EnvironmentGroup: "buz", 55 Version: &versions.VersionInfo{ 56 Version: 1, 57 DeployedAt: time.Unix(1000, 0), 58 }, 59 }, 60 ExpectedBody: ``, 61 }, 62 }, 63 }, 64 { 65 Name: "doesn't write metrics when kuberpult data is missing", 66 Steps: []step{ 67 { 68 ArgoEvent: &service.ArgoEvent{ 69 Application: "foo", 70 Environment: "bar", 71 Version: &versions.VersionInfo{ 72 Version: 1, 73 }, 74 HealthStatusCode: health.HealthStatusHealthy, 75 SyncStatusCode: v1alpha1.SyncStatusCodeUnknown, 76 }, 77 ExpectedBody: ``, 78 }, 79 }, 80 }, 81 { 82 Name: "writes out 0 for successful deployments", 83 Steps: []step{ 84 { 85 VersionsEvent: &versions.KuberpultEvent{ 86 Application: "foo", 87 Environment: "bar", 88 EnvironmentGroup: "buz", 89 Version: &versions.VersionInfo{ 90 Version: 1, 91 DeployedAt: time.Unix(1000, 0), 92 }, 93 }, 94 ArgoEvent: &service.ArgoEvent{ 95 Application: "foo", 96 Environment: "bar", 97 Version: &versions.VersionInfo{ 98 Version: 1, 99 }, 100 HealthStatusCode: health.HealthStatusHealthy, 101 SyncStatusCode: v1alpha1.SyncStatusCodeUnknown, 102 }, 103 ExpectedBody: `# HELP rollout_lag_seconds 104 # TYPE rollout_lag_seconds gauge 105 rollout_lag_seconds{kuberpult_application="foo",kuberpult_environment="bar",kuberpult_environment_group="buz"} 0 106 `, 107 }, 108 }, 109 }, 110 { 111 Name: "writes out time diff for missing deployments", 112 Steps: []step{ 113 { 114 VersionsEvent: &versions.KuberpultEvent{ 115 Application: "foo", 116 Environment: "bar", 117 EnvironmentGroup: "buz", 118 Version: &versions.VersionInfo{ 119 Version: 2, 120 DeployedAt: time.Unix(1000, 0), 121 }, 122 }, 123 ArgoEvent: &service.ArgoEvent{ 124 Application: "foo", 125 Environment: "bar", 126 Version: &versions.VersionInfo{ 127 Version: 1, 128 }, 129 HealthStatusCode: health.HealthStatusHealthy, 130 SyncStatusCode: v1alpha1.SyncStatusCodeUnknown, 131 }, 132 ExpectedBody: `# HELP rollout_lag_seconds 133 # TYPE rollout_lag_seconds gauge 134 rollout_lag_seconds{kuberpult_application="foo",kuberpult_environment="bar",kuberpult_environment_group="buz"} 1000 135 `, 136 }, 137 }, 138 }, 139 { 140 Name: "reports no lag if the application is just unhealthy", 141 Steps: []step{ 142 { 143 VersionsEvent: &versions.KuberpultEvent{ 144 Application: "foo", 145 Environment: "bar", 146 EnvironmentGroup: "buz", 147 Version: &versions.VersionInfo{ 148 Version: 2, 149 DeployedAt: time.Unix(1000, 0), 150 }, 151 }, 152 ArgoEvent: &service.ArgoEvent{ 153 Application: "foo", 154 Environment: "bar", 155 Version: &versions.VersionInfo{ 156 Version: 2, 157 }, 158 HealthStatusCode: health.HealthStatusDegraded, 159 SyncStatusCode: v1alpha1.SyncStatusCodeUnknown, 160 }, 161 ExpectedBody: `# HELP rollout_lag_seconds 162 # TYPE rollout_lag_seconds gauge 163 rollout_lag_seconds{kuberpult_application="foo",kuberpult_environment="bar",kuberpult_environment_group="buz"} 0 164 `, 165 }, 166 }, 167 }, 168 { 169 Name: "removes metrics when app is removed", 170 Steps: []step{ 171 { 172 VersionsEvent: &versions.KuberpultEvent{ 173 Application: "foo", 174 Environment: "bar", 175 EnvironmentGroup: "buz", 176 Version: &versions.VersionInfo{ 177 Version: 2, 178 DeployedAt: time.Unix(1000, 0), 179 }, 180 }, 181 ArgoEvent: &service.ArgoEvent{ 182 Application: "foo", 183 Environment: "bar", 184 Version: &versions.VersionInfo{ 185 Version: 1, 186 }, 187 HealthStatusCode: health.HealthStatusHealthy, 188 SyncStatusCode: v1alpha1.SyncStatusCodeUnknown, 189 }, 190 ExpectedBody: `# HELP rollout_lag_seconds 191 # TYPE rollout_lag_seconds gauge 192 rollout_lag_seconds{kuberpult_application="foo",kuberpult_environment="bar",kuberpult_environment_group="buz"} 1000 193 `, 194 }, 195 { 196 VersionsEvent: &versions.KuberpultEvent{ 197 Application: "foo", 198 Environment: "bar", 199 EnvironmentGroup: "buz", 200 Version: &versions.VersionInfo{ 201 Version: 0, 202 }, 203 }, 204 ExpectedBody: ``, 205 }, 206 }, 207 }, 208 { 209 Name: "updates environment group when it changes", 210 Steps: []step{ 211 { 212 VersionsEvent: &versions.KuberpultEvent{ 213 Application: "foo", 214 Environment: "bar", 215 EnvironmentGroup: "buz", 216 Version: &versions.VersionInfo{ 217 Version: 2, 218 DeployedAt: time.Unix(1000, 0), 219 }, 220 }, 221 ArgoEvent: &service.ArgoEvent{ 222 Application: "foo", 223 Environment: "bar", 224 Version: &versions.VersionInfo{ 225 Version: 1, 226 }, 227 HealthStatusCode: health.HealthStatusHealthy, 228 SyncStatusCode: v1alpha1.SyncStatusCodeUnknown, 229 }, 230 ExpectedBody: `# HELP rollout_lag_seconds 231 # TYPE rollout_lag_seconds gauge 232 rollout_lag_seconds{kuberpult_application="foo",kuberpult_environment="bar",kuberpult_environment_group="buz"} 1000 233 `, 234 }, 235 { 236 VersionsEvent: &versions.KuberpultEvent{ 237 Application: "foo", 238 Environment: "bar", 239 EnvironmentGroup: "not-buz", 240 Version: &versions.VersionInfo{ 241 Version: 3, 242 DeployedAt: time.Unix(1500, 0), 243 }, 244 }, 245 ExpectedBody: `# HELP rollout_lag_seconds 246 # TYPE rollout_lag_seconds gauge 247 rollout_lag_seconds{kuberpult_application="foo",kuberpult_environment="bar",kuberpult_environment_group="not-buz"} 500 248 `, 249 }, 250 }, 251 }, 252 { 253 Name: "handles reconnects", 254 Steps: []step{ 255 { 256 VersionsEvent: &versions.KuberpultEvent{ 257 Application: "foo", 258 Environment: "bar", 259 EnvironmentGroup: "buz", 260 Version: &versions.VersionInfo{ 261 Version: 2, 262 DeployedAt: time.Unix(1000, 0), 263 }, 264 }, 265 ArgoEvent: &service.ArgoEvent{ 266 Application: "foo", 267 Environment: "bar", 268 Version: &versions.VersionInfo{ 269 Version: 1, 270 }, 271 HealthStatusCode: health.HealthStatusHealthy, 272 SyncStatusCode: v1alpha1.SyncStatusCodeUnknown, 273 }, 274 ExpectedBody: `# HELP rollout_lag_seconds 275 # TYPE rollout_lag_seconds gauge 276 rollout_lag_seconds{kuberpult_application="foo",kuberpult_environment="bar",kuberpult_environment_group="buz"} 1000 277 `, 278 }, 279 { 280 Disconnect: true, 281 ExpectedBody: `# HELP rollout_lag_seconds 282 # TYPE rollout_lag_seconds gauge 283 rollout_lag_seconds{kuberpult_application="foo",kuberpult_environment="bar",kuberpult_environment_group="buz"} 1000 284 `, 285 }, 286 { 287 VersionsEvent: &versions.KuberpultEvent{ 288 Application: "foo", 289 Environment: "bar", 290 EnvironmentGroup: "buz", 291 Version: &versions.VersionInfo{ 292 Version: 3, 293 DeployedAt: time.Unix(1500, 0), 294 }, 295 }, 296 ExpectedBody: `# HELP rollout_lag_seconds 297 # TYPE rollout_lag_seconds gauge 298 rollout_lag_seconds{kuberpult_application="foo",kuberpult_environment="bar",kuberpult_environment_group="buz"} 500 299 `, 300 }, 301 }, 302 }, 303 } 304 305 for _, tc := range tcs { 306 tc := tc 307 t.Run(tc.Name, func(t *testing.T) { 308 ctx, cancel := context.WithCancel(context.Background()) 309 mpv, handler, _ := pkgmetrics.Init() 310 srv := httptest.NewServer(handler) 311 defer srv.Close() 312 bc := service.New() 313 eCh := make(chan error) 314 doneCh := make(chan struct{}) 315 go func() { 316 eCh <- Metrics(ctx, bc, mpv, func() time.Time { return time.Unix(2000, 0).UTC() }, func() { doneCh <- struct{}{} }) 317 }() 318 for i, s := range tc.Steps { 319 if s.Disconnect { 320 bc.DisconnectAll() 321 } 322 if s.VersionsEvent != nil { 323 bc.ProcessKuberpultEvent(ctx, *s.VersionsEvent) 324 } 325 if s.ArgoEvent != nil { 326 bc.ProcessArgoEvent(ctx, *s.ArgoEvent) 327 } 328 <-doneCh 329 resp, err := srv.Client().Get(srv.URL + "/metrics") 330 if err != nil { 331 t.Errorf("error in step %d: %q", i, err) 332 } 333 if resp.StatusCode != 200 { 334 t.Errorf("invalid status in step %d: %d", i, resp.StatusCode) 335 } 336 body, err := io.ReadAll(resp.Body) 337 if err != nil { 338 t.Errorf("error reading body in step %d: %q", i, err) 339 } 340 if string(body) != s.ExpectedBody { 341 t.Errorf("wrong metrics received, diff: %s", cmp.Diff(s.ExpectedBody, string(body))) 342 } 343 } 344 cancel() 345 err := <-eCh 346 if err != nil { 347 t.Errorf("expected no error but got %q", err) 348 } 349 }) 350 351 } 352 }