agones.dev/agones@v1.53.0/pkg/gameserversets/metrics_test.go (about) 1 // Copyright 2025 Google LLC All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gameserversets 16 17 import ( 18 "bufio" 19 "context" 20 "net/http" 21 "strings" 22 "testing" 23 "time" 24 25 agonesv1 "agones.dev/agones/pkg/apis/agones/v1" 26 mt "agones.dev/agones/pkg/metrics" 27 agtesting "agones.dev/agones/pkg/testing" 28 "agones.dev/agones/pkg/util/httpserver" 29 utilruntime "agones.dev/agones/pkg/util/runtime" 30 "agones.dev/agones/test/e2e/framework" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 "go.opencensus.io/stats/view" 34 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 "k8s.io/apimachinery/pkg/runtime" 36 k8stesting "k8s.io/client-go/testing" 37 ) 38 39 func resetMetrics() { 40 unRegisterViews() 41 registerViews() 42 } 43 44 func TestGSSMetrics(t *testing.T) { 45 resetMetrics() 46 47 utilruntime.FeatureTestMutex.Lock() 48 defer utilruntime.FeatureTestMutex.Unlock() 49 50 conf := mt.Config{ 51 PrometheusMetrics: true, 52 } 53 server := &httpserver.Server{ 54 Port: "3001", 55 Logger: framework.TestLogger(t), 56 } 57 58 health, closer := mt.SetupMetrics(conf, server) 59 defer t.Cleanup(closer) 60 61 assert.NotNil(t, health, "Health check handler should not be nil") 62 server.Handle("/", health) 63 64 gsSet := defaultFixture() 65 c, m := newFakeController() 66 expected := 10 67 count := 0 68 69 m.AgonesClient.AddReactor("create", "gameservers", func(action k8stesting.Action) (bool, runtime.Object, error) { 70 ca := action.(k8stesting.CreateAction) 71 gs := ca.GetObject().(*agonesv1.GameServer) 72 73 assert.True(t, metav1.IsControlledBy(gs, gsSet)) 74 count++ 75 76 return true, gs, nil 77 }) 78 79 ctx, cancel := agtesting.StartInformers(m) 80 defer cancel() 81 82 err := c.addMoreGameServers(ctx, gsSet, expected) 83 assert.Nil(t, err) 84 assert.Equal(t, expected, count) 85 agtesting.AssertEventContains(t, m.FakeRecorder.Events, "SuccessfulCreate") 86 87 ctxHTTP, cancelHTTP := context.WithCancel(context.Background()) 88 defer cancelHTTP() 89 90 // Start the HTTP server 91 go func() { 92 _ = server.Run(ctxHTTP, 0) 93 }() 94 time.Sleep(300 * time.Millisecond) 95 96 resp, err := http.Get("http://localhost:3001/metrics") 97 require.NoError(t, err, "Failed to GET metrics endpoint") 98 defer func() { 99 assert.NoError(t, resp.Body.Close()) 100 }() 101 102 assert.Equal(t, http.StatusOK, resp.StatusCode, "Expected status code 200") 103 104 metricsSet := collectMetricNames(resp) 105 expectedMetrics := getMetricNames() 106 107 for _, metric := range expectedMetrics { 108 assert.Contains(t, metricsSet, metric, "Missing expected metric: %s", metric) 109 } 110 } 111 112 // getMetricNames returns all metric view names. 113 func getMetricNames() []string { 114 var metricNames []string 115 for _, v := range stateViews { 116 metricName := "agones_" + v.Name 117 118 // Check if the aggregation type is Distribution 119 if v.Aggregation.Type == view.AggTypeDistribution { 120 // If it's a distribution, we append _bucket, _sum, and _count 121 metricNames = append(metricNames, 122 metricName+"_bucket", 123 metricName+"_sum", 124 metricName+"_count", 125 ) 126 } else { 127 metricNames = append(metricNames, metricName) 128 129 } 130 } 131 return metricNames 132 } 133 134 func collectMetricNames(resp *http.Response) map[string]bool { 135 metrics := make(map[string]bool) 136 scanner := bufio.NewScanner(resp.Body) 137 for scanner.Scan() { 138 line := scanner.Text() 139 if strings.HasPrefix(line, "#") || line == "" { 140 continue 141 } 142 fields := strings.Fields(line) 143 if len(fields) > 0 { 144 // Extract only the metric name, excluding labels 145 metricName := fields[0] 146 if idx := strings.Index(metricName, "{"); idx != -1 { 147 metricName = metricName[:idx] 148 } 149 metrics[metricName] = true 150 } 151 } 152 return metrics 153 }