Skip to content

Commit d424947

Browse files
committed
Merge pull request 'Improve configuring whether a proxy should be stopped' (#57) from feature/26332 into develop
2 parents 6a4902a + c228afa commit d424947

8 files changed

Lines changed: 144 additions & 105 deletions

File tree

src/main/java/eu/openanalytics/containerproxy/backend/strategy/impl/DefaultProxyLogoutStrategy.java

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,50 @@
2020
*/
2121
package eu.openanalytics.containerproxy.backend.strategy.impl;
2222

23-
import javax.inject.Inject;
24-
25-
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
26-
import org.springframework.context.annotation.Lazy;
27-
import org.springframework.stereotype.Component;
28-
2923
import eu.openanalytics.containerproxy.backend.strategy.IProxyLogoutStrategy;
3024
import eu.openanalytics.containerproxy.model.runtime.Proxy;
3125
import eu.openanalytics.containerproxy.service.ProxyService;
26+
import org.springframework.core.env.Environment;
27+
import org.springframework.stereotype.Component;
28+
29+
import javax.annotation.PostConstruct;
30+
import javax.inject.Inject;
3231

3332
/**
34-
* Default logout behaviour: stop all proxies owned by the user.
33+
* Default logout behaviour.
3534
*/
3635
@Component
37-
@ConditionalOnProperty(name = "proxy.stop-proxies-on-logout", havingValue = "true", matchIfMissing = true)
3836
public class DefaultProxyLogoutStrategy implements IProxyLogoutStrategy {
3937

38+
private static final String PROP_DEFAULT_STOP_PROXIES_ON_LOGOUT = "proxy.default-stop-proxy-on-logout";
39+
4040
@Inject
4141
private ProxyService proxyService;
42-
42+
43+
@Inject
44+
private Environment environment;
45+
46+
private boolean defaultStopProxyOnLogout;
47+
48+
@PostConstruct
49+
public void init() {
50+
defaultStopProxyOnLogout = environment.getProperty(PROP_DEFAULT_STOP_PROXIES_ON_LOGOUT, Boolean.class, true);
51+
}
52+
4353
@Override
4454
public void onLogout(String userId) {
4555
for (Proxy proxy: proxyService.getProxies(p -> p.getUserId().equals(userId), true)) {
46-
proxyService.stopProxy(proxy, true, true);
56+
if (shouldBeStopped(proxy)) {
57+
proxyService.stopProxy(proxy, true, true);
58+
}
59+
}
60+
}
61+
62+
public boolean shouldBeStopped(Proxy proxy) {
63+
if (proxy.getSpec().stopOnLogout() != null) {
64+
return proxy.getSpec().stopOnLogout();
4765
}
66+
return defaultStopProxyOnLogout;
4867
}
4968

5069
}

src/main/java/eu/openanalytics/containerproxy/backend/strategy/impl/NoOpProxyLogoutStrategy.java

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/main/java/eu/openanalytics/containerproxy/model/spec/ProxySpec.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public class ProxySpec {
4444
private List<String> kubernetesAdditionalPersistentManifests = new ArrayList<>();
4545

4646
private Long maxLifeTime;
47+
private Boolean stopOnLogout;
48+
private Long heartbeatTimeout;
4749

4850
public ProxySpec() {
4951
settings = new HashMap<>();
@@ -162,13 +164,30 @@ public void setMaxLifeTime(Long maxLifeTime) {
162164
this.maxLifeTime = maxLifeTime;
163165
}
164166

167+
public Boolean stopOnLogout() {
168+
return stopOnLogout;
169+
}
170+
171+
public void setStopOnLogout(Boolean stopOnLogout) {
172+
this.stopOnLogout = stopOnLogout;
173+
}
174+
175+
public Long getHeartbeatTimeout() {
176+
return heartbeatTimeout;
177+
}
178+
179+
public void setHeartbeatTimeout(Long heartbeatTimeout) {
180+
this.heartbeatTimeout = heartbeatTimeout;
181+
}
165182

166183
public void copy(ProxySpec target) {
167184
target.setId(id);
168185
target.setDisplayName(displayName);
169186
target.setDescription(description);
170187
target.setLogoURL(logoURL);
171188
target.setMaxLifeTime(maxLifeTime);
189+
target.setStopOnLogout(stopOnLogout);
190+
target.setHeartbeatTimeout(heartbeatTimeout);
172191

173192
if (accessControl != null) {
174193
if (target.getAccessControl() == null) target.setAccessControl(new ProxyAccessControl());

src/main/java/eu/openanalytics/containerproxy/service/AccessControlService.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ public boolean canAccess(Authentication auth, String specId) {
7171
}
7272

7373
public boolean canAccess(Authentication auth, ProxySpec spec) {
74+
if (auth == null || spec == null) {
75+
return false;
76+
}
7477
Optional<String> sessionId = getSessionId();
7578
if (!sessionId.isPresent()) {
7679
return checkAccess(auth, spec);
@@ -91,10 +94,6 @@ private Optional<String> getSessionId() {
9194
}
9295

9396
private boolean checkAccess(Authentication auth, ProxySpec spec) {
94-
if (auth == null || spec == null) {
95-
return false;
96-
}
97-
9897
if (auth instanceof AnonymousAuthenticationToken) {
9998
// if anonymous -> only allow access if we the backend has no authorization enabled
10099
return !authBackend.hasAuthorization();

src/main/java/eu/openanalytics/containerproxy/service/UserService.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import eu.openanalytics.containerproxy.event.UserLogoutEvent;
2828
import eu.openanalytics.containerproxy.model.runtime.Proxy;
2929
import eu.openanalytics.containerproxy.model.spec.ProxySpec;
30-
import eu.openanalytics.containerproxy.util.SessionHelper;
3130
import org.apache.logging.log4j.LogManager;
3231
import org.apache.logging.log4j.Logger;
3332
import org.springframework.context.ApplicationEventPublisher;

src/main/java/eu/openanalytics/containerproxy/service/hearbeat/ActiveProxiesService.java

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import java.util.Collections;
3636
import java.util.HashMap;
3737
import java.util.Map;
38+
import java.util.Timer;
39+
import java.util.TimerTask;
3840

3941
/**
4042
* Service which 1) keeps track of active proxies by listening for heartbeats (created by {@link HeartbeatService})
@@ -43,7 +45,6 @@
4345
@Service
4446
public class ActiveProxiesService implements IHeartbeatProcessor {
4547

46-
public static final String PROP_ENABLED = "proxy.heartbeat-enabled";
4748
public static final String PROP_RATE = "proxy.heartbeat-rate";
4849
public static final Long DEFAULT_RATE = 10000L;
4950
public static final String PROP_TIMEOUT = "proxy.heartbeat-timeout";
@@ -53,8 +54,7 @@ public class ActiveProxiesService implements IHeartbeatProcessor {
5354

5455
private final Map<String, Long> proxyHeartbeats = Collections.synchronizedMap(new HashMap<>());
5556

56-
private long cleanupInterval;
57-
private long heartbeatTimeout;
57+
private long defaultHeartbeatTimeout;
5858

5959
@Inject
6060
private Environment environment;
@@ -64,23 +64,14 @@ public class ActiveProxiesService implements IHeartbeatProcessor {
6464

6565
@PostConstruct
6666
public void init() {
67-
Boolean enabled = environment.getProperty(PROP_ENABLED, Boolean.class);
68-
69-
if (enabled == null) {
70-
enabled = environment.getProperty(PROP_RATE) != null || environment.getProperty(PROP_TIMEOUT) != null;
71-
}
72-
73-
cleanupInterval = 2 * environment.getProperty(PROP_RATE, Long.class, DEFAULT_RATE);
74-
heartbeatTimeout = environment.getProperty(PROP_TIMEOUT, Long.class, DEFAULT_TIMEOUT);
75-
76-
if (enabled) {
77-
Thread cleanupThread = new Thread(new InactiveProxyKiller(), InactiveProxyKiller.class.getSimpleName());
78-
cleanupThread.setDaemon(true);
79-
cleanupThread.start();
80-
log.debug("Releasing of inactive proxies enabled.");
81-
} else {
82-
log.debug("Releasing of inactive proxies disabled.");
83-
}
67+
long cleanupInterval = 2 * environment.getProperty(PROP_RATE, Long.class, DEFAULT_RATE);
68+
defaultHeartbeatTimeout = environment.getProperty(PROP_TIMEOUT, Long.class, DEFAULT_TIMEOUT);
69+
new Timer().schedule(new TimerTask() {
70+
@Override
71+
public void run() {
72+
performCleanup();
73+
}
74+
}, cleanupInterval, cleanupInterval);
8475
}
8576

8677
@Override
@@ -95,33 +86,45 @@ public Long getLastHeartBeat(String proxyId) {
9586
return proxyHeartbeats.get(proxyId);
9687
}
9788

98-
private class InactiveProxyKiller implements Runnable {
99-
@Override
100-
public void run() {
101-
// TODO this could be replaced with a java Timer
102-
while (true) {
103-
try {
104-
long currentTimestamp = System.currentTimeMillis();
105-
for (Proxy proxy : proxyService.getProxies(null, true)) {
106-
if (proxy.getStatus() != ProxyStatus.Up) continue;
107-
108-
Long lastHeartbeat = proxyHeartbeats.get(proxy.getId());
109-
if (lastHeartbeat == null) lastHeartbeat = proxy.getStartupTimestamp();
110-
long proxySilence = currentTimestamp - lastHeartbeat;
111-
if (proxySilence > heartbeatTimeout) {
112-
log.info(String.format("Releasing inactive proxy [user: %s] [spec: %s] [id: %s] [silence: %dms]", proxy.getUserId(), proxy.getSpec().getId(), proxy.getId(), proxySilence));
113-
proxyHeartbeats.remove(proxy.getId());
114-
proxyService.stopProxy(proxy, true, true);
115-
}
116-
}
117-
} catch (Throwable t) {
118-
log.error("Error in " + this.getClass().getSimpleName(), t);
119-
}
120-
try {
121-
Thread.sleep(cleanupInterval);
122-
} catch (InterruptedException e) {
123-
}
89+
private void performCleanup() {
90+
try {
91+
long currentTimestamp = System.currentTimeMillis();
92+
for (Proxy proxy : proxyService.getProxies(null, true)) {
93+
checkAndReleaseProxy(currentTimestamp, proxy);
12494
}
95+
} catch (Throwable t) {
96+
log.error("Error in " + this.getClass().getSimpleName(), t);
97+
}
98+
}
99+
100+
private void checkAndReleaseProxy(long currentTimestamp, Proxy proxy) {
101+
if (proxy.getStatus() != ProxyStatus.Up) {
102+
return;
103+
}
104+
105+
Long heartbeatTimeout;
106+
if (proxy.getSpec().getHeartbeatTimeout() != null) {
107+
heartbeatTimeout = proxy.getSpec().getHeartbeatTimeout();
108+
} else {
109+
heartbeatTimeout = defaultHeartbeatTimeout;
110+
}
111+
112+
if (heartbeatTimeout <= 0) {
113+
// heartbeats disabled for this app (or globally)
114+
return;
115+
}
116+
117+
Long lastHeartbeat = proxyHeartbeats.get(proxy.getId());
118+
if (lastHeartbeat == null) {
119+
lastHeartbeat = proxy.getStartupTimestamp();
120+
}
121+
122+
long proxySilence = currentTimestamp - lastHeartbeat;
123+
if (proxySilence > heartbeatTimeout) {
124+
log.info(String.format("Releasing inactive proxy [user: %s] [spec: %s] [id: %s] [silence: %dms]", proxy.getUserId(), proxy.getSpec().getId(), proxy.getId(), proxySilence));
125+
proxyHeartbeats.remove(proxy.getId());
126+
proxyService.stopProxy(proxy, true, true);
125127
}
126128
}
129+
127130
}

0 commit comments

Comments
 (0)