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  }