// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 //go:build !consulent package agent import ( "context" "net/http" "net/http/httptest" "testing" "github.com/hashicorp/consul/agent/structs" "github.com/stretchr/testify/require" ) func TestOperator_Usage(t *testing.T) { if testing.Short() { t.Skip("too slow for testing.Short") } t.Parallel() a := NewTestAgent(t, "") defer a.Shutdown() req, err := http.NewRequest("GET", "/v1/operator/usage", nil) require.NoError(t, err) // Register a few services require.NoError(t, upsertTestService(a.RPC, "", "dc1", "web", "test-node", "", func(svc *structs.NodeService) { svc.ID = "web1" })) require.NoError(t, upsertTestService(a.RPC, "", "dc1", "web", "test-node", "", func(svc *structs.NodeService) { svc.ID = "web2" })) require.NoError(t, upsertTestService(a.RPC, "", "dc1", "db", "test-node", "")) require.NoError(t, upsertTestService(a.RPC, "", "dc1", "web-proxy", "test-node", "", func(svc *structs.NodeService) { svc.Kind = structs.ServiceKindConnectProxy svc.Proxy = structs.ConnectProxyConfig{ DestinationServiceName: "web", DestinationServiceID: "web1", } })) // Add connect-native service to check that we include it in the billable service instances require.NoError(t, upsertTestService(a.RPC, "", "dc1", "connect-native-app", "test-node", "", func(svc *structs.NodeService) { svc.Connect.Native = true })) raw, err := a.srv.OperatorUsage(httptest.NewRecorder(), req) require.NoError(t, err) expected := map[string]structs.ServiceUsage{ "dc1": { Services: 5, ServiceInstances: 6, ConnectServiceInstances: map[string]int{ "api-gateway": 0, "connect-native": 1, "connect-proxy": 1, "ingress-gateway": 0, "mesh-gateway": 0, "terminating-gateway": 0, }, // 4 = 6 total service instances - 1 connect proxy - 1 consul service BillableServiceInstances: 4, Nodes: 2, }, } require.Equal(t, expected, raw.(structs.Usage).Usage) } func upsertTestService(rpc rpcFn, secret, datacenter, name, node, partition string, modifyFuncs ...func(*structs.NodeService)) error { req := structs.RegisterRequest{ Datacenter: datacenter, Node: node, SkipNodeUpdate: true, Service: &structs.NodeService{ ID: name, Service: name, Port: 8080, }, WriteRequest: structs.WriteRequest{Token: secret}, } for _, modify := range modifyFuncs { modify(req.Service) } var out struct{} return rpc(context.Background(), "Catalog.Register", &req, &out) }