Skip to content

Commit

Permalink
Project list is not updating in desired interval
Browse files Browse the repository at this point in the history
Fix bug whereby project list updating was not active upon connection startup.

Only applies for Gerrit servers < 2.12.

[FIXED JENKINS-31473]
  • Loading branch information
Scott Hebert committed Nov 27, 2015
1 parent 3bfe912 commit f3b31e3
Show file tree
Hide file tree
Showing 6 changed files with 404 additions and 20 deletions.
Expand Up @@ -37,6 +37,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -53,9 +54,8 @@ public class GerritProjectListUpdater extends Thread implements ConnectionListen
* The command for fetching projects.
*/
public static final String GERRIT_LS_PROJECTS = "gerrit ls-projects";
private boolean useIncrementalUpdate;

private boolean connected = false;
private AtomicBoolean connected = new AtomicBoolean(false);
private boolean shutdown = false;
private static final Logger logger = LoggerFactory.getLogger(GerritProjectListUpdater.class);
private List<String> gerritProjects;
Expand All @@ -64,10 +64,8 @@ public class GerritProjectListUpdater extends Thread implements ConnectionListen
/**
* Default constructor.
* @param serverName the name of the Gerrit server.
* @param hasProjectCreatedEvents true if Gerrit server has project-created events.
*/
public GerritProjectListUpdater(String serverName, boolean hasProjectCreatedEvents) {
this.useIncrementalUpdate = hasProjectCreatedEvents;
public GerritProjectListUpdater(String serverName) {
this.setName(this.getClass().getName() + " for " + serverName + " Thread");
this.setDaemon(true);
this.serverName = serverName;
Expand All @@ -85,7 +83,7 @@ private void addThisAsListener() {
GerritServer server = plugin.getServer(serverName);
if (server != null) {
server.addListener(this.connectionListener());
connected = server.isConnected();
connected.set(server.isConnected());
} else {
logger.error("Could not find the server {}", serverName);
}
Expand Down Expand Up @@ -128,6 +126,7 @@ public void gerritEvent(GerritEvent gerritEvent) {
*/
public void gerritEvent(ProjectCreated gerritEvent) {
addGerritProject(gerritEvent.getProjectName());
logger.debug("Added project {} to project lists", gerritEvent.getProjectName());
}

/**
Expand All @@ -150,9 +149,10 @@ public void run() {
waitFor(getConfig().getProjectListFetchDelay());
tryLoadProjectList();
}
if (useIncrementalUpdate) {
listenToProjectCreatedEvents();

if (listenToProjectCreatedEvents()) {
logger.info("ProjectCreated events supported by Gerrit Server {}. "
+ "Will now listen for new projects...", serverName);
} else {
while (!shutdown) {
waitFor(getConfig().getProjectListRefreshInterval());
Expand All @@ -162,28 +162,33 @@ public void run() {
}

/**
* Add this as GerritEventListener.
* Add this as GerritEventListener if project events supported.
* @return true is project created events are supported and listener is added.
*/
private void listenToProjectCreatedEvents() {
private boolean listenToProjectCreatedEvents() {
// Listen to project-created events.
PluginImpl plugin = PluginImpl.getInstance();
if (plugin != null) {
GerritServer server = plugin.getServer(serverName);
if (server != null) {
// If run was called before.
server.removeListener(this.gerritEventListener());
server.addListener(this.gerritEventListener());
if (server.isProjectCreatedEventsSupported()) {
// If run was called before.
server.removeListener(this.gerritEventListener());
server.addListener(this.gerritEventListener());
return true;
}
} else {
logger.error("Could not find the server {} to add GerritEventListener.", serverName);
}
}
return false;
}

/**
* Wait for 'delay' seconds.
* @param delay seconds to wait.
*/
private void waitFor(int delay) {
private void waitFor(long delay) {
try {
synchronized (this) {
long startTime = System.nanoTime();
Expand Down Expand Up @@ -281,14 +286,14 @@ public static List<String> readProjects(Reader commandReader) throws IOException
* @return if connected to Gerrit.
*/
public synchronized boolean isConnected() {
return connected;
return connected.get();
}

/**
* @param connected the connected to set.
*/
public synchronized void setConnected(boolean connected) {
this.connected = connected;
this.connected.set(connected);
}

/**
Expand Down
Expand Up @@ -406,8 +406,7 @@ public void start() {
initializeConnectionListener();

projectListUpdater =
new GerritProjectListUpdater(
name, isProjectCreatedEventsSupported());
new GerritProjectListUpdater(name);
projectListUpdater.start();

missedEventsPlaybackManager.checkIfEventsLogPluginSupported();
Expand Down Expand Up @@ -631,10 +630,16 @@ public boolean isReplicationEventsSupported() {

/**
* Checks whether the current server support project-created events or not.
*
* Note: We need to exclude snapshot versions from this check. Otherwise, snapshot versions
* that are < Gerrit 2.12 will default to waiting for Project Created events which are only
* supported in Gerrit >= 2.12.
*
* @return true if project-created events are supported, otherwise false
*/
public boolean isProjectCreatedEventsSupported() {
return GerritVersionChecker.isCorrectVersion(GerritVersionChecker.Feature.projectCreatedEvents, name);
return GerritVersionChecker.isCorrectVersion(GerritVersionChecker.Feature.projectCreatedEvents, name,
true);
}

/**
Expand Down
Expand Up @@ -133,6 +133,37 @@ public static boolean isCorrectVersion(Feature feature, String serverName) {
}
}

/**
* Tells us if we are running the correct version for a particular feature.
*
* @param feature the feature we want to check.
* @param serverName the name of the Gerrit server.
* @param excludeSnapshotVersions exclude snapshot versions from feature checks.
* @return true if the Gerrit version is high enough for us to use this feature.
*/
public static boolean isCorrectVersion(Feature feature, String serverName,
boolean excludeSnapshotVersions) {
if (PluginImpl.getInstance() != null) {
if (serverName == null || serverName.isEmpty()
|| GerritServer.ANY_SERVER.equals(serverName)) {
for (GerritServer server : PluginImpl.getServers_()) {
GerritVersionNumber gerritVersion
= createVersionNumber(server.getGerritVersion());
if (isCorrectVersion(gerritVersion, feature, excludeSnapshotVersions)) {
return true;
}
}
return false;
} else {
GerritVersionNumber gerritVersion
= createVersionNumber(getGerritVersion(serverName));
return isCorrectVersion(gerritVersion, feature, excludeSnapshotVersions);
}
} else {
return false;
}
}

/**
*Returns the current Gerrit version.
*@param serverName the name of the server.
Expand All @@ -153,6 +184,23 @@ private static String getGerritVersion(String serverName) {
return null;
}

/**
* Tells us if we are running the correct version for a particular feature.
*
* @param gerritVersion the version of Gerrit we are running.
* @param feature the feature we want to check.
* @param excludeSnapshotVersions exclude snapshots from feature check.
* @return true if the Gerrit version is high enough for us to use this feature.
*/
public static boolean isCorrectVersion(GerritVersionNumber gerritVersion,
Feature feature, boolean excludeSnapshotVersions) {
if (excludeSnapshotVersions) {
return !feature.versionNumber.isNewerThan(gerritVersion);
} else {
return (gerritVersion.isSnapshot() || !feature.versionNumber.isNewerThan(gerritVersion));
}
}

/**
* Tells us if we are running the correct version for a particular feature.
*
Expand All @@ -161,7 +209,7 @@ private static String getGerritVersion(String serverName) {
* @return true if the Gerrit version is high enough for us to use this feature.
*/
public static boolean isCorrectVersion(GerritVersionNumber gerritVersion, Feature feature) {
return (gerritVersion.isSnapshot() || !feature.versionNumber.isNewerThan(gerritVersion));
return isCorrectVersion(gerritVersion, feature, false);
}

/**
Expand Down
@@ -0,0 +1,140 @@
/*
* The MIT License
*
* Copyright 2015 Ericsson All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.sonyericsson.hudson.plugins.gerrit.trigger;

import com.sonyericsson.hudson.plugins.gerrit.trigger.mock.MockConfigForProjectListTest;
import com.sonymobile.tools.gerrit.gerritevents.mock.SshdServerMock;

import static org.junit.Assert.assertEquals;

import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.time.StopWatch;
import org.apache.sshd.SshServer;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;

import static com.sonymobile.tools.gerrit.gerritevents.mock.SshdServerMock.GERRIT_STREAM_EVENTS;
//CS IGNORE AvoidStarImport FOR NEXT 1 LINES. REASON: UnitTest.

/**
* Functional Test for Project List Updater.
*
*/
public class GerritProjectListUpdaterFunctionalTest {

/**
* An instance of Jenkins Rule.
*/
// CS IGNORE VisibilityModifier FOR NEXT 2 LINES. REASON: JenkinsRule.
@Rule
public final JenkinsRule j = new JenkinsRule();

private static final int SLEEPTIME = 1;
private static final int LONGSLEEPTIME = 10;

private static final long MAXSLEEPTIME = 10;

private final int port = 29418;

private SshdServerMock server;
private SshServer sshd;
private SshdServerMock.KeyPairFiles sshKey;

/**
* Runs before test method.
*
* @throws Exception throw if so.
*/
@Before
public void setUp() throws Exception {
sshKey = SshdServerMock.generateKeyPair();
System.setProperty(PluginImpl.TEST_SSH_KEYFILE_LOCATION_PROPERTY, sshKey.getPrivateKey().getAbsolutePath());
server = new SshdServerMock();
sshd = SshdServerMock.startServer(port, server);
// We need to do this so that subsequent calls will be served with another command define later
server.returnCommandFor("gerrit ls-projects", SshdServerMock.SendOneProjectCommand.class,
true, new Object[0], new Class<?>[0]);
server.returnCommandFor(GERRIT_STREAM_EVENTS, SshdServerMock.CommandMock.class);
server.returnCommandFor("gerrit review.*", SshdServerMock.EofCommandMock.class);
server.returnCommandFor("gerrit version", SshdServerMock.SendVersionCommand.class);
}

/**
* Runs after test method.
*
* @throws Exception throw if so.
*/
@After
public void tearDown() throws Exception {
sshd.stop(true);
sshd = null;
}

/**
* Test that Project List updates are active for Gerrit Version 2.11 upon
* connection startup.
* @throws Exception if occurs.
*/
@Test
public void testProjectListUpdateActiveOnStartup() throws Exception {
GerritServer gerritServer = new GerritServer("ABCDEF");
PluginImpl.getInstance().addServer(gerritServer);
MockConfigForProjectListTest config = new MockConfigForProjectListTest();
config.setGerritAuthKeyFile(sshKey.getPrivateKey());
gerritServer.setConfig(config);
gerritServer.start();
gerritServer.startConnection();

StopWatch watch = new StopWatch();
watch.start();
while (!gerritServer.isConnected()) {
TimeUnit.SECONDS.sleep(SLEEPTIME);
if (TimeUnit.MILLISECONDS.toSeconds(watch.getTime()) > MAXSLEEPTIME) {
break;
}
}
watch.stop();

watch.reset();
watch.start();
while (gerritServer.getGerritProjects().size() == 0) {
TimeUnit.SECONDS.sleep(SLEEPTIME);
if (TimeUnit.MILLISECONDS.toSeconds(watch.getTime()) > MAXSLEEPTIME) {
break;
}
}
watch.stop();
assertEquals(1, gerritServer.getGerritProjects().size());

server.returnCommandFor("gerrit ls-projects", SshdServerMock.SendTwoProjectsCommand.class);

TimeUnit.SECONDS.sleep(LONGSLEEPTIME);
assertEquals(2, gerritServer.getGerritProjects().size());

}
}

0 comments on commit f3b31e3

Please sign in to comment.