Skip to content

Commit e0b4b8a

Browse files
committed
Merge pull request 'Micrometer Monitoring' (#33) from feature/18278 into develop
2 parents a8c7b36 + b2e42c3 commit e0b4b8a

22 files changed

+760
-233
lines changed

pom.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,14 @@
240240
<artifactId>mysql-connector-java</artifactId>
241241
</dependency>
242242

243-
243+
<dependency>
244+
<groupId>io.micrometer</groupId>
245+
<artifactId>micrometer-registry-prometheus</artifactId>
246+
</dependency>
247+
<dependency>
248+
<groupId>io.micrometer</groupId>
249+
<artifactId>micrometer-registry-influx</artifactId>
250+
</dependency>
244251

245252
<!-- Kubernetes -->
246253
<dependency>

src/main/java/eu/openanalytics/containerproxy/ContainerProxyApplication.java

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,20 @@
3131
import org.springframework.boot.actuate.health.HealthIndicator;
3232
import org.springframework.boot.actuate.redis.RedisHealthIndicator;
3333
import org.springframework.boot.autoconfigure.SpringBootApplication;
34+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
3435
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
3536
import org.springframework.boot.web.server.PortInUseException;
3637
import org.springframework.boot.web.servlet.FilterRegistrationBean;
3738
import org.springframework.context.annotation.Bean;
3839
import org.springframework.context.annotation.ComponentScan;
3940
import org.springframework.core.env.Environment;
4041
import org.springframework.data.redis.connection.RedisConnectionFactory;
41-
import org.springframework.session.data.redis.config.ConfigureRedisAction;
42+
import org.springframework.security.core.session.SessionRegistry;
43+
import org.springframework.security.web.session.HttpSessionEventPublisher;
44+
import org.springframework.session.FindByIndexNameSessionRepository;
45+
import org.springframework.session.Session;
4246
import org.springframework.session.web.http.DefaultCookieSerializer;
47+
import org.springframework.session.security.SpringSessionBackedSessionRegistry;
4348
import org.springframework.web.filter.FormContentFilter;
4449

4550
import javax.annotation.PostConstruct;
@@ -142,16 +147,6 @@ public JSR353Module jsr353Module() {
142147
return new JSR353Module();
143148
}
144149

145-
/**
146-
* Compatibility with AWS ElastiCache
147-
*
148-
* @return
149-
*/
150-
@Bean
151-
public static ConfigureRedisAction configureRedisAction() {
152-
return ConfigureRedisAction.NO_OP;
153-
}
154-
155150
@Bean
156151
public HealthIndicator redisSessionHealthIndicator(RedisConnectionFactory rdeRedisConnectionFactory) {
157152
if (Objects.equals(environment.getProperty("spring.session.store-type"), "redis")) {
@@ -174,11 +169,27 @@ public Health health() {
174169
}
175170
}
176171

172+
/**
173+
* This Bean ensures that User Session are properly expired when using Redis for session storage.
174+
*/
175+
@Bean
176+
@ConditionalOnProperty(name = "spring.session.store-type", havingValue = "redis")
177+
public <S extends Session> SessionRegistry sessionRegistry(FindByIndexNameSessionRepository<S> sessionRepository) {
178+
return new SpringSessionBackedSessionRegistry<S>(sessionRepository);
179+
}
180+
181+
@Bean
182+
public HttpSessionEventPublisher httpSessionEventPublisher() {
183+
return new HttpSessionEventPublisher();
184+
}
185+
177186
private static void setDefaultProperties(SpringApplication app) {
178187
Properties properties = new Properties();
179188

180189
// use in-memory session storage by default. Can be overwritten in application.yml
181190
properties.put("spring.session.store-type", "none");
191+
// required for proper working of the SP_USER_INITIATED_LOGOUT session attribute in the UserService
192+
properties.put("spring.session.redis.flush-mode", "IMMEDIATE");
182193

183194
// disable multi-part handling by Spring. We don't need this anywhere in the application.
184195
// When enabled this will cause problems when proxying file-uploads to the shiny apps.
@@ -189,6 +200,22 @@ private static void setDefaultProperties(SpringApplication app) {
189200

190201
properties.put("spring.application.name", "ContainerProxy");
191202

203+
// Metrics configuration
204+
// ====================
205+
206+
// disable all supported exporters by default
207+
// Note: if we upgrade to Spring Boot 2.4.0 we can use properties.put("management.metrics.export.defaults.enabled", "false");
208+
properties.put("management.metrics.export.prometheus.enabled", "false");
209+
properties.put("management.metrics.export.influx.enabled", "false");
210+
// set actuator to port 9090 (can be overwritten)
211+
properties.put("management.server.port", "9090");
212+
// enable prometheus endpoint by default (but not the exporter)
213+
properties.put("management.endpoint.prometheus.enabled", "true");
214+
// include prometheus and health endpoint in exposure
215+
properties.put("management.endpoints.web.exposure.include", "health,prometheus");
216+
217+
// ====================
218+
192219
// Health configuration
193220
// ====================
194221

@@ -198,7 +225,7 @@ private static void setDefaultProperties(SpringApplication app) {
198225
properties.put("management.health.ldap.enabled", false);
199226
// disable default redis health endpoint since it's managed by redisSession
200227
properties.put("management.health.redis.enabled", "false");
201-
// enable Kubernetes porobes
228+
// enable Kubernetes probes
202229
properties.put("management.endpoint.health.probes.enabled", true);
203230

204231
// ====================

src/main/java/eu/openanalytics/containerproxy/auth/UserLogoutHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class UserLogoutHandler implements LogoutHandler {
3535

3636
@Inject
3737
private UserService userService;
38-
38+
3939
@Override
4040
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
4141
userService.logout(authentication);

src/main/java/eu/openanalytics/containerproxy/auth/impl/KerberosAuthenticationBackend.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727

2828
import javax.inject.Inject;
2929

30+
import eu.openanalytics.containerproxy.event.UserLogoutEvent;
31+
import org.springframework.context.event.EventListener;
3032
import org.springframework.core.env.Environment;
3133
import org.springframework.core.io.FileSystemResource;
3234
import org.springframework.security.authentication.AuthenticationManager;
@@ -52,25 +54,22 @@
5254
import eu.openanalytics.containerproxy.auth.impl.kerberos.KRBClientCacheRegistry;
5355
import eu.openanalytics.containerproxy.auth.impl.kerberos.KRBTicketRenewalManager;
5456
import eu.openanalytics.containerproxy.model.spec.ContainerSpec;
55-
import eu.openanalytics.containerproxy.service.EventService;
56-
import eu.openanalytics.containerproxy.service.EventService.EventType;
5757

5858
public class KerberosAuthenticationBackend implements IAuthenticationBackend {
5959

6060
public static final String NAME = "kerberos";
6161

6262
private KRBClientCacheRegistry ccacheReg;
63-
63+
64+
private KRBTicketRenewalManager renewalManager;
65+
6466
@Inject
6567
Environment environment;
6668

6769
@Lazy
6870
@Inject
6971
AuthenticationManager authenticationManager;
7072

71-
@Inject
72-
EventService eventService;
73-
7473
@Override
7574
public String getName() {
7675
return NAME;
@@ -116,13 +115,9 @@ public void configureAuthenticationManagerBuilder(AuthenticationManagerBuilder a
116115
ticketValidator.setDebug(true);
117116
ticketValidator.afterPropertiesSet();
118117

119-
KRBTicketRenewalManager renewalManager = new KRBTicketRenewalManager(
118+
renewalManager = new KRBTicketRenewalManager(
120119
delegSvcPrinc, delegSvcKeytab, backendPrincipals, ccacheReg, ticketRenewInterval);
121120

122-
eventService.addListener(e -> {
123-
if (EventType.Logout.toString().equals(e.type)) renewalManager.stop(e.user);
124-
});
125-
126121
KerberosServiceAuthenticationProvider authProvider = new KerberosServiceAuthenticationProvider() {
127122
@Override
128123
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
@@ -138,6 +133,11 @@ public Authentication authenticate(Authentication authentication) throws Authent
138133
auth.authenticationProvider(authProvider);
139134
}
140135

136+
@EventListener
137+
public void on(UserLogoutEvent event) {
138+
renewalManager.stop(event.getUserId());
139+
}
140+
141141
@Override
142142
public void customizeContainer(ContainerSpec spec) {
143143
String principal = getCurrentPrincipal();

src/main/java/eu/openanalytics/containerproxy/backend/AbstractContainerBackend.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ public void initialize() throws ContainerProxyException {
129129
public void startProxy(Proxy proxy) throws ContainerProxyException {
130130
proxy.setId(UUID.randomUUID().toString());
131131
proxy.setStatus(ProxyStatus.Starting);
132-
proxy.setStartupTimestamp(System.currentTimeMillis());
133-
132+
proxy.setCreatedTimestamp(System.currentTimeMillis());
133+
134134
try {
135135
doStartProxy(proxy);
136136
} catch (Throwable t) {
@@ -142,7 +142,8 @@ public void startProxy(Proxy proxy) throws ContainerProxyException {
142142
stopProxy(proxy);
143143
throw new ContainerProxyException("Container did not respond in time");
144144
}
145-
145+
146+
proxy.setStartupTimestamp(System.currentTimeMillis());
146147
proxy.setStatus(ProxyStatus.Up);
147148
}
148149

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* ContainerProxy
3+
*
4+
* Copyright (C) 2016-2020 Open Analytics
5+
*
6+
* ===========================================================================
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the Apache License as published by
10+
* The Apache Software Foundation, either version 2 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* Apache License for more details.
17+
*
18+
* You should have received a copy of the Apache License
19+
* along with this program. If not, see <http://www.apache.org/licenses/>
20+
*/
21+
package eu.openanalytics.containerproxy.event;
22+
23+
import org.springframework.context.ApplicationEvent;
24+
25+
public class AuthFailedEvent extends ApplicationEvent {
26+
27+
private final String userId;
28+
private final String sessionId;
29+
30+
public AuthFailedEvent(Object source, String userId, String sessionId) {
31+
super(source);
32+
this.userId = userId;
33+
this.sessionId = sessionId;
34+
}
35+
36+
public String getSessionId() {
37+
return sessionId;
38+
}
39+
40+
public String getUserId() {
41+
return userId;
42+
}
43+
44+
}
45+
46+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* ContainerProxy
3+
*
4+
* Copyright (C) 2016-2020 Open Analytics
5+
*
6+
* ===========================================================================
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the Apache License as published by
10+
* The Apache Software Foundation, either version 2 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* Apache License for more details.
17+
*
18+
* You should have received a copy of the Apache License
19+
* along with this program. If not, see <http://www.apache.org/licenses/>
20+
*/
21+
package eu.openanalytics.containerproxy.event;
22+
23+
import org.springframework.context.ApplicationEvent;
24+
25+
import java.time.Duration;
26+
27+
public class ProxyStartEvent extends ApplicationEvent {
28+
29+
private final String userId;
30+
private final String specId;
31+
private final Duration startupTime;
32+
33+
public ProxyStartEvent(Object source, String userId, String specId, Duration startupTime) {
34+
super(source);
35+
this.userId = userId;
36+
this.specId = specId;
37+
this.startupTime = startupTime;
38+
}
39+
40+
public String getUserId() {
41+
return userId;
42+
}
43+
44+
public String getSpecId() {
45+
return specId;
46+
}
47+
48+
public Duration getStartupTime() {
49+
return startupTime;
50+
}
51+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* ContainerProxy
3+
*
4+
* Copyright (C) 2016-2020 Open Analytics
5+
*
6+
* ===========================================================================
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the Apache License as published by
10+
* The Apache Software Foundation, either version 2 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* Apache License for more details.
17+
*
18+
* You should have received a copy of the Apache License
19+
* along with this program. If not, see <http://www.apache.org/licenses/>
20+
*/
21+
package eu.openanalytics.containerproxy.event;
22+
23+
import org.springframework.context.ApplicationEvent;
24+
25+
public class ProxyStartFailedEvent extends ApplicationEvent {
26+
27+
private final String userId;
28+
private final String specId;
29+
30+
public ProxyStartFailedEvent(Object source, String userId, String specId) {
31+
super(source);
32+
this.userId = userId;
33+
this.specId = specId;
34+
}
35+
36+
public String getUserId() {
37+
return userId;
38+
}
39+
40+
public String getSpecId() {
41+
return specId;
42+
}
43+
44+
}

0 commit comments

Comments
 (0)