2020 */
2121package eu .openanalytics .containerproxy .test .proxy ;
2222
23+ import com .google .common .base .Throwables ;
2324import eu .openanalytics .containerproxy .backend .dispatcher .proxysharing .IDelegateProxyStore ;
2425import eu .openanalytics .containerproxy .backend .dispatcher .proxysharing .ProxySharingScaler ;
26+ import eu .openanalytics .containerproxy .backend .dispatcher .proxysharing .Seat ;
2527import eu .openanalytics .containerproxy .backend .dispatcher .proxysharing .SeatIdKey ;
2628import eu .openanalytics .containerproxy .backend .dispatcher .proxysharing .store .DelegateProxy ;
29+ import eu .openanalytics .containerproxy .backend .dispatcher .proxysharing .store .DelegateProxyStatus ;
30+ import eu .openanalytics .containerproxy .backend .dispatcher .proxysharing .store .ISeatStore ;
31+ import eu .openanalytics .containerproxy .model .runtime .Container ;
2732import eu .openanalytics .containerproxy .model .runtime .Proxy ;
2833import eu .openanalytics .containerproxy .model .runtime .runtimevalues .PublicPathKey ;
2934import eu .openanalytics .containerproxy .model .runtime .runtimevalues .TargetIdKey ;
3035import eu .openanalytics .containerproxy .test .helpers .ContainerSetup ;
3136import eu .openanalytics .containerproxy .test .helpers .ShinyProxyInstance ;
37+ import eu .openanalytics .containerproxy .test .helpers .TestHelperException ;
3238import eu .openanalytics .containerproxy .test .helpers .TestProxySharingScaler ;
3339import eu .openanalytics .containerproxy .util .Retrying ;
40+ import okhttp3 .Request ;
41+ import okhttp3 .Response ;
3442import org .junit .jupiter .api .Assertions ;
43+ import org .junit .jupiter .api .Test ;
3544import org .junit .jupiter .params .ParameterizedTest ;
3645import org .junit .jupiter .params .provider .Arguments ;
3746import org .junit .jupiter .params .provider .MethodSource ;
47+ import org .mandas .docker .client .DockerClient ;
48+ import org .mandas .docker .client .builder .jersey .JerseyDockerClientBuilder ;
49+ import org .mandas .docker .client .exceptions .DockerCertificateException ;
50+ import org .mandas .docker .client .exceptions .DockerException ;
3851
52+ import java .io .IOException ;
3953import java .time .Duration ;
4054import java .time .Instant ;
4155import java .util .ArrayList ;
@@ -57,26 +71,40 @@ private static Stream<Arguments> backends() {
5771 @ MethodSource ("backends" )
5872 public void simpleTest (String backend , Map <String , String > properties ) {
5973 try (ContainerSetup containerSetup = new ContainerSetup (backend )) {
60- try (ShinyProxyInstance inst = new ShinyProxyInstance ("application-test-pre-initialization.yml" , properties , true )) {
61- ProxySharingScaler proxySharingScaler = inst .getBean ("proxySharingScaler_simpleTest" , ProxySharingScaler .class );
74+ try (ShinyProxyInstance inst = new ShinyProxyInstance ("application-test-pre-initialization-1.yml" , properties , true )) {
75+ TestProxySharingScaler proxySharingScaler = inst .getBean ("proxySharingScaler_myApp" , TestProxySharingScaler .class );
76+ IDelegateProxyStore delegateProxyStore = proxySharingScaler .getDelegateProxyStore ();
77+ ISeatStore seatStore = proxySharingScaler .getSeatStore ();
6278 inst .enableCleanup ();
63- String id = inst .client .startProxy ("simpleTest " );
79+ String id = inst .client .startProxy ("myApp " );
6480 Proxy proxy = inst .proxyService .getProxy (id );
6581 inst .client .testProxyReachable (proxy .getTargetId ());
6682
6783 // target id should be different from proxy id
6884 Assertions .assertNotEquals (proxy .getTargetId (), proxy .getId ());
6985 Assertions .assertEquals ("/api/route/" + proxy .getTargetId () + "/" , proxy .getRuntimeValue (PublicPathKey .inst ));
7086 Assertions .assertEquals (proxy .getTargetId (), proxy .getRuntimeValue (TargetIdKey .inst ));
71- Assertions .assertNotNull (proxy .getRuntimeValue (SeatIdKey .inst ));
7287
88+ // check DelegateProxy
89+ DelegateProxy delegateProxy = delegateProxyStore .getDelegateProxy (proxy .getTargetId ());
90+ Assertions .assertNotNull (delegateProxy );
91+ Assertions .assertEquals (1 , delegateProxy .getSeatIds ().size ());
92+ Assertions .assertEquals (proxy .getRuntimeValue (SeatIdKey .inst ), delegateProxy .getSeatIds ().stream ().findFirst ().get ());
93+
94+ // check seat
95+ Seat seat = seatStore .getSeat (proxy .getRuntimeValue (SeatIdKey .inst ));
96+ Assertions .assertEquals (proxy .getRuntimeValue (SeatIdKey .inst ), seat .getId ());
97+ Assertions .assertEquals (delegateProxy .getProxy ().getId (), seat .getDelegateProxyId ());
98+ Assertions .assertEquals (proxy .getId (), seat .getDelegatingProxyId ());
99+
100+ // should have scaled-up
73101 waitUntilNoPendingSeats (proxySharingScaler );
74102 Assertions .assertEquals (1 , proxySharingScaler .getNumClaimedSeats ());
75103 Assertions .assertEquals (1 , proxySharingScaler .getNumUnclaimedSeats ());
76104
77105 // start an additional app
78106 Instant start = Instant .now ();
79- String id2 = inst .client .startProxy ("simpleTest " );
107+ String id2 = inst .client .startProxy ("myApp " );
80108 Proxy proxy2 = inst .proxyService .getProxy (id2 );
81109 inst .client .testProxyReachable (proxy2 .getTargetId ());
82110
@@ -113,9 +141,9 @@ public void simpleTest(String backend, Map<String, String> properties) {
113141 public void testSetPublicPathPrefix (String backend , Map <String , String > properties ) {
114142 ProxySharingScaler .setPublicPathPrefix ("/my/custom/path/" );
115143 try (ContainerSetup containerSetup = new ContainerSetup (backend )) {
116- try (ShinyProxyInstance inst = new ShinyProxyInstance ("application-test-pre-initialization.yml" , properties , true )) {
144+ try (ShinyProxyInstance inst = new ShinyProxyInstance ("application-test-pre-initialization-1 .yml" , properties , true )) {
117145 inst .enableCleanup ();
118- String id = inst .client .startProxy ("simpleTest " );
146+ String id = inst .client .startProxy ("myApp " );
119147 Proxy proxy = inst .proxyService .getProxy (id );
120148 inst .client .testProxyReachable (proxy .getTargetId ());
121149
@@ -134,13 +162,13 @@ public void testSetPublicPathPrefix(String backend, Map<String, String> properti
134162 @ MethodSource ("backends" )
135163 public void testNoContainerReUse (String backend , Map <String , String > properties ) {
136164 try (ContainerSetup containerSetup = new ContainerSetup (backend )) {
137- try (ShinyProxyInstance inst = new ShinyProxyInstance ("application-test-pre-initialization.yml" , properties , true )) {
165+ try (ShinyProxyInstance inst = new ShinyProxyInstance ("application-test-pre-initialization-2 .yml" , properties , true )) {
138166 inst .enableCleanup ();
139- TestProxySharingScaler proxySharingScaler = inst .getBean ("proxySharingScaler_noReUse " , TestProxySharingScaler .class );
167+ TestProxySharingScaler proxySharingScaler = inst .getBean ("proxySharingScaler_myApp " , TestProxySharingScaler .class );
140168 proxySharingScaler .disableCleanup ();
141169 IDelegateProxyStore delegateProxyStore = proxySharingScaler .getDelegateProxyStore ();
142170
143- String id = inst .client .startProxy ("noReUse " );
171+ String id = inst .client .startProxy ("myApp " );
144172 Proxy proxy = inst .proxyService .getProxy (id );
145173 inst .client .testProxyReachable (proxy .getTargetId ());
146174
@@ -173,9 +201,66 @@ public void testNoContainerReUse(String backend, Map<String, String> properties)
173201 }
174202 }
175203
204+ @ ParameterizedTest
205+ @ MethodSource ("backends" )
206+ public void testReUseWithMultipleSeats (String backend , Map <String , String > properties ) {
207+ TestHelperException ex = Assertions .assertThrows (TestHelperException .class ,
208+ () -> new ShinyProxyInstance ("application-test-pre-initialization-3.yml" , properties , true ),
209+ "Provided parameter values are not allowed" );
210+
211+ Throwable rootCause = Throwables .getRootCause (ex );
212+ Assertions .assertInstanceOf (IllegalStateException .class , rootCause );
213+ Assertions .assertEquals ("Spec myApp is invalid: when allow-container-re-use is disabled, seatsPerContainer must be exactly 1" , rootCause .getMessage ());
214+ }
215+
216+ @ Test
217+ public void testDelegateProxyCrashed () throws DockerCertificateException , DockerException , InterruptedException , IOException {
218+ DockerClient dockerClient = new JerseyDockerClientBuilder ().fromEnv ().build ();
219+ try (ContainerSetup containerSetup = new ContainerSetup ("docker" )) {
220+ try (ShinyProxyInstance inst = new ShinyProxyInstance ("application-test-pre-initialization-1.yml" , Map .of ("proxy.container-backend" , "docker" ), true )) {
221+ inst .enableCleanup ();
222+ String id = inst .client .startProxy ("myApp" );
223+ Proxy proxy = inst .proxyService .getProxy (id );
224+ inst .client .testProxyReachable (proxy .getTargetId ());
225+
226+ // delete underlying container
227+ TestProxySharingScaler proxySharingScaler = inst .getBean ("proxySharingScaler_myApp" , TestProxySharingScaler .class );
228+ proxySharingScaler .disableCleanup ();
229+ IDelegateProxyStore delegateProxyStore = proxySharingScaler .getDelegateProxyStore ();
230+ DelegateProxy delegateProxy = delegateProxyStore .getDelegateProxy (proxy .getTargetId ());
231+ Container container = delegateProxy .getProxy ().getContainer (0 );
232+ dockerClient .removeContainer (container .getId (), DockerClient .RemoveContainerParam .forceKill ());
233+
234+ // try to access proxy
235+ Request request = new Request .Builder ()
236+ .get ()
237+ .url (inst .client .getBaseUrl () + "/api/route/" + proxy .getTargetId () + "/" )
238+ .build ();
239+
240+ try (Response response = inst .client .newCall (request ).execute ()) {
241+ Assertions .assertEquals (503 , response .code ());
242+ Assertions .assertEquals ("{\" status\" :\" fail\" ,\" data\" :\" app_crashed\" }" , response .body ().string ());
243+ }
244+
245+ // DelegateProxy should have no seats and marked for removal
246+ delegateProxy = delegateProxyStore .getDelegateProxy (proxy .getTargetId ());
247+ Assertions .assertEquals (DelegateProxyStatus .ToRemove , delegateProxy .getDelegateProxyStatus ());
248+ Assertions .assertTrue ( delegateProxy .getSeatIds ().isEmpty ());
249+
250+ // enable cleanup
251+ proxySharingScaler .enableCleanup ();
252+
253+ // old proxy should get cleaned up
254+ waitUntilNumberOfDelegateProxies (proxySharingScaler , 1 );
255+ DelegateProxy newDelegateProxy = delegateProxyStore .getAllDelegateProxies ().stream ().findFirst ().get ();
256+ // should be a different proxy, old one should have been removed
257+ Assertions .assertNotEquals (newDelegateProxy .getProxy ().getId (), delegateProxy .getProxy ().getId ());
258+ Assertions .assertNotEquals (newDelegateProxy .getProxy ().getTargetId (), delegateProxy .getProxy ().getTargetId ());
259+ Assertions .assertEquals (1 , newDelegateProxy .getSeatIds ().size ());
260+ }
261+ }
262+ }
176263
177- // TODO test re-use with more than one seats
178- // TODO test crashed
179264 // TODO test scaleDownDelay
180265 // TODO test minimumSeatsAvailable
181266 // TODO test config change
0 commit comments