2016-11-25 15:42:29 +01:00
package org.gcube.smartgears.managers ;
import static org.gcube.smartgears.Constants.container_profile_file_path ;
import static org.gcube.smartgears.lifecycle.container.ContainerState.active ;
import static org.gcube.smartgears.lifecycle.container.ContainerState.down ;
import static org.gcube.smartgears.lifecycle.container.ContainerState.failed ;
import static org.gcube.smartgears.lifecycle.container.ContainerState.stopped ;
import static org.gcube.smartgears.provider.ProviderFactory.provider ;
import java.io.File ;
import java.io.FileOutputStream ;
import java.io.ObjectOutputStream ;
import java.util.HashSet ;
import java.util.List ;
import java.util.Set ;
import org.gcube.common.authorization.client.exceptions.ObjectNotFound ;
import org.gcube.common.authorization.client.proxy.AuthorizationProxy ;
import org.gcube.common.authorization.library.AuthorizationEntry ;
import org.gcube.common.authorization.library.provider.ClientInfo ;
import org.gcube.common.authorization.library.provider.ContainerInfo ;
import org.gcube.common.events.Observes ;
import org.gcube.common.events.Observes.Kind ;
2022-02-07 09:44:31 +01:00
import org.gcube.smartgears.configuration.Mode ;
2016-11-25 15:42:29 +01:00
import org.gcube.smartgears.configuration.container.ContainerHandlers ;
import org.gcube.smartgears.context.application.ApplicationContext ;
import org.gcube.smartgears.context.container.ContainerContext ;
import org.gcube.smartgears.handlers.ProfileEvents ;
import org.gcube.smartgears.handlers.container.ContainerHandler ;
import org.gcube.smartgears.handlers.container.ContainerLifecycleEvent ;
import org.gcube.smartgears.handlers.container.ContainerPipeline ;
import org.gcube.smartgears.lifecycle.application.ApplicationLifecycle ;
import org.gcube.smartgears.lifecycle.container.ContainerState ;
import org.gcube.smartgears.utils.Utils ;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
/ * *
* Coordinates management of the container as a gCube resource .
*
* @author Fabio Simeoni
*
* /
public class ContainerManager {
private static Logger log = LoggerFactory . getLogger ( ContainerManager . class ) ;
public static ContainerManager instance = new ContainerManager ( ) ;
private AuthorizationProxy authProvider = provider ( ) . authorizationProxy ( ) ;
private ContainerContext context ;
private ContainerPipeline pipeline ;
private ContainerManager ( ) { }
/ * *
* Starts container management .
* /
public ContainerContext start ( ContainerContext context ) {
this . context = context ;
try {
2022-02-07 09:44:31 +01:00
2016-11-25 15:42:29 +01:00
2022-02-07 09:44:31 +01:00
if ( context . configuration ( ) . mode ( ) ! = Mode . offline )
validateContainer ( context ) ;
2016-11-25 15:42:29 +01:00
saveContainerState ( ) ;
ContainerHandlers handlers = provider ( ) . containerHandlers ( ) ;
log . trace ( " managing container lifecycle with {} " , handlers . get ( ) ) ;
startHandlers ( handlers . get ( ) ) ;
context . lifecycle ( ) . moveTo ( active ) ;
log . trace ( " loading keys for starting token ... " ) ;
//loadKeyForToken(context.configuration().startTokens());
log . trace ( " keys loaded for starting token ... " ) ;
return context ;
}
catch ( RuntimeException e ) {
log . error ( " cannot manage container (see cause) " , e ) ;
if ( context ! = null )
context . lifecycle ( ) . moveTo ( failed ) ;
throw e ;
}
}
private void saveContainerState ( ) {
File file = context . configuration ( ) . persistence ( ) . file ( container_profile_file_path ) ;
try ( ObjectOutputStream oos = new ObjectOutputStream ( new FileOutputStream ( file ) ) ) {
oos . writeObject ( context . id ( ) ) ;
oos . writeObject ( context . configuration ( ) . startTokens ( ) ) ;
} catch ( Exception e ) {
log . error ( " error serializing cointainer state " ) ;
throw new RuntimeException ( e ) ;
}
}
private void validateContainer ( ContainerContext context ) {
2020-01-23 15:24:19 +01:00
//List<String> tokensToRemove = new ArrayList<String>();
2022-02-07 09:44:31 +01:00
context . configuration ( ) . validate ( ) ;
2016-11-25 15:42:29 +01:00
Set < String > foundContexts = new HashSet < String > ( ) ;
2020-01-23 15:24:19 +01:00
try {
List < AuthorizationEntry > entries = authProvider . get ( context . configuration ( ) . startTokens ( ) ) ;
log . info ( " requesting auth on {} tokens returned {} entries " , context . configuration ( ) . startTokens ( ) . size ( ) , entries . size ( ) ) ;
for ( AuthorizationEntry entry : entries ) {
log . info ( " the container will be started in context {} " , entry . getContext ( ) ) ;
foundContexts . add ( entry . getContext ( ) ) ;
}
} catch ( Exception e ) {
log . error ( " error contacting auth service on container " , e ) ;
}
2016-11-25 15:42:29 +01:00
if ( foundContexts . isEmpty ( ) ) {
log . error ( " no valid starting token are specified, moving the container to failed " ) ;
throw new RuntimeException ( " no valid starting token are specified " ) ;
}
2020-01-23 15:24:19 +01:00
//context.configuration().startTokens().removeAll(tokensToRemove);
2016-11-25 15:42:29 +01:00
context . configuration ( ) . allowedContexts ( foundContexts ) ;
}
private String resolveTokenForAdd ( Set < String > alreadyAddedContext , String token ) {
try {
AuthorizationEntry entry = authProvider . get ( token ) ;
ClientInfo info = entry . getClientInfo ( ) ;
log . info ( " resolved authorization entry for container {} " , entry ) ;
if ( alreadyAddedContext . contains ( entry . getContext ( ) ) ) {
log . warn ( " the token {} cannot be used, another token with the same context {} found " , entry . getContext ( ) ) ;
} else if ( ! entry . getContext ( ) . startsWith ( " / " + context . configuration ( ) . infrastructure ( ) ) ) {
log . warn ( " the token {} cannot be used, is not in the infrastructure {} of the container " , token , context . configuration ( ) . infrastructure ( ) ) ;
} else if ( ! ( info instanceof ContainerInfo ) ) {
log . warn ( " the token {} cannot be used, is not for a container token " , token ) ;
} else if ( ! ( ( ContainerInfo ) info ) . getHost ( ) . equals ( context . configuration ( ) . hostname ( ) )
| | context . configuration ( ) . port ( ) ! = ( ( ContainerInfo ) info ) . getPort ( ) ) {
log . warn ( " the token {} cannot be used, the client id {} resolved with the token is not the same of the one specified in this container " , token , info . getId ( ) ) ;
} else
return entry . getContext ( ) ;
} catch ( ObjectNotFound onf ) {
log . error ( " token {} not valid " , token ) ;
} catch ( Exception e ) {
log . error ( " error contacting authorization for token {} " , token , e ) ;
}
return null ;
}
public void manage ( ApplicationContext app ) {
app . events ( ) . subscribe ( this ) ;
}
@Observes ( value = { ApplicationLifecycle . failure , ApplicationLifecycle . stop } , kind = Kind . critical )
void monitorApplication ( ApplicationLifecycle lifecycle ) {
context . lifecycle ( ) . tryMoveTo ( ContainerState . partActive ) ;
}
@Observes ( value = ContextEvents . ADD_TOKEN_TO_CONTAINER , kind = Kind . critical )
void addToken ( String token ) {
log . trace ( " adding token {} to container " , token ) ;
String newContext ;
if ( ( newContext = resolveTokenForAdd ( context . configuration ( ) . allowedContexts ( ) , token ) ) ! = null ) {
context . configuration ( ) . startTokens ( ) . add ( token ) ;
context . configuration ( ) . allowedContexts ( ) . add ( newContext ) ;
saveContainerState ( ) ;
//loadKeyForToken(Arrays.asList(token));
context . events ( ) . fire ( token , ContextEvents . ADD_TOKEN_TO_APPLICATION ) ;
context . events ( ) . fire ( token , ProfileEvents . addToContext ) ;
log . trace ( " token added and event fired " ) ;
} else log . warn ( " trying to add an invalid token " ) ;
}
@Observes ( value = ContextEvents . REMOVE_TOKEN_FROM_CONTAINER , kind = Kind . critical )
void removeToken ( String token ) {
log . trace ( " removing token {} from container " , token ) ;
AuthorizationEntry entry ;
try {
entry = authProvider . get ( token ) ;
} catch ( Exception e ) {
log . error ( " error resolving token to remove " ) ;
return ;
}
if ( context . configuration ( ) . startTokens ( ) . contains ( token ) ) {
context . configuration ( ) . startTokens ( ) . remove ( token ) ;
context . configuration ( ) . allowedContexts ( ) . remove ( entry . getContext ( ) ) ;
saveContainerState ( ) ;
context . events ( ) . fire ( token , ContextEvents . REMOVE_TOKEN_FROM_APPLICATION ) ;
context . events ( ) . fire ( token , ProfileEvents . removeFromContext ) ;
log . trace ( " token removed and event fired " ) ;
} else log . warn ( " cannot remove token, it is not present in the container " ) ;
}
/ * *
* Stops container management on remote request .
*
* /
public void stop ( ) {
stop ( false ) ;
}
/ * *
* Stops container management on remote request or container shutdown .
*
* /
public void stop ( boolean shutdown ) {
//two cases: stop-on-shutdown and stop-on-request, some listeners will be selective about this,
//shutdown is triggered by probe app, which is notified among other apps
//if other app have been already notified, the container may already be part-active.
//apps still to notify will listen only on stop, hence won't react to this but will go down when their turn arrives.
if ( context = = null )
return ;
log . info ( " stopping container management " ) ;
try {
context . lifecycle ( ) . tryMoveTo ( shutdown ? down : stopped ) ;
stopHandlers ( ) ;
//no further reactions
log . info ( " stopping container events " ) ;
context . events ( ) . stop ( ) ;
Utils . scheduledServicePool . shutdownNow ( ) ;
}
catch ( RuntimeException e ) {
log . warn ( " cannot stop container management (see cause) " , e ) ;
}
}
//helpers
private void startHandlers ( List < ContainerHandler > handlers ) {
try {
pipeline = new ContainerPipeline ( handlers ) ;
pipeline . forward ( new ContainerLifecycleEvent . Start ( context ) ) ;
} catch ( RuntimeException e ) {
context . lifecycle ( ) . tryMoveTo ( failed ) ;
throw e ;
}
}
private void stopHandlers ( ) {
if ( pipeline = = null )
return ;
// copy pipeline, flip it, and
ContainerPipeline returnPipeline = pipeline . reverse ( ) ;
// start lifetime pipeline in inverse order with stop event
returnPipeline . forward ( new ContainerLifecycleEvent . Stop ( context ) ) ;
}
/ *
private void loadKeyForToken ( List < String > tokens ) {
String initialToken = SecurityTokenProvider . instance . get ( ) ;
//TODO: change this
String filePath = " /tmp/keys " ;
File PathDirs = new File ( filePath + " / " ) ;
PathDirs . mkdirs ( ) ;
try {
for ( String token : tokens ) {
try {
SecurityTokenProvider . instance . set ( token ) ;
File key = authProvider . getSymmKey ( filePath ) ;
log . trace ( " loading key {} file name " , key . getAbsolutePath ( ) ) ;
log . trace ( " loaded key {} file name " , key . getAbsolutePath ( ) ) ;
} catch ( Exception e ) {
log . warn ( " error loading key for token {} " , token , e ) ;
}
}
loadFileIntoClasspath ( PathDirs ) ;
} finally {
SecurityTokenProvider . instance . set ( initialToken ) ;
}
}
private void loadFileIntoClasspath ( File file ) {
try {
URL url = file . toURI ( ) . toURL ( ) ;
ClassLoader currentClassloader = Thread . currentThread ( ) . getContextClassLoader ( ) . getParent ( ) = = null ?
Thread . currentThread ( ) . getContextClassLoader ( ) : Thread . currentThread ( ) . getContextClassLoader ( ) . getParent ( ) ;
URLClassLoader classLoader = ( URLClassLoader ) currentClassloader ;
Method method = URLClassLoader . class . getDeclaredMethod ( " addURL " , URL . class ) ;
method . setAccessible ( true ) ;
method . invoke ( classLoader , url ) ;
} catch ( Exception ex ) {
log . error ( " error loading file into classpath " , ex ) ;
}
}
* /
}