Change:
I've observed, that some metrics has and labels. (create) requests are "single resource", therefor they should be classified as . Def: customresourcehandler uses , which is based on the RequstInfo only: the RequestInfo is based only on the requests: method and url example: e.g: Since this RequestInfo has Name and Namespace empty, it is classified as . In a similar way it could be explained why: There's a mismatch in scope classification between built-in and custom resources: The key difference - the check if either "name" was provided OR it is a : Where the metrics.CleanScope ONLY checks, if name is not-empty. Tested: = v1.20 There were other scope related issues <1.20 and the distinction is not obvious. <details</details<details</details<details</details<details</details<details</details
/sig api-machinery
/triage accepted

Discussion:
// CleanScope returns the scope of the request. func CleanScope(requestInfo *request.RequestInfo) string { <del> if requestInfo.Name != "" { </del> <ins> if requestInfo.Name != "" || requestInfo.Verb == "create" { </ins> return "resource" } if requestInfo.Namespace != "" {

expectedScope: "resource", }, { <ins> name: "POST resource scope", requestInfo: &request.RequestInfo{ Verb:              "create", Namespace:         "my-namespace", IsResourceRequest: false, }, expectedScope: "resource", }, { </ins> name: "namespace scope", requestInfo: &request.RequestInfo{ Namespace:         "my-namespace",

}, }, requestInfo: &request.RequestInfo{ <ins> Verb:              "list", </ins> APIGroup:          "", APIVersion:        "v1", Resource:          "pods",

}, }, requestInfo: &request.RequestInfo{ <ins> Verb:              "create", </ins> APIGroup:          "", APIVersion:        "v1", Resource:          "pods",

apiserver_dropped_requests_total{request_kind="mutating"} 1 # HELP apiserver_request_total [STABLE] Counter of apiserver requests broken out for each verb, dry run value, group, version, resource, scope, component, and HTTP response code. # TYPE apiserver_request_total counter <del> apiserver_request_total{code="429",component="apiserver",dry_run="",group="",resource="pods",scope="cluster",subresource="",verb="POST",version="v1"} 1 </del> <ins> apiserver_request_total{code="429",component="apiserver",dry_run="",group="",resource="pods",scope="resource",subresource="",verb="POST",version="v1"} 1 </ins> `, }, {

request: &http.Request{ Method: "PATCH", URL: &url.URL{ <del> RawPath:  "/apis/batch/v1/namespaces/foo/pods/status", </del> <ins> RawPath:  "/apis/batch/v1/namespaces/foo/jobs/bar/status", </ins> RawQuery: "dryRun=All", }, }, requestInfo: &request.RequestInfo{ <ins> Verb:              "patch", </ins> APIGroup:          "batch", APIVersion:        "v1", Resource:          "jobs", <ins> Name:              "bar", </ins> Subresource:       "status", IsResourceRequest: true, },

apiserver_dropped_requests_total{request_kind="mutating"} 1 # HELP apiserver_request_total [STABLE] Counter of apiserver requests broken out for each verb, dry run value, group, version, resource, scope, component, and HTTP response code. # TYPE apiserver_request_total counter <del> apiserver_request_total{code="429",component="apiserver",dry_run="All",group="batch",resource="jobs",scope="cluster",subresource="status",verb="PATCH",version="v1"} 1 </del> <ins> apiserver_request_total{code="429",component="apiserver",dry_run="All",group="batch",resource="jobs",scope="resource",subresource="status",verb="PATCH",version="v1"} 1 </ins> `, }, }