Skip to content

Commit b2cf0a0

Browse files
committed
metrics labelling w/ finer granularity
1 parent 7ce13b4 commit b2cf0a0

File tree

7 files changed

+376
-12
lines changed

7 files changed

+376
-12
lines changed

pom.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
<common.codec.version>1.14</common.codec.version>
4949
<spring.boot.version>2.3.3.RELEASE</spring.boot.version>
5050
<spring.version>5.2.8.RELEASE</spring.version>
51+
<prometheus.client.version>0.9.0</prometheus.client.version>
5152

5253
<gpg.keyname>48540ECBBF00A28EACCF04E720FD12AFB0C9EBA9</gpg.keyname>
5354
<gpg.passphrase>${env.GPG_PASSPHRASE}</gpg.passphrase>
@@ -153,6 +154,18 @@
153154
<artifactId>spring-boot-autoconfigure</artifactId>
154155
<version>${spring.boot.version}</version>
155156
</dependency>
157+
<dependency>
158+
<groupId>io.prometheus</groupId>
159+
<artifactId>simpleclient</artifactId>
160+
<version>${prometheus.client.version}</version>
161+
<optional>true</optional>
162+
</dependency>
163+
<dependency>
164+
<groupId>io.prometheus</groupId>
165+
<artifactId>simpleclient_httpserver</artifactId>
166+
<version>${prometheus.client.version}</version>
167+
<optional>true</optional>
168+
</dependency>
156169

157170

158171
<!-- tests -->

util/pom.xml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,10 @@
1515
<dependency>
1616
<groupId>io.prometheus</groupId>
1717
<artifactId>simpleclient</artifactId>
18-
<version>0.9.0</version>
19-
<optional>true</optional>
2018
</dependency>
2119
<dependency>
2220
<groupId>io.prometheus</groupId>
2321
<artifactId>simpleclient_httpserver</artifactId>
24-
<version>0.9.0</version>
25-
<optional>true</optional>
2622
</dependency>
2723
<dependency>
2824
<groupId>io.kubernetes</groupId>
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.apimachinery;
14+
15+
import com.google.common.base.Strings;
16+
import java.util.regex.Pattern;
17+
import okhttp3.Request;
18+
19+
public class KubernetesRequestDigest {
20+
21+
public static final Pattern RESOURCE_URL_PATH_PATTERN =
22+
Pattern.compile("^/(api|apis)(/\\S+)?/v\\d\\w*/\\S+");
23+
24+
KubernetesRequestDigest(
25+
String urlPath,
26+
boolean isNonResourceRequest,
27+
KubernetesResource resourceMeta,
28+
KubernetesVerb verb) {
29+
this.urlPath = urlPath;
30+
this.isNonResourceRequest = isNonResourceRequest;
31+
this.resourceMeta = resourceMeta;
32+
this.verb = verb;
33+
}
34+
35+
public static KubernetesRequestDigest parse(Request request) {
36+
String urlPath = request.url().encodedPath();
37+
if (!isResourceRequest(urlPath)) {
38+
return nonResource(urlPath);
39+
}
40+
try {
41+
KubernetesResource resourceMeta;
42+
if (urlPath.startsWith("/api/v1")) {
43+
resourceMeta = KubernetesResource.parseCoreResource(urlPath);
44+
} else {
45+
resourceMeta = KubernetesResource.parseRegularResource(urlPath);
46+
}
47+
48+
return new KubernetesRequestDigest(
49+
urlPath,
50+
false,
51+
resourceMeta,
52+
KubernetesVerb.of(
53+
request.method(), hasNamePathParameter(resourceMeta), hasWatchParameter(request)));
54+
} catch (ParseKubernetesResourceException e) {
55+
return nonResource(urlPath);
56+
}
57+
}
58+
59+
private static KubernetesRequestDigest nonResource(String urlPath) {
60+
KubernetesRequestDigest digest = new KubernetesRequestDigest(urlPath, true, null, null);
61+
return digest;
62+
}
63+
64+
public static boolean isResourceRequest(String urlPath) {
65+
return RESOURCE_URL_PATH_PATTERN.matcher(urlPath).matches();
66+
}
67+
68+
private static boolean hasWatchParameter(Request request) {
69+
return !Strings.isNullOrEmpty(request.url().queryParameter("watch"));
70+
}
71+
72+
private static boolean hasNamePathParameter(KubernetesResource resource) {
73+
return !Strings.isNullOrEmpty(resource.getName());
74+
}
75+
76+
private final String urlPath;
77+
private final boolean isNonResourceRequest;
78+
79+
private final KubernetesResource resourceMeta;
80+
private final KubernetesVerb verb;
81+
82+
public String getUrlPath() {
83+
return urlPath;
84+
}
85+
86+
public boolean isNonResourceRequest() {
87+
return isNonResourceRequest;
88+
}
89+
90+
public KubernetesResource getResourceMeta() {
91+
return resourceMeta;
92+
}
93+
94+
public KubernetesVerb getVerb() {
95+
return verb;
96+
}
97+
98+
@Override
99+
public String toString() {
100+
if (isNonResourceRequest) {
101+
return new StringBuilder().append(verb).append(' ').append(urlPath).toString();
102+
}
103+
104+
String groupVersion;
105+
if (Strings.isNullOrEmpty(resourceMeta.getApiGroup())) { // core resource
106+
groupVersion = "";
107+
} else { // regular resource
108+
groupVersion = resourceMeta.getApiGroup() + "/" + resourceMeta.getApiVersion();
109+
}
110+
111+
String targetResourceName;
112+
if (Strings.isNullOrEmpty(resourceMeta.getSubResource())) {
113+
targetResourceName = resourceMeta.getResource();
114+
} else { // subresource
115+
targetResourceName = resourceMeta.getResource() + "/" + resourceMeta.getSubResource();
116+
}
117+
118+
return new StringBuilder()
119+
.append(verb.value())
120+
.append(' ')
121+
.append(groupVersion)
122+
.append(' ')
123+
.append(targetResourceName)
124+
.toString();
125+
}
126+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.apimachinery;
14+
15+
import java.util.regex.Matcher;
16+
import java.util.regex.Pattern;
17+
18+
public class KubernetesResource {
19+
20+
public static final Pattern CORE_RESOURCE_URL_PATH_PATTERN =
21+
Pattern.compile(
22+
"^/api/v1(/namespaces/(?<namespace>[\\w-]+))?/(?<resource>[\\w-]+)(/(?<name>[\\w-]+))?(/(?<subresource>[\\w-]+))?");
23+
24+
public static final Pattern REGULAR_RESOURCE_URL_PATH_PATTERN =
25+
Pattern.compile(
26+
"^/apis/(?<group>\\S+?)/(?<version>\\S+?)(/namespaces/(?<namespace>[\\w-]+))?/(?<resource>[\\w-]+)(/(?<name>[\\w-]+))?(/(?<subresource>[\\w-]+))?");
27+
28+
public static KubernetesResource parseCoreResource(String urlPath)
29+
throws ParseKubernetesResourceException {
30+
Matcher matcher = CORE_RESOURCE_URL_PATH_PATTERN.matcher(urlPath);
31+
if (!matcher.matches()) {
32+
throw new ParseKubernetesResourceException();
33+
}
34+
KubernetesResource resource =
35+
new KubernetesResource(
36+
"",
37+
"v1",
38+
matcher.group("resource"),
39+
matcher.group("subresource"),
40+
matcher.group("namespace"),
41+
matcher.group("name"));
42+
return resource;
43+
}
44+
45+
public static KubernetesResource parseRegularResource(String urlPath)
46+
throws ParseKubernetesResourceException {
47+
Matcher matcher = REGULAR_RESOURCE_URL_PATH_PATTERN.matcher(urlPath);
48+
if (!matcher.matches()) {
49+
throw new ParseKubernetesResourceException();
50+
}
51+
KubernetesResource resource =
52+
new KubernetesResource(
53+
matcher.group("group"),
54+
matcher.group("version"),
55+
matcher.group("resource"),
56+
matcher.group("subresource"),
57+
matcher.group("namespace"),
58+
matcher.group("name"));
59+
return resource;
60+
}
61+
62+
KubernetesResource(
63+
String apiGroup,
64+
String apiVersion,
65+
String resource,
66+
String subResource,
67+
String namespace,
68+
String name) {
69+
this.apiGroup = apiGroup;
70+
this.apiVersion = apiVersion;
71+
this.resource = resource;
72+
this.subResource = subResource;
73+
this.namespace = namespace;
74+
this.name = name;
75+
}
76+
77+
private final String apiGroup;
78+
private final String apiVersion;
79+
private final String resource;
80+
private final String subResource;
81+
82+
private final String namespace;
83+
private final String name;
84+
85+
public String getApiGroup() {
86+
return apiGroup;
87+
}
88+
89+
public String getApiVersion() {
90+
return apiVersion;
91+
}
92+
93+
public String getResource() {
94+
return resource;
95+
}
96+
97+
public String getSubResource() {
98+
return subResource;
99+
}
100+
101+
public String getNamespace() {
102+
return namespace;
103+
}
104+
105+
public String getName() {
106+
return name;
107+
}
108+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.apimachinery;
14+
15+
public enum KubernetesVerb {
16+
GET("get"),
17+
LIST("list"),
18+
CREATE("create"),
19+
UPDATE("update"),
20+
DELETE("delete"),
21+
PATCH("patch"),
22+
WATCH("watch"),
23+
DELETE_COLLECTION("deleteCollection");
24+
25+
private final String value;
26+
27+
KubernetesVerb(String value) {
28+
this.value = value;
29+
}
30+
31+
public static KubernetesVerb of(
32+
String httpVerb, boolean hasNamePathParam, boolean hasWatchParam) {
33+
if (hasWatchParam) {
34+
return WATCH;
35+
}
36+
switch (httpVerb) {
37+
case "GET":
38+
if (!hasNamePathParam) {
39+
return LIST;
40+
}
41+
return GET;
42+
case "POST":
43+
return CREATE;
44+
case "PUT":
45+
return UPDATE;
46+
case "PATCH":
47+
return PATCH;
48+
case "DELETE":
49+
if (!hasNamePathParam) {
50+
return DELETE_COLLECTION;
51+
}
52+
return DELETE;
53+
}
54+
throw new IllegalArgumentException("invalid HTTP verb for kubernetes client");
55+
}
56+
57+
public String value() {
58+
return value;
59+
}
60+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.apimachinery;
14+
15+
public class ParseKubernetesResourceException extends Exception {}

0 commit comments

Comments
 (0)