Skip to content

Commit 05e47e6

Browse files
authored
Merge pull request #5542 from gchq/7.12-add-sysinfo-servlet
Add admin servlet menu and sysinfo servlet
2 parents 4b24f6e + 4fb849c commit 05e47e6

18 files changed

Lines changed: 705 additions & 36 deletions

File tree

stroom-app/src/main/java/stroom/app/guice/AppModule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import stroom.dropwizard.common.LogLevelInspector;
2525
import stroom.dropwizard.common.prometheus.AppInfoProvider;
2626
import stroom.dropwizard.common.prometheus.PrometheusModule;
27+
import stroom.dropwizard.common.sysinfo.SystemInfoAdminServletModule;
2728
import stroom.lifecycle.api.LifecycleBinder;
2829
import stroom.lifecycle.impl.LifecycleServiceModule;
2930
import stroom.meta.statistics.impl.MetaStatisticsModule;
@@ -54,6 +55,7 @@ protected void configure() {
5455
install(new ResourceModule());
5556
install(new JerseyModule());
5657
install(new PrometheusModule());
58+
install(new SystemInfoAdminServletModule());
5759

5860
bind(AppInfoProvider.class).to(StroomAppInfoProvider.class);
5961

stroom-cache/stroom-cache-impl/src/main/java/stroom/cache/impl/CacheManagerImpl.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ public SystemInfoResult getSystemInfo(final Map<String, String> params) {
213213
throw new RuntimeException(LogUtil.message("Unknown cache name {}", cacheName));
214214
}
215215
} else {
216-
final List<String> cacheNames = caches.keySet()
216+
final List<String> cacheNames = getCacheNames()
217217
.stream()
218218
.sorted()
219219
.limit(limit)
@@ -243,4 +243,14 @@ public List<ParamInfo> getParamInfo() {
243243
"The name of the cache to see the list of keys for. " +
244244
"If not supplied a list of cache names will be returned"));
245245
}
246+
247+
@Override
248+
public List<NamedParamCombination> getNamedParamCombinations() {
249+
return getCacheNames()
250+
.stream()
251+
.filter(Objects::nonNull)
252+
.map(cacheName ->
253+
new NamedParamCombination(PARAM_NAME_CACHE_NAME, cacheName))
254+
.toList();
255+
}
246256
}

stroom-data/stroom-data-store-impl-fs/src/main/java/stroom/data/store/impl/fs/FsVolumeService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ private OptionalLong getDefaultVolumeLimit(final Path path) {
709709
@Override
710710
public SystemInfoResult getSystemInfo() {
711711

712-
final Volumes volumes = getCurrentVolumes();
712+
final Volumes volumes = securityContext.asProcessingUserResult(this::getCurrentVolumes);
713713
final List<Map<String, Object>> volInfoList = volumes
714714
.getMap()
715715
.values()

stroom-dropwizard-common/src/main/java/stroom/dropwizard/common/AdminServlets.java

Lines changed: 143 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,22 @@
1919
import stroom.util.ConsoleColour;
2020
import stroom.util.logging.LogUtil;
2121
import stroom.util.shared.IsAdminServlet;
22+
import stroom.util.shared.NullSafe;
2223

2324
import io.dropwizard.core.setup.Environment;
24-
import io.vavr.Tuple;
25-
import io.vavr.Tuple3;
2625
import jakarta.inject.Inject;
2726
import jakarta.servlet.Servlet;
27+
import jakarta.servlet.ServletException;
28+
import jakarta.servlet.http.HttpServlet;
29+
import jakarta.servlet.http.HttpServletRequest;
30+
import jakarta.servlet.http.HttpServletResponse;
2831
import org.apache.commons.lang3.StringUtils;
2932
import org.eclipse.jetty.servlet.ServletContextHandler;
3033
import org.eclipse.jetty.servlet.ServletHolder;
3134
import org.slf4j.Logger;
3235
import org.slf4j.LoggerFactory;
3336

37+
import java.io.IOException;
3438
import java.util.Comparator;
3539
import java.util.HashSet;
3640
import java.util.List;
@@ -45,6 +49,7 @@ public class AdminServlets {
4549
private static final Logger LOGGER = LoggerFactory.getLogger(AdminServlets.class);
4650

4751
private static final String SERVLET_PATH_KEY = "servletPath";
52+
private static final String MENU_SERVLET_PATH = "/menu";
4853

4954
private final Environment environment;
5055
private final Set<IsAdminServlet> adminServlets;
@@ -85,39 +90,32 @@ public void register() {
8590
.orElse(0);
8691

8792
// Register all the path specs for each servlet class in pathspec order
88-
adminServlets.stream()
93+
final List<AdminServletInfo> servletInfoList = adminServlets.stream()
8994
.flatMap(servlet ->
9095
servlet.getPathSpecs().stream()
91-
.map(partialPathSpec -> {
96+
.map(pathSpec -> {
9297
final String name = servlet.getClass().getName();
93-
final String servletPath = Objects.requireNonNull(partialPathSpec);
94-
return Tuple.of(servlet, name, servletPath);
98+
final String servletPath = Objects.requireNonNull(pathSpec);
99+
return new AdminServletInfo(servlet, name, servletPath);
95100
}))
96-
.sorted(Comparator.comparing(Tuple3::_3))
97-
.forEach(tuple3 -> {
98-
final IsAdminServlet isAdminServlet = tuple3._1();
99-
final String name = tuple3._2();
100-
final String fullPathSpec = tuple3._3();
101-
102-
addServlet(
103-
servletContextHandler,
104-
allPaths,
105-
maxNameLength,
106-
isAdminServlet,
107-
name,
108-
fullPathSpec);
109-
});
101+
.sorted(Comparator.comparing(AdminServletInfo::partialPathSpec))
102+
.toList();
103+
104+
addMenuServlet(servletContextHandler, allPaths, maxNameLength, servletInfoList);
105+
106+
servletInfoList.forEach(servletInfo ->
107+
addServlet(servletContextHandler, allPaths, maxNameLength, servletInfo));
110108
}
111109

112110
private void addServlet(final ServletContextHandler servletContextHandler,
113111
final Set<String> allPaths,
114112
final int maxNameLength,
115-
final IsAdminServlet isAdminServlet,
116-
final String name,
117-
final String partialPathSpec) {
113+
final AdminServletInfo adminServletInfo) {
118114

119115
final String contextPath = servletContextHandler.getContextPath();
120-
final String fullPathSpec = contextPath + partialPathSpec;
116+
final String fullPathSpec = contextPath + adminServletInfo.partialPathSpec;
117+
final String name = adminServletInfo.servletName;
118+
final IsAdminServlet isAdminServlet = adminServletInfo.adminServlet;
121119
if (allPaths.contains(fullPathSpec)) {
122120
LOGGER.error("\t{} => {} {}",
123121
StringUtils.rightPad(name, maxNameLength, " "),
@@ -137,7 +135,127 @@ private void addServlet(final ServletContextHandler servletContextHandler,
137135
throw new RuntimeException(LogUtil.message("Injected class {} is not a Servlet",
138136
isAdminServlet.getClass().getName()));
139137
}
140-
servletContextHandler.addServlet(servletHolder, partialPathSpec);
138+
servletContextHandler.addServlet(servletHolder, adminServletInfo.partialPathSpec);
141139
allPaths.add(fullPathSpec);
142140
}
141+
142+
private void addMenuServlet(final ServletContextHandler servletContextHandler,
143+
final Set<String> allPaths,
144+
final int maxNameLength,
145+
final List<AdminServletInfo> adminServletInfoList) {
146+
147+
148+
final String contextPath = servletContextHandler.getContextPath();
149+
final String fullPathSpec = contextPath + MENU_SERVLET_PATH;
150+
final String name = MenuServlet.class.getName();
151+
152+
final MenuServlet menuServlet = new MenuServlet(contextPath, adminServletInfoList);
153+
154+
if (allPaths.contains(fullPathSpec)) {
155+
//noinspection LoggingSimilarMessage
156+
LOGGER.error("\t{} => {} {}",
157+
StringUtils.rightPad(name, maxNameLength, " "),
158+
fullPathSpec,
159+
ConsoleColour.red("**Duplicate path**"));
160+
throw new RuntimeException(LogUtil.message("Duplicate servlet path {}", fullPathSpec));
161+
}
162+
//noinspection LoggingSimilarMessage
163+
LOGGER.info("\t{} => {}",
164+
StringUtils.rightPad(name, maxNameLength, " "),
165+
fullPathSpec);
166+
167+
final ServletHolder servletHolder = new ServletHolder(name, menuServlet);
168+
servletContextHandler.addServlet(servletHolder, MENU_SERVLET_PATH);
169+
allPaths.add(fullPathSpec);
170+
}
171+
172+
173+
// --------------------------------------------------------------------------------
174+
175+
176+
private record AdminServletInfo(IsAdminServlet adminServlet,
177+
String servletName,
178+
String partialPathSpec) {
179+
180+
}
181+
182+
183+
// --------------------------------------------------------------------------------
184+
185+
186+
private static class MenuServlet extends HttpServlet {
187+
188+
private final String html;
189+
190+
private MenuServlet(final String contextPath,
191+
final List<AdminServletInfo> servletInfoList) {
192+
final List<AdminServletInfo> sortedInfo = NullSafe.stream(servletInfoList)
193+
.sorted(Comparator.comparing(adminServletInfo ->
194+
adminServletInfo.adminServlet.getDisplayName()))
195+
.toList();
196+
197+
final StringBuilder stringBuilder = new StringBuilder();
198+
writeHtmlHeader(stringBuilder);
199+
stringBuilder.append("<h1>Admin Servlets Menu</h1>");
200+
stringBuilder.append("<ul>");
201+
appendMenuItem(stringBuilder, "Dropwizard Admin Servlet", contextPath);
202+
203+
for (final AdminServletInfo servletInfo : sortedInfo) {
204+
appendMenuItem(
205+
stringBuilder,
206+
servletInfo.adminServlet.getDisplayName(),
207+
contextPath + servletInfo.partialPathSpec);
208+
}
209+
stringBuilder.append("</ul>");
210+
writeHtmlFooter(stringBuilder);
211+
html = stringBuilder.toString();
212+
}
213+
214+
@Override
215+
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
216+
throws ServletException, IOException {
217+
resp.setContentType("text/html");
218+
resp.getWriter().print(html);
219+
}
220+
221+
private static void appendMenuItem(final StringBuilder stringBuilder,
222+
final String name,
223+
final String url) {
224+
stringBuilder.append("<li>")
225+
.append("<a href=\"")
226+
.append(url)
227+
.append("\">")
228+
.append(name)
229+
.append("</a>")
230+
.append("</li>");
231+
}
232+
233+
private static void writeHtmlHeader(final StringBuilder stringBuilder) {
234+
stringBuilder.append("""
235+
<!DOCTYPE html>
236+
<html>
237+
<head>
238+
<title>Admin Servlets Menu</title>
239+
<style>
240+
body {
241+
font-family: arial, tahoma, verdana;
242+
}
243+
li {
244+
line-height: 1.75em;
245+
}
246+
p {
247+
margin-top: 0.5em;
248+
margin-bottom: 0.5em;
249+
}
250+
</style>
251+
</head>
252+
<body>""");
253+
}
254+
255+
private static void writeHtmlFooter(final StringBuilder stringBuilder) {
256+
stringBuilder.append("""
257+
</body>
258+
</html>""");
259+
}
260+
}
143261
}

stroom-dropwizard-common/src/main/java/stroom/dropwizard/common/FilteredHealthCheckServlet.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public class FilteredHealthCheckServlet extends HttpServlet implements IsAdminSe
6565
private static final String PARAM_NAME_MINIMAL = "minimal";
6666
private static final String PARAM_NAME_PRETTY = "pretty";
6767
private static final String CONTENT_TYPE = "application/json";
68+
private static final String DISPLAY_NAME = "Filtered Health Check Servlet";
6869

6970
private final HealthCheckRegistry healthCheckRegistry;
7071
private final ObjectMapper objectMapper;
@@ -148,11 +149,11 @@ private HealthCheckFilter buildHealthCheckFilter(final HttpServletRequest reques
148149
final Set<String> allowSet = allowListParamVal == null || allowListParamVal.isBlank()
149150
? Collections.emptySet()
150151
: Arrays.stream(allowListParamVal.split(","))
151-
.collect(Collectors.toSet());
152+
.collect(Collectors.toSet());
152153
final Set<String> denySet = denyListParamVal == null || denyListParamVal.isBlank()
153154
? Collections.emptySet()
154155
: Arrays.stream(denyListParamVal.split(","))
155-
.collect(Collectors.toSet());
156+
.collect(Collectors.toSet());
156157

157158
validateName(allowSet, PARAM_NAME_ALLOW_LIST);
158159
validateName(denySet, PARAM_NAME_DENY_LIST);
@@ -177,8 +178,8 @@ private void validateName(final Set<String> names,
177178
if (!allNames.contains(allowName)) {
178179
throw new RuntimeException(
179180
"Name '" + allowName
180-
+ "' is not a valid health check name for parameter '"
181-
+ paramName + "'.");
181+
+ "' is not a valid health check name for parameter '"
182+
+ paramName + "'.");
182183
}
183184
}
184185
}
@@ -188,4 +189,9 @@ private void validateName(final Set<String> names,
188189
public Set<String> getPathSpecs() {
189190
return PATH_SPECS;
190191
}
192+
193+
@Override
194+
public String getDisplayName() {
195+
return DISPLAY_NAME;
196+
}
191197
}

stroom-dropwizard-common/src/main/java/stroom/dropwizard/common/prometheus/PrometheusMetricsServlet.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class PrometheusMetricsServlet extends HttpServlet implements IsAdminServlet {
7373
private static final LambdaLogger LOGGER = LambdaLoggerFactory.getLogger(PrometheusMetricsServlet.class);
7474
private static final String BASE_PATH_SPEC = "/prometheusMetrics";
7575
private static final Set<String> PATH_SPECS = Set.of(BASE_PATH_SPEC);
76+
private static final String DISPLAY_NAME = "Prometheus Metrics";
7677

7778
public static final String METRICS_REGISTRY = MetricsServlet.class.getCanonicalName() + ".registry";
7879
public static final String METRIC_FILTER = MetricsServlet.class.getCanonicalName() + ".metricFilter";
@@ -97,6 +98,11 @@ public Set<String> getPathSpecs() {
9798
return PATH_SPECS;
9899
}
99100

101+
@Override
102+
public String getDisplayName() {
103+
return DISPLAY_NAME;
104+
}
105+
100106
@Override
101107
public void init(final ServletConfig config) throws ServletException {
102108
super.init(config);

0 commit comments

Comments
 (0)