diff options
Diffstat (limited to 'workhorse/internal/upstream')
-rw-r--r-- | workhorse/internal/upstream/.gitignore | 1 | ||||
-rw-r--r-- | workhorse/internal/upstream/metrics.go | 103 | ||||
-rw-r--r-- | workhorse/internal/upstream/routes.go | 27 | ||||
-rw-r--r-- | workhorse/internal/upstream/routes_test.go | 30 |
4 files changed, 55 insertions, 106 deletions
diff --git a/workhorse/internal/upstream/.gitignore b/workhorse/internal/upstream/.gitignore new file mode 100644 index 00000000000..d63cd8b2c40 --- /dev/null +++ b/workhorse/internal/upstream/.gitignore @@ -0,0 +1 @@ +testdata/public diff --git a/workhorse/internal/upstream/metrics.go b/workhorse/internal/upstream/metrics.go index 1a11bdc8b53..27cc6bb045b 100644 --- a/workhorse/internal/upstream/metrics.go +++ b/workhorse/internal/upstream/metrics.go @@ -6,6 +6,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" + + "gitlab.com/gitlab-org/labkit/metrics" ) const ( @@ -13,95 +15,7 @@ const ( httpSubsystem = "http" ) -func secondsDurationBuckets() []float64 { - return []float64{ - 0.005, /* 5ms */ - 0.025, /* 25ms */ - 0.1, /* 100ms */ - 0.5, /* 500ms */ - 1.0, /* 1s */ - 10.0, /* 10s */ - 30.0, /* 30s */ - 60.0, /* 1m */ - 300.0, /* 10m */ - } -} - -func byteSizeBuckets() []float64 { - return []float64{ - 10, - 64, - 256, - 1024, /* 1kB */ - 64 * 1024, /* 64kB */ - 256 * 1024, /* 256kB */ - 1024 * 1024, /* 1mB */ - 64 * 1024 * 1024, /* 64mB */ - } -} - var ( - httpInFlightRequests = promauto.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Subsystem: httpSubsystem, - Name: "in_flight_requests", - Help: "A gauge of requests currently being served by workhorse.", - }) - - httpRequestsTotal = promauto.NewCounterVec( - prometheus.CounterOpts{ - Namespace: namespace, - Subsystem: httpSubsystem, - Name: "requests_total", - Help: "A counter for requests to workhorse.", - }, - []string{"code", "method", "route"}, - ) - - httpRequestDurationSeconds = promauto.NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: namespace, - Subsystem: httpSubsystem, - Name: "request_duration_seconds", - Help: "A histogram of latencies for requests to workhorse.", - Buckets: secondsDurationBuckets(), - }, - []string{"code", "method", "route"}, - ) - - httpRequestSizeBytes = promauto.NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: namespace, - Subsystem: httpSubsystem, - Name: "request_size_bytes", - Help: "A histogram of sizes of requests to workhorse.", - Buckets: byteSizeBuckets(), - }, - []string{"code", "method", "route"}, - ) - - httpResponseSizeBytes = promauto.NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: namespace, - Subsystem: httpSubsystem, - Name: "response_size_bytes", - Help: "A histogram of response sizes for requests to workhorse.", - Buckets: byteSizeBuckets(), - }, - []string{"code", "method", "route"}, - ) - - httpTimeToWriteHeaderSeconds = promauto.NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: namespace, - Subsystem: httpSubsystem, - Name: "time_to_write_header_seconds", - Help: "A histogram of request durations until the response headers are written.", - Buckets: secondsDurationBuckets(), - }, - []string{"code", "method", "route"}, - ) - httpGeoProxiedRequestsTotal = promauto.NewCounterVec( prometheus.CounterOpts{ Namespace: namespace, @@ -111,19 +25,12 @@ var ( }, []string{"code", "method", "route"}, ) + + buildHandler = metrics.NewHandlerFactory(metrics.WithNamespace(namespace), metrics.WithLabels("route")) ) func instrumentRoute(next http.Handler, method string, regexpStr string) http.Handler { - handler := next - - handler = promhttp.InstrumentHandlerCounter(httpRequestsTotal.MustCurryWith(map[string]string{"route": regexpStr}), handler) - handler = promhttp.InstrumentHandlerDuration(httpRequestDurationSeconds.MustCurryWith(map[string]string{"route": regexpStr}), handler) - handler = promhttp.InstrumentHandlerInFlight(httpInFlightRequests, handler) - handler = promhttp.InstrumentHandlerRequestSize(httpRequestSizeBytes.MustCurryWith(map[string]string{"route": regexpStr}), handler) - handler = promhttp.InstrumentHandlerResponseSize(httpResponseSizeBytes.MustCurryWith(map[string]string{"route": regexpStr}), handler) - handler = promhttp.InstrumentHandlerTimeToWriteHeader(httpTimeToWriteHeaderSeconds.MustCurryWith(map[string]string{"route": regexpStr}), handler) - - return handler + return buildHandler(next, metrics.WithLabelValues(map[string]string{"route": regexpStr})) } func instrumentGeoProxyRoute(next http.Handler, method string, regexpStr string) http.Handler { diff --git a/workhorse/internal/upstream/routes.go b/workhorse/internal/upstream/routes.go index b8089865ffe..b1d76dfc1bd 100644 --- a/workhorse/internal/upstream/routes.go +++ b/workhorse/internal/upstream/routes.go @@ -20,7 +20,6 @@ import ( "gitlab.com/gitlab-org/gitlab/workhorse/internal/git" "gitlab.com/gitlab-org/gitlab/workhorse/internal/helper" "gitlab.com/gitlab-org/gitlab/workhorse/internal/imageresizer" - "gitlab.com/gitlab-org/gitlab/workhorse/internal/lfs" proxypkg "gitlab.com/gitlab-org/gitlab/workhorse/internal/proxy" "gitlab.com/gitlab-org/gitlab/workhorse/internal/queueing" "gitlab.com/gitlab-org/gitlab/workhorse/internal/redis" @@ -251,8 +250,8 @@ func configureRoutes(u *upstream) { u.route("PUT", gitProjectPattern+`gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`, upload.RequestBody(api, signingProxy, preparers.lfs), withMatcher(isContentType("application/octet-stream"))), // CI Artifacts - u.route("POST", apiPattern+`v4/jobs/[0-9]+/artifacts\z`, contentEncodingHandler(artifacts.UploadArtifacts(api, signingProxy, preparers.artifacts))), - u.route("POST", ciAPIPattern+`v1/builds/[0-9]+/artifacts\z`, contentEncodingHandler(artifacts.UploadArtifacts(api, signingProxy, preparers.artifacts))), + u.route("POST", apiPattern+`v4/jobs/[0-9]+/artifacts\z`, contentEncodingHandler(upload.Artifacts(api, signingProxy, preparers.artifacts))), + u.route("POST", ciAPIPattern+`v1/builds/[0-9]+/artifacts\z`, contentEncodingHandler(upload.Artifacts(api, signingProxy, preparers.artifacts))), // ActionCable websocket u.wsRoute(`^/-/cable\z`, cableProxy), @@ -318,9 +317,12 @@ func configureRoutes(u *upstream) { // Group Import via UI upload acceleration u.route("POST", importPattern+`gitlab_group`, upload.Multipart(api, signingProxy, preparers.uploads)), - // Metric image upload + // Issuable Metric image upload u.route("POST", apiProjectPattern+`issues/[0-9]+/metric_images\z`, upload.Multipart(api, signingProxy, preparers.uploads)), + // Alert Metric image upload + u.route("POST", apiProjectPattern+`alert_management_alerts/[0-9]+/metric_images\z`, upload.Multipart(api, signingProxy, preparers.uploads)), + // Requirements Import via UI upload acceleration u.route("POST", projectPattern+`requirements_management/requirements/import_csv`, upload.Multipart(api, signingProxy, preparers.uploads)), @@ -383,11 +385,10 @@ func configureRoutes(u *upstream) { u.route("", "^/oauth/geo/(auth|callback|logout)$", defaultUpstream), // Admin Area > Geo routes - u.route("", "^/admin/geo$", defaultUpstream), - u.route("", "^/admin/geo/", defaultUpstream), + u.route("", "^/admin/geo/replication/projects", defaultUpstream), + u.route("", "^/admin/geo/replication/designs", defaultUpstream), // Geo API routes - u.route("", "^/api/v4/geo_nodes", defaultUpstream), u.route("", "^/api/v4/geo_replication", defaultUpstream), u.route("", "^/api/v4/geo/proxy_git_ssh", defaultUpstream), u.route("", "^/api/v4/geo/graphql", defaultUpstream), @@ -395,6 +396,16 @@ func configureRoutes(u *upstream) { // Internal API routes u.route("", "^/api/v4/internal", defaultUpstream), + u.route( + "", `^/assets/`, + static.ServeExisting( + u.URLPrefix, + staticpages.CacheExpireMax, + assetsNotFoundHandler, + ), + withoutTracing(), // Tracing on assets is very noisy + ), + // Don't define a catch-all route. If a route does not match, then we know // the request should be proxied. } @@ -405,7 +416,7 @@ func createUploadPreparers(cfg config.Config) uploadPreparers { return uploadPreparers{ artifacts: defaultPreparer, - lfs: lfs.NewLfsUploadPreparer(cfg, defaultPreparer), + lfs: upload.NewLfsPreparer(cfg, defaultPreparer), packages: defaultPreparer, uploads: defaultPreparer, } diff --git a/workhorse/internal/upstream/routes_test.go b/workhorse/internal/upstream/routes_test.go index f196433f5b4..8a032519bdf 100644 --- a/workhorse/internal/upstream/routes_test.go +++ b/workhorse/internal/upstream/routes_test.go @@ -2,8 +2,26 @@ package upstream import ( "testing" + + "gitlab.com/gitlab-org/gitlab/workhorse/internal/testhelper" ) +func TestAdminGeoPathsWithGeoProxy(t *testing.T) { + testCases := []testCase{ + {"Regular admin/geo", "/admin/geo", "Geo primary received request to path /admin/geo"}, + {"Specific object replication", "/admin/geo/replication/object_type", "Geo primary received request to path /admin/geo/replication/object_type"}, + {"Specific object replication per-site", "/admin/geo/sites/2/replication/object_type", "Geo primary received request to path /admin/geo/sites/2/replication/object_type"}, + {"Projects replication per-site", "/admin/geo/sites/2/replication/projects", "Geo primary received request to path /admin/geo/sites/2/replication/projects"}, + {"Designs replication per-site", "/admin/geo/sites/2/replication/designs", "Geo primary received request to path /admin/geo/sites/2/replication/designs"}, + {"Projects replication", "/admin/geo/replication/projects", "Local Rails server received request to path /admin/geo/replication/projects"}, + {"Projects replication subpaths", "/admin/geo/replication/projects/2", "Local Rails server received request to path /admin/geo/replication/projects/2"}, + {"Designs replication", "/admin/geo/replication/designs", "Local Rails server received request to path /admin/geo/replication/designs"}, + {"Designs replication subpaths", "/admin/geo/replication/designs/3", "Local Rails server received request to path /admin/geo/replication/designs/3"}, + } + + runTestCasesWithGeoProxyEnabled(t, testCases) +} + func TestProjectNotExistingGitHttpPullWithGeoProxy(t *testing.T) { testCases := []testCase{ {"secondary info/refs", "/group/project.git/info/refs", "Local Rails server received request to path /group/project.git/info/refs"}, @@ -45,3 +63,15 @@ func TestProjectNotExistingGitSSHPushWithGeoProxy(t *testing.T) { runTestCasesWithGeoProxyEnabled(t, testCases) } + +func TestAssetsServedLocallyWithGeoProxy(t *testing.T) { + path := "/assets/static.txt" + content := "local geo asset" + testhelper.SetupStaticFileHelper(t, path, content, testDocumentRoot) + + testCases := []testCase{ + {"assets path", "/assets/static.txt", "local geo asset"}, + } + + runTestCasesWithGeoProxyEnabled(t, testCases) +} |