diff --git a/frontend/Makefile b/frontend/Makefile index d99d70025..82b60b1d2 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -8,6 +8,7 @@ ARO_HCP_FRONTEND_IMAGE ?= $(ARO_HCP_BASE_IMAGE)/arohcpfrontend:$(COMMIT) RESOURCE_GROUP ?= CLUSTER_NAME ?= DEPLOYMENTNAME=$(RESOURCE_GROUP) +REGION ?= eastus frontend: @@ -31,7 +32,8 @@ deploy: oc process -f ./deploy/aro-hcp-frontend.yml --local \ -p ARO_HCP_FRONTEND_IMAGE=${ARO_HCP_FRONTEND_IMAGE} \ -p FRONTEND_MI_CLIENT_ID="$${FRONTEND_MI_CLIENT_ID}" \ - -p DB_NAME="$${DB_NAME}"| oc apply -f - + -p DB_NAME="$${DB_NAME}" + -p REGION=${REGION}| oc apply -f - undeploy: @test "${RESOURCE_GROUP}" != "" || (echo "RESOURCE_GROUP must be defined" && exit 1) @@ -50,7 +52,8 @@ deploy-private: oc process -f ./deploy/aro-hcp-frontend.yml --local \ -p ARO_HCP_FRONTEND_IMAGE=${ARO_HCP_FRONTEND_IMAGE} \ -p FRONTEND_MI_CLIENT_ID="$${FRONTEND_MI_CLIENT_ID}" \ - -p DB_NAME="$${DB_NAME}" > "$${TMP_DEPLOY}";\ + -p DB_NAME="$${DB_NAME}" \ + -p REGION=${REGION}> "$${TMP_DEPLOY}";\ az aks command invoke --resource-group ${RESOURCE_GROUP} --name ${CLUSTER_NAME} --command "kubectl create -f $$(basename $${TMP_DEPLOY})" --file "$${TMP_DEPLOY}" undeploy-private: diff --git a/frontend/deploy/aro-hcp-frontend.yml b/frontend/deploy/aro-hcp-frontend.yml index f43c8373d..73c718ac0 100644 --- a/frontend/deploy/aro-hcp-frontend.yml +++ b/frontend/deploy/aro-hcp-frontend.yml @@ -19,6 +19,8 @@ parameters: - name: DB_NAME description: Name of the Cosmos DB object in Azure value: "none" + - name: REGION + required: true objects: - apiVersion: v1 @@ -67,6 +69,8 @@ objects: value: ${DB_NAME} - name: DB_URL value: "https://${DB_NAME}.documents.azure.com:443/" + - name: REGION + value: ${REGION} ports: - containerPort: 8443 protocol: TCP diff --git a/frontend/frontend.go b/frontend/frontend.go index 5b1b45103..2a5ef6772 100644 --- a/frontend/frontend.go +++ b/frontend/frontend.go @@ -53,7 +53,7 @@ func MuxPattern(method string, segments ...string) string { return fmt.Sprintf("%s /%s", method, strings.ToLower(path.Join(segments...))) } -func NewFrontend(logger *slog.Logger, listener net.Listener, emitter metrics.Emitter) *Frontend { +func NewFrontend(logger *slog.Logger, listener net.Listener, emitter metrics.Emitter, region string) *Frontend { f := &Frontend{ logger: logger, listener: listener, @@ -69,7 +69,7 @@ func NewFrontend(logger *slog.Logger, listener net.Listener, emitter metrics.Emi done: make(chan struct{}), } - subscriptionStateMuxValidator := NewSubscriptionStateMuxValidator(&f.cache) + subscriptionStateMuxValidator := NewSubscriptionStateMuxValidator(&f.cache, emitter, region) // Setup metrics middleware metricsMiddleware := MetricsMiddleware{Emitter: emitter} diff --git a/frontend/main.go b/frontend/main.go index 1eea0c7f1..e3ccace4e 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -29,6 +29,13 @@ func main() { logger.Info(fmt.Sprintf("%s (%s) started", ProgramName, version)) + // Fetch the region from the env variable + region := os.Getenv("REGION") + if region == "" { + logger.Error("REGION env variable is not set.") + } + logger.Info(fmt.Sprintf("Application running in region: %s", region)) + ctx := context.Background() stop := make(chan struct{}) @@ -43,7 +50,7 @@ func main() { // Init prometheus emitter prometheusEmitter := NewPrometheusEmitter() - frontend := NewFrontend(logger, listener, prometheusEmitter) + frontend := NewFrontend(logger, listener, prometheusEmitter, region) // Verify the Async DB is available and accessible logger.Info("Testing DB Access") diff --git a/frontend/middleware_validatesubscription.go b/frontend/middleware_validatesubscription.go index 53875632d..be4b29072 100644 --- a/frontend/middleware_validatesubscription.go +++ b/frontend/middleware_validatesubscription.go @@ -7,6 +7,7 @@ import ( "net/http" "github.com/Azure/ARO-HCP/internal/api/arm" + "github.com/Azure/ARO-HCP/internal/metrics" ) const ( @@ -16,12 +17,16 @@ const ( ) type SubscriptionStateMuxValidator struct { - cache *Cache + cache *Cache + metrics metrics.Emitter + region string } -func NewSubscriptionStateMuxValidator(c *Cache) *SubscriptionStateMuxValidator { +func NewSubscriptionStateMuxValidator(c *Cache, emitter metrics.Emitter, region string) *SubscriptionStateMuxValidator { return &SubscriptionStateMuxValidator{ - cache: c, + cache: c, + metrics: emitter, + region: region, } } @@ -49,6 +54,13 @@ func (s *SubscriptionStateMuxValidator) MiddlewareValidateSubscriptionState(w ht return } + // Emit the subscription state metric + s.metrics.EmitGauge("subscription_lifecycle", 1, map[string]string{ + "region": s.region, + "subscriptionid": subscriptionId, + "state": string(sub.State), + }) + // the subscription exists, store its current state as context ctx := ContextWithSubscriptionState(r.Context(), sub.State) r = r.WithContext(ctx) diff --git a/frontend/middleware_validatesubscription_test.go b/frontend/middleware_validatesubscription_test.go index 882fc9a48..59a4fe89f 100644 --- a/frontend/middleware_validatesubscription_test.go +++ b/frontend/middleware_validatesubscription_test.go @@ -16,13 +16,29 @@ import ( "github.com/google/go-cmp/cmp" "github.com/Azure/ARO-HCP/internal/api/arm" + "github.com/Azure/ARO-HCP/internal/metrics" ) +type MockEmitter struct{} + +func (m *MockEmitter) EmitGauge(metric string, value float64, labels map[string]string) { + // No-op for testing +} + +func (m *MockEmitter) EmitCounter(metric string, value float64, labels map[string]string) { + // No-op for testing +} + +func NewMockEmitter() metrics.Emitter { + return &MockEmitter{} +} func TestMiddlewareValidateSubscription(t *testing.T) { subscriptionId := "1234-5678" + region := "eastus" defaultRequestPath := fmt.Sprintf("subscriptions/%s/resourceGroups/xyz", subscriptionId) cache := NewCache() - middleware := NewSubscriptionStateMuxValidator(cache) + mockEmitter := NewMockEmitter() + middleware := NewSubscriptionStateMuxValidator(cache, mockEmitter, region) tests := []struct { name string