3333import eu .openanalytics .containerproxy .model .runtime .runtimevalues .PublicPathKey ;
3434import eu .openanalytics .containerproxy .model .runtime .runtimevalues .TargetIdKey ;
3535import eu .openanalytics .containerproxy .test .helpers .ContainerSetup ;
36+ import eu .openanalytics .containerproxy .test .helpers .RedisServer ;
3637import eu .openanalytics .containerproxy .test .helpers .ShinyProxyInstance ;
3738import eu .openanalytics .containerproxy .test .helpers .TestHelperException ;
3839import eu .openanalytics .containerproxy .test .helpers .TestProxySharingScaler ;
5556import java .util .ArrayList ;
5657import java .util .List ;
5758import java .util .Map ;
59+ import java .util .Optional ;
5860import java .util .stream .Stream ;
5961
6062public class TestPreInitialization {
@@ -245,7 +247,7 @@ public void testDelegateProxyCrashed() throws DockerCertificateException, Docker
245247 // DelegateProxy should have no seats and marked for removal
246248 delegateProxy = delegateProxyStore .getDelegateProxy (proxy .getTargetId ());
247249 Assertions .assertEquals (DelegateProxyStatus .ToRemove , delegateProxy .getDelegateProxyStatus ());
248- Assertions .assertTrue ( delegateProxy .getSeatIds ().isEmpty ());
250+ Assertions .assertTrue (delegateProxy .getSeatIds ().isEmpty ());
249251
250252 // enable cleanup
251253 proxySharingScaler .enableCleanup ();
@@ -261,9 +263,85 @@ public void testDelegateProxyCrashed() throws DockerCertificateException, Docker
261263 }
262264 }
263265
266+ @ ParameterizedTest
267+ @ MethodSource ("backends" )
268+ public void testConfigChange (String backend , Map <String , String > properties ) {
269+ try (ContainerSetup containerSetup = new ContainerSetup (backend )) {
270+ try (RedisServer redisServer = new RedisServer ()) {
271+ // launch an instance and app
272+ String oldAppId ;
273+ try (ShinyProxyInstance inst = new ShinyProxyInstance ("application-test-pre-initialization-redis-1.yml" , properties , true )) {
274+ oldAppId = inst .client .startProxy ("myApp" );
275+ Proxy proxy = inst .proxyService .getProxy (oldAppId );
276+ inst .client .testProxyReachable (proxy .getTargetId ());
277+
278+ // target id should be different from proxy id
279+ Assertions .assertNotEquals (proxy .getTargetId (), proxy .getId ());
280+ Assertions .assertEquals ("/api/route/" + proxy .getTargetId () + "/" , proxy .getRuntimeValue (PublicPathKey .inst ));
281+ Assertions .assertEquals (proxy .getTargetId (), proxy .getRuntimeValue (TargetIdKey .inst ));
282+ Assertions .assertNotNull (proxy .getRuntimeValue (SeatIdKey .inst ));
283+
284+ }
285+ // re-start instance with updated app config
286+ try (ShinyProxyInstance inst = new ShinyProxyInstance ("application-test-pre-initialization-redis-2.yml" , properties , true )) {
287+ TestProxySharingScaler proxySharingScaler = inst .getBean ("proxySharingScaler_myApp" , TestProxySharingScaler .class );
288+ proxySharingScaler .disableCleanup ();
289+ IDelegateProxyStore delegateProxyStore = proxySharingScaler .getDelegateProxyStore ();
290+ inst .enableCleanup ();
291+
292+ Proxy proxy = inst .proxyService .getProxy (oldAppId );
293+ // old app should still exist
294+ Assertions .assertNotNull (proxy );
295+ // old app should still be reachable
296+ inst .client .testProxyReachable (proxy .getTargetId ());
297+
298+ DelegateProxy delegateProxy = delegateProxyStore .getDelegateProxy (proxy .getTargetId ());
299+ Assertions .assertEquals (DelegateProxyStatus .ToRemove , delegateProxy .getDelegateProxyStatus ());
300+ Assertions .assertEquals ("641fead76e0c432d0ae258a9745bcf69eac7b3c3" , delegateProxy .getProxySpecHash ());
301+
302+ // should create new instance with new hash
303+ waitUntilNumberOfDelegateProxies (proxySharingScaler , 3 );
304+
305+ // running DelegateProxy with old config should exist in DelegateProxyStore
306+ Optional <DelegateProxy > delegateProxyInUse = delegateProxyStore .getAllDelegateProxies ().stream ()
307+ .filter (it -> it .getProxy ().getId ().equals (proxy .getTargetId ()))
308+ .findFirst ();
309+ Assertions .assertTrue (delegateProxyInUse .isPresent ());
310+
311+ // a second DelegateProxy with old config should exist in DelegateProxyStore
312+ Optional <DelegateProxy > secondDelegateProxy = delegateProxyStore .getAllDelegateProxies ().stream ()
313+ .filter (it -> it .getProxySpecHash ().equals ("641fead76e0c432d0ae258a9745bcf69eac7b3c3" ) && !it .getProxy ().getId ().equals (proxy .getTargetId ()))
314+ .findFirst ();
315+ Assertions .assertTrue (secondDelegateProxy .isPresent ());
316+ Assertions .assertEquals (DelegateProxyStatus .ToRemove , secondDelegateProxy .get ().getDelegateProxyStatus ());
317+
318+ // a DelegateProxy with new config should exist in DelegateProxyStore
319+ Optional <DelegateProxy > newDelegateProxy = delegateProxyStore .getAllDelegateProxies ().stream ()
320+ .filter (it -> !it .getProxySpecHash ().equals ("641fead76e0c432d0ae258a9745bcf69eac7b3c3" ))
321+ .findFirst ();
322+ Assertions .assertTrue (newDelegateProxy .isPresent ());
323+ waitUntilDelegateProxyIsAvailable (proxySharingScaler , newDelegateProxy .get ().getProxy ().getId ());
324+ Assertions .assertEquals (DelegateProxyStatus .Available , newDelegateProxy .get ().getDelegateProxyStatus ());
325+ Assertions .assertEquals ("b21e966c35c7689f465f06722b13ec28cead0e33" , newDelegateProxy .get ().getProxySpecHash ());
326+
327+ // stop running app
328+ inst .client .stopProxy (oldAppId );
329+
330+ proxySharingScaler .enableCleanup ();
331+
332+ // proxies with old version should get cleaned up
333+ waitUntilNumberOfDelegateProxies (proxySharingScaler , 1 );
334+ DelegateProxy newDelegateProxy2 = delegateProxyStore .getAllDelegateProxies ().stream ().findFirst ().get ();
335+ Assertions .assertEquals (newDelegateProxy .get ().getProxy ().getId (), newDelegateProxy2 .getProxy ().getId ());
336+ Assertions .assertEquals ("b21e966c35c7689f465f06722b13ec28cead0e33" , newDelegateProxy2 .getProxySpecHash ());
337+ }
338+ }
339+ }
340+ }
341+
264342 // TODO test scaleDownDelay
265- // TODO test minimumSeatsAvailable
266- // TODO test config change
343+ // TODO test multiple seats
344+ // TODO test DelegateProxy structure
267345
268346 private void waitUntilNoPendingSeats (ProxySharingScaler proxySharingScaler ) {
269347 boolean noPendingSeats = Retrying .retry ((c , m ) -> {
@@ -286,4 +364,11 @@ private void waitUntilNumberOfDelegateProxies(TestProxySharingScaler proxySharin
286364 Assertions .assertTrue (noPendingSeats );
287365 }
288366
367+ private void waitUntilDelegateProxyIsAvailable (TestProxySharingScaler proxySharingScaler , String delegateProxyId ) {
368+ boolean noPendingSeats = Retrying .retry ((c , m ) -> {
369+ return proxySharingScaler .getDelegateProxyStore ().getDelegateProxy (delegateProxyId ).getDelegateProxyStatus () == DelegateProxyStatus .Available ;
370+ }, 60_000 , "assert number delegated proxies" , 1 , true );
371+ Assertions .assertTrue (noPendingSeats );
372+ }
373+
289374}
0 commit comments