@@ -398,6 +398,7 @@ | |||
<exclude>META-INF/*.RSA</exclude> | |||
<!-- Exclude other stuff by path --> | |||
<!-- after META we go alphabetically, noting exceptions along the way --> | |||
<exclude>META-INF/maven/**</exclude> | |||
<exclude>aj/org/objectweb/**</exclude> | |||
<exclude>com/ctc/wstx/dom/**</exclude> | |||
@@ -423,8 +424,18 @@ | |||
<exclude>com/ctc/wstx/shaded/msv_core/verifier/**</exclude> | |||
<exclude>com/ctc/wstx/shaded/msv_core/writer/**</exclude> | |||
<exclude>com/github/jmkgreen/**</exclude> | |||
<!-- <exclude>com/google/common/collect/**</exclude> needed during activation --> | |||
<!-- we can ALMOST exclude all of com/google/common/graph --> | |||
<!-- we run this command: --> | |||
<!-- jar tf bubble-server/target/bubble-server-1.0.0-SNAPSHOT.jar | grep com/google/common/graph | sed -e 's/\$.*/*.class/' | sort | uniq | grep -v './$' | grep -v SuccessorsFunction | while read ex ; do echo -n "<exclude>$ex</exclude>" ; done --> | |||
<!-- to create the line below, which lists everything in that package except for SuccessorsFunction --> | |||
<exclude>com/google/common/graph/AbstractBaseGraph*.class</exclude><exclude>com/google/common/graph/AbstractBaseGraph.class</exclude><exclude>com/google/common/graph/AbstractDirectedNetworkConnections*.class</exclude><exclude>com/google/common/graph/AbstractDirectedNetworkConnections.class</exclude><exclude>com/google/common/graph/AbstractGraph.class</exclude><exclude>com/google/common/graph/AbstractGraphBuilder.class</exclude><exclude>com/google/common/graph/AbstractNetwork*.class</exclude><exclude>com/google/common/graph/AbstractNetwork.class</exclude><exclude>com/google/common/graph/AbstractUndirectedNetworkConnections.class</exclude><exclude>com/google/common/graph/AbstractValueGraph*.class</exclude><exclude>com/google/common/graph/AbstractValueGraph.class</exclude><exclude>com/google/common/graph/BaseGraph.class</exclude><exclude>com/google/common/graph/DirectedGraphConnections*.class</exclude><exclude>com/google/common/graph/DirectedGraphConnections.class</exclude><exclude>com/google/common/graph/DirectedMultiNetworkConnections*.class</exclude><exclude>com/google/common/graph/DirectedMultiNetworkConnections.class</exclude><exclude>com/google/common/graph/DirectedNetworkConnections.class</exclude><exclude>com/google/common/graph/EdgesConnecting.class</exclude><exclude>com/google/common/graph/ElementOrder*.class</exclude><exclude>com/google/common/graph/ElementOrder.class</exclude><exclude>com/google/common/graph/EndpointPair*.class</exclude><exclude>com/google/common/graph/EndpointPair.class</exclude><exclude>com/google/common/graph/EndpointPairIterator*.class</exclude><exclude>com/google/common/graph/EndpointPairIterator.class</exclude><exclude>com/google/common/graph/ForwardingGraph.class</exclude><exclude>com/google/common/graph/ForwardingNetwork.class</exclude><exclude>com/google/common/graph/ForwardingValueGraph.class</exclude><exclude>com/google/common/graph/Graph.class</exclude><exclude>com/google/common/graph/GraphBuilder.class</exclude><exclude>com/google/common/graph/GraphConnections.class</exclude><exclude>com/google/common/graph/GraphConstants*.class</exclude><exclude>com/google/common/graph/GraphConstants.class</exclude><exclude>com/google/common/graph/Graphs*.class</exclude><exclude>com/google/common/graph/Graphs.class</exclude><exclude>com/google/common/graph/ImmutableGraph*.class</exclude><exclude>com/google/common/graph/ImmutableGraph.class</exclude><exclude>com/google/common/graph/ImmutableNetwork*.class</exclude><exclude>com/google/common/graph/ImmutableNetwork.class</exclude><exclude>com/google/common/graph/ImmutableValueGraph*.class</exclude><exclude>com/google/common/graph/ImmutableValueGraph.class</exclude><exclude>com/google/common/graph/IncidentEdgeSet.class</exclude><exclude>com/google/common/graph/MapIteratorCache*.class</exclude><exclude>com/google/common/graph/MapIteratorCache.class</exclude><exclude>com/google/common/graph/MapRetrievalCache*.class</exclude><exclude>com/google/common/graph/MapRetrievalCache.class</exclude><exclude>com/google/common/graph/MultiEdgesConnecting*.class</exclude><exclude>com/google/common/graph/MultiEdgesConnecting.class</exclude><exclude>com/google/common/graph/MutableGraph.class</exclude><exclude>com/google/common/graph/MutableNetwork.class</exclude><exclude>com/google/common/graph/MutableValueGraph.class</exclude><exclude>com/google/common/graph/Network.class</exclude><exclude>com/google/common/graph/NetworkBuilder.class</exclude><exclude>com/google/common/graph/NetworkConnections.class</exclude><exclude>com/google/common/graph/PredecessorsFunction.class</exclude><exclude>com/google/common/graph/StandardMutableGraph.class</exclude><exclude>com/google/common/graph/StandardMutableNetwork.class</exclude><exclude>com/google/common/graph/StandardMutableValueGraph.class</exclude><exclude>com/google/common/graph/StandardNetwork.class</exclude><exclude>com/google/common/graph/StandardValueGraph*.class</exclude><exclude>com/google/common/graph/StandardValueGraph.class</exclude><exclude>com/google/common/graph/Traverser*.class</exclude><exclude>com/google/common/graph/Traverser.class</exclude><exclude>com/google/common/graph/UndirectedGraphConnections*.class</exclude><exclude>com/google/common/graph/UndirectedGraphConnections.class</exclude><exclude>com/google/common/graph/UndirectedMultiNetworkConnections*.class</exclude><exclude>com/google/common/graph/UndirectedMultiNetworkConnections.class</exclude><exclude>com/google/common/graph/UndirectedNetworkConnections.class</exclude><exclude>com/google/common/graph/ValueGraph.class</exclude><exclude>com/google/common/graph/ValueGraphBuilder.class</exclude><exclude>com/google/common/graph/package-info.class</exclude> | |||
<!-- <exclude>com/google/graph/**</exclude> something needs SuccessorsFunction --> | |||
<exclude>com/opencsv/**</exclude> | |||
<exclude>com/sun/mail/imap/**</exclude> | |||
<exclude>com/twilio/rest/**</exclude> | |||
<exclude>io/jsonwebtoken/**</exclude> | |||
<exclude>javax/servlet/descriptor/**</exclude> | |||
@@ -446,6 +457,8 @@ | |||
<exclude>org/apache/fontbox/**</exclude> | |||
<exclude>org/apache/pdfbox/**</exclude> | |||
<!-- org/apache/poi stuff --> | |||
<!-- we can ALMOST exclude all of poi, we need the ZipSecureFile utility class --> | |||
<exclude>META-INF/services/org.apache.xmlbeans**</exclude> | |||
<exclude>com/microsoft/schemas/**</exclude> | |||
@@ -18,6 +18,7 @@ import bubble.service.upgrade.AppUpgradeService; | |||
import bubble.service.boot.StandardSelfNodeService; | |||
import bubble.service.cloud.StandardNetworkService; | |||
import bubble.service.notify.NotificationService; | |||
import bubble.service.upgrade.BubbleJarUpgradeService; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
@@ -35,6 +36,7 @@ public class NotificationHandler_hello_from_sage extends ReceivedNotificationHan | |||
@Autowired private BubbleNetworkDAO networkDAO; | |||
@Autowired private StandardNetworkService networkService; | |||
@Autowired private StandardSelfNodeService selfNodeService; | |||
@Autowired private BubbleJarUpgradeService jarUpgradeService; | |||
@Autowired private AppUpgradeService appUpgradeService; | |||
@Override public void handleNotification(ReceivedNotification n) { | |||
@@ -44,10 +46,13 @@ public class NotificationHandler_hello_from_sage extends ReceivedNotificationHan | |||
// First check to see if the sage reported a new jar version available | |||
if (payloadNode.hasSageVersion()) { | |||
log.info("handleNotification: payload node has sage version: "+payloadNode.getSageVersion()); | |||
configuration.setSageVersion(payloadNode.getSageVersion()); | |||
// start the app upgrade service, if not running | |||
if (!appUpgradeService.getIsAlive() && appUpgradeService.shouldRun()) appUpgradeService.start(); | |||
if (configuration.setSageVersion(payloadNode.getSageVersion())) { | |||
// run the jar upgrade service | |||
if (!jarUpgradeService.getIsAlive() && jarUpgradeService.shouldRun()) jarUpgradeService.startOrInterrupt(); | |||
} else { | |||
// start the app upgrade service, if not running | |||
if (!appUpgradeService.getIsAlive() && appUpgradeService.shouldRun()) appUpgradeService.startOrInterrupt(); | |||
} | |||
} | |||
final BubbleNode thisNode = configuration.getThisNode(); | |||
@@ -293,7 +293,7 @@ public class BubbleConfiguration extends PgRestServerConfiguration | |||
// For a Bubble node with a sage, this will be set in the hello_from_sage notification handler | |||
// For a Bubble without a sage, this will be set in NodeInitializerListener | |||
@Getter private BubbleVersionInfo sageVersion; | |||
public void setSageVersion(BubbleVersionInfo version) { | |||
public boolean setSageVersion(BubbleVersionInfo version) { | |||
sageVersion = version; | |||
final boolean isNewer = version == null ? false : isNewerVersion(getVersionInfo().getVersion(), sageVersion.getVersion()); | |||
if (!jarUpgradeAvailable && isNewer) { | |||
@@ -302,9 +302,10 @@ public class BubbleConfiguration extends PgRestServerConfiguration | |||
jarUpgradeAvailable = false; | |||
} | |||
refreshPublicSystemConfigs(); | |||
return jarUpgradeAvailable; | |||
} | |||
public boolean hasSageVersion () { return sageVersion != null; } | |||
@Getter private Boolean jarUpgradeAvailable = false; | |||
@Getter private volatile Boolean jarUpgradeAvailable = false; | |||
public boolean sameVersionAsSage () { | |||
return hasSageVersion() && getSageVersion().getVersion().equals(getVersionInfo().getVersion()); | |||
@@ -49,6 +49,7 @@ public class AppUpgradeService extends SimpleDaemon { | |||
@Autowired private BubbleAppDAO appDAO; | |||
@Autowired private RuleDriverDAO driverDAO; | |||
@Autowired private StandardRuleEngineService ruleEngine; | |||
@Autowired private BubbleJarUpgradeService jarUpgradeService; | |||
public boolean shouldRun () { | |||
final BubbleNetwork thisNetwork = configuration.getThisNetwork(); | |||
@@ -139,7 +140,13 @@ public class AppUpgradeService extends SimpleDaemon { | |||
return; | |||
} | |||
handleAdminUpgrades(admin, sageNode); | |||
Boolean enabled = null; | |||
try { | |||
enabled = jarUpgradeService.pause(); | |||
handleAdminUpgrades(admin, sageNode); | |||
} finally { | |||
if (enabled != null) jarUpgradeService.restore(enabled); | |||
} | |||
} | |||
private void handleAdminUpgrades(Account admin, BubbleNode sageNode) { | |||
@@ -19,10 +19,12 @@ import lombok.extern.slf4j.Slf4j; | |||
import org.cobbzilla.util.daemon.SimpleDaemon; | |||
import org.cobbzilla.wizard.cache.redis.RedisService; | |||
import org.joda.time.DateTime; | |||
import org.joda.time.DateTimeZone; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import java.io.File; | |||
import java.util.concurrent.atomic.AtomicBoolean; | |||
import static bubble.ApiConstants.HOME_DIR; | |||
import static java.util.concurrent.TimeUnit.MINUTES; | |||
@@ -48,6 +50,16 @@ public class BubbleJarUpgradeService extends SimpleDaemon { | |||
@Autowired private RedisService redis; | |||
@Autowired private StandardSelfNodeService selfNodeService; | |||
private final AtomicBoolean enabled = new AtomicBoolean(true); | |||
public void enable() { enabled.set(true); } | |||
public void disable() { enabled.set(false); } | |||
public boolean pause() { | |||
boolean b = enabled.get(); | |||
enabled.set(false); | |||
return b; | |||
} | |||
public void restore(boolean b) { enabled.set(b); } | |||
@Getter(lazy=true) private final RedisService nodeUpgradeRequests = redis.prefixNamespace(getClass().getName()); | |||
public String registerNodeUpgrade(String nodeUuid) { | |||
@@ -58,35 +70,71 @@ public class BubbleJarUpgradeService extends SimpleDaemon { | |||
public String getNodeForKey(String key) { return getNodeUpgradeRequests().get(key); } | |||
@Getter private final long sleepTime = MINUTES.toMillis(59); | |||
// For a node, when to run: | |||
// - delayed start | |||
// - sleep just less than one hour | |||
// - only run during one hour of the day, as measured by the local time | |||
@Override protected long getStartupDelay() { return MINUTES.toMillis(1); } | |||
@Override protected boolean canInterruptSleep() { return true; } | |||
@Getter private final long sleepTime = MINUTES.toMillis(2); | |||
// todo: make this configurable | |||
public static final int UPGRADE_HOUR_OF_DAY = 4; | |||
@Override protected void process() { | |||
final DateTime dateTime = new DateTime(now()); | |||
if (dateTime.hourOfDay().get() != UPGRADE_HOUR_OF_DAY) return; | |||
log.info("process: starting upgrade check"); | |||
if (!shouldRun()) return; | |||
log.info("process: checking/upgrading bubble jar..."); | |||
try { | |||
upgrade(); | |||
} catch (Exception e) { | |||
reportError("upgrade error: " + shortError(e), e); | |||
log.error("process: upgrade error: " + shortError(e)); | |||
} | |||
} | |||
public boolean shouldRun() { | |||
if (!enabled.get()) { | |||
log.warn("shouldRun: upgrades not currently enabled, returning"); | |||
return false; | |||
} | |||
if (!configuration.getJarUpgradeAvailable()) { | |||
log.warn("shouldRun: no upgrade available, returning"); | |||
return false; | |||
} | |||
// we shouldn't really need to adjust for the timezone here, because the Bubble | |||
// should have set the operating system timezone to be the same during ansible setup. | |||
// but just to be safe, use the bubble's time zone here. we have it handy. | |||
final DateTimeZone dtz = DateTimeZone.forID(configuration.getThisNetwork().getTimezone()); | |||
final DateTime dateTime = new DateTime(now(), dtz); | |||
final int hour = dateTime.hourOfDay().get(); | |||
if (hour != UPGRADE_HOUR_OF_DAY) { | |||
log.warn("shouldRun: hour of day ("+hour+") != UPGRADE_HOUR_OF_DAY ("+UPGRADE_HOUR_OF_DAY+"), returning"); | |||
return false; | |||
} | |||
// OK, it's that special hour of the day. does the admin even want updates? | |||
final Account account = accountDAO.getFirstAdmin(); | |||
if (account == null) return; | |||
if (account.getAutoUpdatePolicy().jarUpdates()) { | |||
log.info("process: automatic-upgrading bubble jar..."); | |||
try { | |||
upgrade(); | |||
} catch (Exception e) { | |||
reportError("upgrade error: " + shortError(e), e); | |||
log.error("process: upgrade error: " + shortError(e)); | |||
} | |||
if (account == null || !account.getAutoUpdatePolicy().jarUpdates()) { | |||
log.warn("shouldRun: account is null or auto-update policy does not allow jar updates"); | |||
return false; | |||
} | |||
log.info("shouldRun: returning true"); | |||
return true; | |||
} | |||
// set to 'false' for faster debugging of upgrade process | |||
private static final boolean BACKUP_BEFORE_UPGRADE = true; | |||
public synchronized void upgrade() { | |||
public synchronized boolean upgrade() { | |||
if (!configuration.getJarUpgradeAvailable()) { | |||
log.warn("upgrade: No upgrade available, returning"); | |||
return; | |||
log.warn("upgrade: no upgrade available, returning"); | |||
return false; | |||
} | |||
final BubbleVersionInfo sageVersion = configuration.getSageVersion(); | |||
@@ -104,17 +152,18 @@ public class BubbleJarUpgradeService extends SimpleDaemon { | |||
} | |||
if (bubbleBackup.getStatus() != BackupStatus.backup_completed) { | |||
log.warn("upgrade: timeout waiting for backup to complete, status=" + bubbleBackup.getStatus()); | |||
return; | |||
return false; | |||
} | |||
} | |||
final File upgradeJar = new File(HOME_DIR, "upgrade.jar"); | |||
if (upgradeJar.exists()) { | |||
log.error("upgrade: jar already exists, not upgrading: "+abs(upgradeJar)); | |||
return; | |||
return false; | |||
} | |||
final JarUpgradeMonitor jarUpgradeMonitor = selfNodeService.getJarUpgradeMonitorBean(); | |||
jarUpgradeMonitor.downloadJar(upgradeJar, sageVersion); | |||
return true; | |||
} | |||
} |
@@ -1 +1 @@ | |||
bubble.version=Adventure 1.4.49 | |||
bubble.version=Adventure 1.4.47 |
@@ -1 +1 @@ | |||
Subproject commit 356e928d6c308e5819284d37d9eb0d613e0c70ee | |||
Subproject commit d8cb733a4dbfe2d48881ecb6446b0ed4abbc8d04 |