From 7f39d8282cefb5214ed25bc435690a1fa9dfec43 Mon Sep 17 00:00:00 2001 From: Vibhu Pandey Date: Tue, 4 Jun 2024 18:25:24 +0530 Subject: [PATCH] (feature) multiple ingestion keys (#4762) * feat(keys): add support for multiple ingestion keys * ci(git): remove vendor/ from git * feat(gateway): create a proxy for sending requests to the gateway * fix(sqlite): remove keys schema * fix(api): replace with constant * fix(server): remove redundant options * fix(server): remove redundant options * test(gateway): add unit tests for gateway proxy * ci(docker): update gateway url * refactor(gateway): move gateway to api layer * fix(manager): fix declared error in manager * feat(testing): add a new testing docker-compose * fix(license): revert to nil license since select will never return a norows error * feat(gateway): add feature flags * chore(server): add a logger --------- Co-authored-by: Srikanth Chekuri --- .github/workflows/staging-deployment.yaml | 2 +- .github/workflows/testing-deployment.yaml | 2 +- .gitignore | 6 +- Makefile | 3 + .../docker-compose.testing.yaml | 307 ++++++++++++++++++ .../clickhouse-setup/docker-compose.yaml | 2 +- ee/query-service/app/api/api.go | 10 + ee/query-service/app/api/gateway.go | 34 ++ ee/query-service/app/server.go | 31 +- ee/query-service/dao/interface.go | 2 +- ee/query-service/integrations/gateway/noop.go | 9 + .../integrations/gateway/proxy.go | 66 ++++ .../integrations/gateway/proxy_test.go | 61 ++++ ee/query-service/license/db.go | 7 +- ee/query-service/license/manager.go | 18 +- ee/query-service/main.go | 3 + go.mod | 11 +- go.sum | 33 +- 18 files changed, 571 insertions(+), 36 deletions(-) create mode 100644 deploy/docker/clickhouse-setup/docker-compose.testing.yaml create mode 100644 ee/query-service/app/api/gateway.go create mode 100644 ee/query-service/integrations/gateway/noop.go create mode 100644 ee/query-service/integrations/gateway/proxy.go create mode 100644 ee/query-service/integrations/gateway/proxy_test.go diff --git a/.github/workflows/staging-deployment.yaml b/.github/workflows/staging-deployment.yaml index b5ec3febfe..455ecbce8c 100644 --- a/.github/workflows/staging-deployment.yaml +++ b/.github/workflows/staging-deployment.yaml @@ -49,6 +49,6 @@ jobs: git pull make build-ee-query-service-amd64 make build-frontend-amd64 - make run-signoz + make run-testing EOF gcloud compute ssh ${GCP_INSTANCE} --zone ${GCP_ZONE} --ssh-key-expire-after=15m --tunnel-through-iap --project ${GCP_PROJECT} --command "${COMMAND}" diff --git a/.github/workflows/testing-deployment.yaml b/.github/workflows/testing-deployment.yaml index 25784442ef..f51de56192 100644 --- a/.github/workflows/testing-deployment.yaml +++ b/.github/workflows/testing-deployment.yaml @@ -50,6 +50,6 @@ jobs: git checkout --track origin/${GITHUB_BRANCH} make build-ee-query-service-amd64 make build-frontend-amd64 - make run-signoz + make run-testing EOF gcloud compute ssh ${GCP_INSTANCE} --zone ${GCP_ZONE} --ssh-key-expire-after=15m --tunnel-through-iap --project ${GCP_PROJECT} --command "${COMMAND}" diff --git a/.gitignore b/.gitignore index 46915dccb8..8fe54dcf3d 100644 --- a/.gitignore +++ b/.gitignore @@ -62,4 +62,8 @@ e2e/test-results/ e2e/playwright-report/ e2e/blob-report/ e2e/playwright/.cache/ -e2e/.auth \ No newline at end of file +e2e/.auth + +# go +vendor/ +**/main/** diff --git a/Makefile b/Makefile index 5213c4597a..95cf7afdb9 100644 --- a/Makefile +++ b/Makefile @@ -156,6 +156,9 @@ pull-signoz: run-signoz: @docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.yaml up --build -d +run-testing: + @docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.testing.yaml up --build -d + down-signoz: @docker-compose -f $(STANDALONE_DIRECTORY)/docker-compose.yaml down -v diff --git a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml new file mode 100644 index 0000000000..efd61986b5 --- /dev/null +++ b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml @@ -0,0 +1,307 @@ +version: "2.4" + +x-clickhouse-defaults: &clickhouse-defaults + restart: on-failure + # addding non LTS version due to this fix https://github.com/ClickHouse/ClickHouse/commit/32caf8716352f45c1b617274c7508c86b7d1afab + image: clickhouse/clickhouse-server:24.1.2-alpine + tty: true + depends_on: + - zookeeper-1 + # - zookeeper-2 + # - zookeeper-3 + logging: + options: + max-size: 50m + max-file: "3" + healthcheck: + # "clickhouse", "client", "-u ${CLICKHOUSE_USER}", "--password ${CLICKHOUSE_PASSWORD}", "-q 'SELECT 1'" + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "0.0.0.0:8123/ping" + ] + interval: 30s + timeout: 5s + retries: 3 + ulimits: + nproc: 65535 + nofile: + soft: 262144 + hard: 262144 + +x-db-depend: &db-depend + depends_on: + clickhouse: + condition: service_healthy + otel-collector-migrator: + condition: service_completed_successfully + # clickhouse-2: + # condition: service_healthy + # clickhouse-3: + # condition: service_healthy + +services: + + zookeeper-1: + image: bitnami/zookeeper:3.7.1 + container_name: signoz-zookeeper-1 + hostname: zookeeper-1 + user: root + ports: + - "2181:2181" + - "2888:2888" + - "3888:3888" + volumes: + - ./data/zookeeper-1:/bitnami/zookeeper + environment: + - ZOO_SERVER_ID=1 + # - ZOO_SERVERS=0.0.0.0:2888:3888,zookeeper-2:2888:3888,zookeeper-3:2888:3888 + - ALLOW_ANONYMOUS_LOGIN=yes + - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-2: + # image: bitnami/zookeeper:3.7.0 + # container_name: signoz-zookeeper-2 + # hostname: zookeeper-2 + # user: root + # ports: + # - "2182:2181" + # - "2889:2888" + # - "3889:3888" + # volumes: + # - ./data/zookeeper-2:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=2 + # - ZOO_SERVERS=zookeeper-1:2888:3888,0.0.0.0:2888:3888,zookeeper-3:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + # zookeeper-3: + # image: bitnami/zookeeper:3.7.0 + # container_name: signoz-zookeeper-3 + # hostname: zookeeper-3 + # user: root + # ports: + # - "2183:2181" + # - "2890:2888" + # - "3890:3888" + # volumes: + # - ./data/zookeeper-3:/bitnami/zookeeper + # environment: + # - ZOO_SERVER_ID=3 + # - ZOO_SERVERS=zookeeper-1:2888:3888,zookeeper-2:2888:3888,0.0.0.0:2888:3888 + # - ALLOW_ANONYMOUS_LOGIN=yes + # - ZOO_AUTOPURGE_INTERVAL=1 + + clickhouse: + <<: *clickhouse-defaults + container_name: signoz-clickhouse + hostname: clickhouse + ports: + - "9000:9000" + - "8123:8123" + - "9181:9181" + volumes: + - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + - ./data/clickhouse/:/var/lib/clickhouse/ + - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + # clickhouse-2: + # <<: *clickhouse-defaults + # container_name: signoz-clickhouse-2 + # hostname: clickhouse-2 + # ports: + # - "9001:9000" + # - "8124:8123" + # - "9182:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-2/:/var/lib/clickhouse/ + # - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + + # clickhouse-3: + # <<: *clickhouse-defaults + # container_name: signoz-clickhouse-3 + # hostname: clickhouse-3 + # ports: + # - "9002:9000" + # - "8125:8123" + # - "9183:9181" + # volumes: + # - ./clickhouse-config.xml:/etc/clickhouse-server/config.xml + # - ./clickhouse-users.xml:/etc/clickhouse-server/users.xml + # - ./custom-function.xml:/etc/clickhouse-server/custom-function.xml + # - ./clickhouse-cluster.xml:/etc/clickhouse-server/config.d/cluster.xml + # # - ./clickhouse-storage.xml:/etc/clickhouse-server/config.d/storage.xml + # - ./data/clickhouse-3/:/var/lib/clickhouse/ + # - ./user_scripts:/var/lib/clickhouse/user_scripts/ + + alertmanager: + image: signoz/alertmanager:${ALERTMANAGER_TAG:-0.23.5} + container_name: signoz-alertmanager + volumes: + - ./data/alertmanager:/data + depends_on: + query-service: + condition: service_healthy + restart: on-failure + command: + - --queryService.url=http://query-service:8085 + - --storage.path=/data + + # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` + + query-service: + image: signoz/query-service:${DOCKER_TAG:-0.46.0} + container_name: signoz-query-service + command: + [ + "-config=/root/config/prometheus.yml", + "-gateway-url=https://api.staging.signoz.cloud" + # "--prefer-delta=true" + ] + # ports: + # - "6060:6060" # pprof port + # - "8080:8080" # query-service port + volumes: + - ./prometheus.yml:/root/config/prometheus.yml + - ../dashboards:/root/config/dashboards + - ./data/signoz/:/var/lib/signoz/ + environment: + - ClickHouseUrl=tcp://clickhouse:9000 + - ALERTMANAGER_API_PREFIX=http://alertmanager:9093/api/ + - SIGNOZ_LOCAL_DB_PATH=/var/lib/signoz/signoz.db + - DASHBOARDS_PATH=/root/config/dashboards + - STORAGE=clickhouse + - GODEBUG=netdns=go + - TELEMETRY_ENABLED=true + - DEPLOYMENT_TYPE=docker-standalone-amd + restart: on-failure + healthcheck: + test: + [ + "CMD", + "wget", + "--spider", + "-q", + "localhost:8080/api/v1/health" + ] + interval: 30s + timeout: 5s + retries: 3 + <<: *db-depend + + frontend: + image: signoz/frontend:${DOCKER_TAG:-0.46.0} + container_name: signoz-frontend + restart: on-failure + depends_on: + - alertmanager + - query-service + ports: + - "3301:3301" + volumes: + - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf + + otel-collector-migrator: + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.24} + container_name: otel-migrator + command: + - "--dsn=tcp://clickhouse:9000" + depends_on: + clickhouse: + condition: service_healthy + # clickhouse-2: + # condition: service_healthy + # clickhouse-3: + # condition: service_healthy + + + otel-collector: + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.24} + container_name: signoz-otel-collector + command: + [ + "--config=/etc/otel-collector-config.yaml", + "--manager-config=/etc/manager-config.yaml", + "--copy-path=/var/tmp/collector-config.yaml", + "--feature-gates=-pkg.translator.prometheus.NormalizeName" + ] + user: root # required for reading docker container logs + volumes: + - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml + - ./otel-collector-opamp-config.yaml:/etc/manager-config.yaml + - /var/lib/docker/containers:/var/lib/docker/containers:ro + environment: + - OTEL_RESOURCE_ATTRIBUTES=host.name=signoz-host,os.type=linux + - DOCKER_MULTI_NODE_CLUSTER=false + - LOW_CARDINAL_EXCEPTION_GROUPING=false + ports: + # - "1777:1777" # pprof extension + - "4317:4317" # OTLP gRPC receiver + - "4318:4318" # OTLP HTTP receiver + # - "8888:8888" # OtelCollector internal metrics + # - "8889:8889" # signoz spanmetrics exposed by the agent + # - "9411:9411" # Zipkin port + # - "13133:13133" # health check extension + # - "14250:14250" # Jaeger gRPC + # - "14268:14268" # Jaeger thrift HTTP + # - "55678:55678" # OpenCensus receiver + # - "55679:55679" # zPages extension + restart: on-failure + depends_on: + clickhouse: + condition: service_healthy + otel-collector-migrator: + condition: service_completed_successfully + query-service: + condition: service_healthy + + logspout: + image: "gliderlabs/logspout:v3.2.14" + container_name: signoz-logspout + volumes: + - /etc/hostname:/etc/host_hostname:ro + - /var/run/docker.sock:/var/run/docker.sock + command: syslog+tcp://otel-collector:2255 + depends_on: + - otel-collector + restart: on-failure + + hotrod: + image: jaegertracing/example-hotrod:1.30 + container_name: hotrod + logging: + options: + max-size: 50m + max-file: "3" + command: [ "all" ] + environment: + - JAEGER_ENDPOINT=http://otel-collector:14268/api/traces + + load-hotrod: + image: "signoz/locust:1.2.3" + container_name: load-hotrod + hostname: load-hotrod + environment: + ATTACKED_HOST: http://hotrod:8080 + LOCUST_MODE: standalone + NO_PROXY: standalone + TASK_DELAY_FROM: 5 + TASK_DELAY_TO: 30 + QUIET_MODE: "${QUIET_MODE:-false}" + LOCUST_OPTS: "--headless -u 10 -r 1" + volumes: + - ../common/locust-scripts:/locust diff --git a/deploy/docker/clickhouse-setup/docker-compose.yaml b/deploy/docker/clickhouse-setup/docker-compose.yaml index 12b91b6992..5e2096e38a 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.yaml @@ -168,7 +168,7 @@ services: container_name: signoz-query-service command: [ - "-config=/root/config/prometheus.yml", + "-config=/root/config/prometheus.yml" # "--prefer-delta=true" ] # ports: diff --git a/ee/query-service/app/api/api.go b/ee/query-service/app/api/api.go index 418cd00cf9..be0cf1ec36 100644 --- a/ee/query-service/app/api/api.go +++ b/ee/query-service/app/api/api.go @@ -2,10 +2,12 @@ package api import ( "net/http" + "net/http/httputil" "time" "github.com/gorilla/mux" "go.signoz.io/signoz/ee/query-service/dao" + "go.signoz.io/signoz/ee/query-service/integrations/gateway" "go.signoz.io/signoz/ee/query-service/interfaces" "go.signoz.io/signoz/ee/query-service/license" "go.signoz.io/signoz/ee/query-service/usage" @@ -35,6 +37,7 @@ type APIHandlerOptions struct { IntegrationsController *integrations.Controller LogsParsingPipelineController *logparsingpipeline.LogParsingPipelineController Cache cache.Cache + Gateway *httputil.ReverseProxy // Querier Influx Interval FluxInterval time.Duration } @@ -95,6 +98,10 @@ func (ah *APIHandler) AppDao() dao.ModelDao { return ah.opts.AppDao } +func (ah *APIHandler) Gateway() *httputil.ReverseProxy { + return ah.opts.Gateway +} + func (ah *APIHandler) CheckFeature(f string) bool { err := ah.FF().CheckFeature(f) return err == nil @@ -170,6 +177,9 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router, am *baseapp.AuthMiddlew am.ViewAccess(ah.listLicensesV2)). Methods(http.MethodGet) + // Gateway + router.PathPrefix(gateway.RoutePrefix).HandlerFunc(am.AdminAccess(ah.ServeGatewayHTTP)) + ah.APIHandler.RegisterRoutes(router, am) } diff --git a/ee/query-service/app/api/gateway.go b/ee/query-service/app/api/gateway.go new file mode 100644 index 0000000000..15d274ee23 --- /dev/null +++ b/ee/query-service/app/api/gateway.go @@ -0,0 +1,34 @@ +package api + +import ( + "net/http" + "strings" + + "go.signoz.io/signoz/ee/query-service/integrations/gateway" +) + +func (ah *APIHandler) ServeGatewayHTTP(rw http.ResponseWriter, req *http.Request) { + ctx := req.Context() + if !strings.HasPrefix(req.URL.Path, gateway.RoutePrefix+gateway.AllowedPrefix) { + rw.WriteHeader(http.StatusNotFound) + return + } + + license, err := ah.LM().GetRepo().GetActiveLicense(ctx) + if err != nil { + RespondError(rw, err, nil) + return + } + + //Create headers + var licenseKey string + if license != nil { + licenseKey = license.Key + } + + req.Header.Set("X-Signoz-Cloud-Api-Key", licenseKey) + req.Header.Set("X-Consumer-Username", "lid:00000000-0000-0000-0000-000000000000") + req.Header.Set("X-Consumer-Groups", "ns:default") + + ah.Gateway().ServeHTTP(rw, req) +} diff --git a/ee/query-service/app/server.go b/ee/query-service/app/server.go index 8dd3ed56e0..2e1df484d1 100644 --- a/ee/query-service/app/server.go +++ b/ee/query-service/app/server.go @@ -8,6 +8,7 @@ import ( "io" "net" "net/http" + "net/http/httputil" _ "net/http/pprof" // http profiler "os" "regexp" @@ -24,6 +25,7 @@ import ( "go.signoz.io/signoz/ee/query-service/auth" "go.signoz.io/signoz/ee/query-service/constants" "go.signoz.io/signoz/ee/query-service/dao" + "go.signoz.io/signoz/ee/query-service/integrations/gateway" "go.signoz.io/signoz/ee/query-service/interfaces" baseauth "go.signoz.io/signoz/pkg/query-service/auth" baseInterface "go.signoz.io/signoz/pkg/query-service/interfaces" @@ -71,6 +73,7 @@ type ServerOptions struct { CacheConfigPath string FluxInterval string Cluster string + GatewayUrl string } // Server runs HTTP api service @@ -122,8 +125,33 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) { localDB.SetMaxOpenConns(10) + gatewayFeature := basemodel.Feature{ + Name: "GATEWAY", + Active: false, + Usage: 0, + UsageLimit: -1, + Route: "", + } + + //Activate this feature if the url is not empty + var gatewayProxy *httputil.ReverseProxy + if serverOptions.GatewayUrl == "" { + gatewayFeature.Active = false + gatewayProxy, err = gateway.NewNoopProxy() + if err != nil { + return nil, err + } + } else { + zap.L().Info("Enabling gateway feature flag ...") + gatewayFeature.Active = true + gatewayProxy, err = gateway.NewProxy(serverOptions.GatewayUrl, gateway.RoutePrefix) + if err != nil { + return nil, err + } + } + // initiate license manager - lm, err := licensepkg.StartManager("sqlite", localDB) + lm, err := licensepkg.StartManager("sqlite", localDB, gatewayFeature) if err != nil { return nil, err } @@ -248,6 +276,7 @@ func NewServer(serverOptions *ServerOptions) (*Server, error) { LogsParsingPipelineController: logParsingPipelineController, Cache: c, FluxInterval: fluxInterval, + Gateway: gatewayProxy, } apiHandler, err := api.NewAPIHandler(apiOpts) diff --git a/ee/query-service/dao/interface.go b/ee/query-service/dao/interface.go index 695ff860a0..2fc81468d5 100644 --- a/ee/query-service/dao/interface.go +++ b/ee/query-service/dao/interface.go @@ -34,7 +34,7 @@ type ModelDao interface { GetDomainByEmail(ctx context.Context, email string) (*model.OrgDomain, basemodel.BaseApiError) CreatePAT(ctx context.Context, p model.PAT) (model.PAT, basemodel.BaseApiError) - UpdatePAT(ctx context.Context, p model.PAT, id string) (basemodel.BaseApiError) + UpdatePAT(ctx context.Context, p model.PAT, id string) basemodel.BaseApiError GetPAT(ctx context.Context, pat string) (*model.PAT, basemodel.BaseApiError) UpdatePATLastUsed(ctx context.Context, pat string, lastUsed int64) basemodel.BaseApiError GetPATByID(ctx context.Context, id string) (*model.PAT, basemodel.BaseApiError) diff --git a/ee/query-service/integrations/gateway/noop.go b/ee/query-service/integrations/gateway/noop.go new file mode 100644 index 0000000000..bbe930e2f9 --- /dev/null +++ b/ee/query-service/integrations/gateway/noop.go @@ -0,0 +1,9 @@ +package gateway + +import ( + "net/http/httputil" +) + +func NewNoopProxy() (*httputil.ReverseProxy, error) { + return nil, nil +} diff --git a/ee/query-service/integrations/gateway/proxy.go b/ee/query-service/integrations/gateway/proxy.go new file mode 100644 index 0000000000..8b225c4459 --- /dev/null +++ b/ee/query-service/integrations/gateway/proxy.go @@ -0,0 +1,66 @@ +package gateway + +import ( + "net/http" + "net/http/httputil" + "net/url" + "path" + "strings" +) + +const ( + RoutePrefix string = "/api/gateway" + AllowedPrefix string = "/v1/workspaces/me" +) + +type proxy struct { + url *url.URL + stripPath string +} + +func NewProxy(u string, stripPath string) (*httputil.ReverseProxy, error) { + url, err := url.Parse(u) + if err != nil { + return nil, err + } + + proxy := &proxy{url: url, stripPath: stripPath} + + return &httputil.ReverseProxy{ + Rewrite: proxy.rewrite, + ModifyResponse: proxy.modifyResponse, + ErrorHandler: proxy.errorHandler, + }, nil +} + +func (p *proxy) rewrite(pr *httputil.ProxyRequest) { + pr.SetURL(p.url) + pr.SetXForwarded() + pr.Out.URL.Path = cleanPath(strings.ReplaceAll(pr.Out.URL.Path, p.stripPath, "")) +} + +func (p *proxy) modifyResponse(res *http.Response) error { + return nil +} + +func (p *proxy) errorHandler(rw http.ResponseWriter, req *http.Request, err error) { + rw.WriteHeader(http.StatusBadGateway) +} + +func cleanPath(p string) string { + if p == "" { + return "/" + } + if p[0] != '/' { + p = "/" + p + } + np := path.Clean(p) + if p[len(p)-1] == '/' && np != "/" { + if len(p) == len(np)+1 && strings.HasPrefix(p, np) { + np = p + } else { + np += "/" + } + } + return np +} diff --git a/ee/query-service/integrations/gateway/proxy_test.go b/ee/query-service/integrations/gateway/proxy_test.go new file mode 100644 index 0000000000..45f5211efe --- /dev/null +++ b/ee/query-service/integrations/gateway/proxy_test.go @@ -0,0 +1,61 @@ +package gateway + +import ( + "context" + "net/http" + "net/http/httputil" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestProxyRewrite(t *testing.T) { + testCases := []struct { + name string + url *url.URL + stripPath string + in *url.URL + expected *url.URL + }{ + { + name: "SamePathAdded", + url: &url.URL{Scheme: "http", Host: "backend", Path: "/path1"}, + stripPath: "/strip", + in: &url.URL{Scheme: "http", Host: "localhost", Path: "/strip/path1"}, + expected: &url.URL{Scheme: "http", Host: "backend", Path: "/path1/path1"}, + }, + { + name: "NoStripPathInput", + url: &url.URL{Scheme: "http", Host: "backend"}, + stripPath: "", + in: &url.URL{Scheme: "http", Host: "localhost", Path: "/strip/path1"}, + expected: &url.URL{Scheme: "http", Host: "backend", Path: "/strip/path1"}, + }, + { + name: "NoStripPathPresentInReq", + url: &url.URL{Scheme: "http", Host: "backend"}, + stripPath: "/not-found", + in: &url.URL{Scheme: "http", Host: "localhost", Path: "/strip/path1"}, + expected: &url.URL{Scheme: "http", Host: "backend", Path: "/strip/path1"}, + }, + } + + for _, tc := range testCases { + proxy, err := NewProxy(tc.url.String(), tc.stripPath) + require.NoError(t, err) + inReq, err := http.NewRequest(http.MethodGet, tc.in.String(), nil) + require.NoError(t, err) + proxyReq := &httputil.ProxyRequest{ + In: inReq, + Out: inReq.Clone(context.Background()), + } + proxy.Rewrite(proxyReq) + + assert.Equal(t, tc.expected.Host, proxyReq.Out.URL.Host) + assert.Equal(t, tc.expected.Scheme, proxyReq.Out.URL.Scheme) + assert.Equal(t, tc.expected.Path, proxyReq.Out.URL.Path) + assert.Equal(t, tc.expected.Query(), proxyReq.Out.URL.Query()) + } +} diff --git a/ee/query-service/license/db.go b/ee/query-service/license/db.go index bf71e9376d..d6065d045b 100644 --- a/ee/query-service/license/db.go +++ b/ee/query-service/license/db.go @@ -48,8 +48,9 @@ func (r *Repo) GetLicenses(ctx context.Context) ([]model.License, error) { return licenses, nil } -// GetActiveLicense fetches the latest active license from DB -func (r *Repo) GetActiveLicense(ctx context.Context) (*model.License, error) { +// GetActiveLicense fetches the latest active license from DB. +// If the license is not present, expect a nil license and a nil error in the output. +func (r *Repo) GetActiveLicense(ctx context.Context) (*model.License, *basemodel.ApiError) { var err error licenses := []model.License{} @@ -57,7 +58,7 @@ func (r *Repo) GetActiveLicense(ctx context.Context) (*model.License, error) { err = r.db.Select(&licenses, query) if err != nil { - return nil, fmt.Errorf("failed to get active licenses from db: %v", err) + return nil, basemodel.InternalError(fmt.Errorf("failed to get active licenses from db: %v", err)) } var active *model.License diff --git a/ee/query-service/license/manager.go b/ee/query-service/license/manager.go index d348b6d216..74887608ab 100644 --- a/ee/query-service/license/manager.go +++ b/ee/query-service/license/manager.go @@ -49,8 +49,7 @@ type Manager struct { activeFeatures basemodel.FeatureSet } -func StartManager(dbType string, db *sqlx.DB) (*Manager, error) { - +func StartManager(dbType string, db *sqlx.DB, features ...basemodel.Feature) (*Manager, error) { if LM != nil { return LM, nil } @@ -66,7 +65,7 @@ func StartManager(dbType string, db *sqlx.DB) (*Manager, error) { repo: &repo, } - if err := m.start(); err != nil { + if err := m.start(features...); err != nil { return m, err } LM = m @@ -74,8 +73,8 @@ func StartManager(dbType string, db *sqlx.DB) (*Manager, error) { } // start loads active license in memory and initiates validator -func (lm *Manager) start() error { - err := lm.LoadActiveLicense() +func (lm *Manager) start(features ...basemodel.Feature) error { + err := lm.LoadActiveLicense(features...) return err } @@ -85,7 +84,7 @@ func (lm *Manager) Stop() { <-lm.terminated } -func (lm *Manager) SetActive(l *model.License) { +func (lm *Manager) SetActive(l *model.License, features ...basemodel.Feature) { lm.mutex.Lock() defer lm.mutex.Unlock() @@ -94,7 +93,7 @@ func (lm *Manager) SetActive(l *model.License) { } lm.activeLicense = l - lm.activeFeatures = l.FeatureSet + lm.activeFeatures = append(l.FeatureSet, features...) // set default features setDefaultFeatures(lm) @@ -116,14 +115,13 @@ func setDefaultFeatures(lm *Manager) { } // LoadActiveLicense loads the most recent active license -func (lm *Manager) LoadActiveLicense() error { - var err error +func (lm *Manager) LoadActiveLicense(features ...basemodel.Feature) error { active, err := lm.repo.GetActiveLicense(context.Background()) if err != nil { return err } if active != nil { - lm.SetActive(active) + lm.SetActive(active, features...) } else { zap.L().Info("No active license found, defaulting to basic plan") // if no active license is found, we default to basic(free) plan with all default features diff --git a/ee/query-service/main.go b/ee/query-service/main.go index 4fad91008f..f88f2cb498 100644 --- a/ee/query-service/main.go +++ b/ee/query-service/main.go @@ -95,6 +95,7 @@ func main() { var maxIdleConns int var maxOpenConns int var dialTimeout time.Duration + var gatewayUrl string flag.StringVar(&promConfigPath, "config", "./config/prometheus.yml", "(prometheus config to read metrics)") flag.StringVar(&skipTopLvlOpsPath, "skip-top-level-ops", "", "(config file to skip top level operations)") @@ -109,6 +110,7 @@ func main() { flag.StringVar(&fluxInterval, "flux-interval", "5m", "(cache config to use)") flag.BoolVar(&enableQueryServiceLogOTLPExport, "enable.query.service.log.otlp.export", false, "(enable query service log otlp export)") flag.StringVar(&cluster, "cluster", "cluster", "(cluster name - defaults to 'cluster')") + flag.StringVar(&gatewayUrl, "gateway-url", "", "(url to the gateway)") flag.Parse() @@ -134,6 +136,7 @@ func main() { CacheConfigPath: cacheConfigPath, FluxInterval: fluxInterval, Cluster: cluster, + GatewayUrl: gatewayUrl, } // Read the jwt secret key diff --git a/go.mod b/go.mod index 51d72be2f8..d6c4eb4b36 100644 --- a/go.mod +++ b/go.mod @@ -77,6 +77,14 @@ require ( k8s.io/apimachinery v0.28.2 ) +require ( + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + golang.org/x/tools v0.16.1 // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) + require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0 // indirect @@ -139,6 +147,7 @@ require ( github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/oklog/run v1.1.0 // indirect github.com/oklog/ulid v1.3.1 // indirect + github.com/onsi/gomega v1.19.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.88.0 // indirect github.com/paulmach/orb v0.11.1 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect @@ -198,7 +207,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect - k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/klog/v2 v2.110.1 // indirect k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect ) diff --git a/go.sum b/go.sum index e0a7e14e85..c413c7545b 100644 --- a/go.sum +++ b/go.sum @@ -204,8 +204,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= -github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= -github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -260,8 +260,8 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -636,8 +636,9 @@ github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/open-telemetry/opamp-go v0.5.0 h1:2YFbb6G4qBkq3yTRdVb5Nfz9hKHW/ldUyex352e1J7g= github.com/open-telemetry/opamp-go v0.5.0/go.mod h1:IMdeuHGVc5CjKSu5/oNV0o+UmiXuahoHvoZ4GOmAI9M= github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage v0.88.0 h1:9gjzrpUlzGC5BebgO1cxb/9KQ9yuIIE6B+6wLySKVCQ= @@ -984,8 +985,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1260,8 +1261,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1504,10 +1505,10 @@ k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ= k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU= k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY= k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= -k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= k8s.io/utils v0.0.0-20230711102312-30195339c3c7 h1:ZgnF1KZsYxWIifwSNZFZgNtWE89WI5yiP5WwlfDoIyc= k8s.io/utils v0.0.0-20230711102312-30195339c3c7/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= @@ -1515,8 +1516,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= -sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=