James Moger
2011-09-27 4aafd4362caf198bdea97d2fdf2957aa5c345465
Poke test during proposal phase.
6 files modified
144 ■■■■ changed files
src/com/gitblit/Constants.java 14 ●●●●● patch | view | raw | blame | history
src/com/gitblit/FederationServlet.java 31 ●●●● patch | view | raw | blame | history
src/com/gitblit/GitBlit.java 3 ●●●● patch | view | raw | blame | history
src/com/gitblit/utils/FederationUtils.java 48 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/SendProposalPage.java 38 ●●●● patch | view | raw | blame | history
tests/com/gitblit/tests/FederationTests.java 10 ●●●●● patch | view | raw | blame | history
src/com/gitblit/Constants.java
@@ -115,7 +115,7 @@
     * Enumeration representing the types of federation requests.
     */
    public static enum FederationRequest {
        PROPOSAL, PULL_REPOSITORIES, PULL_USERS, PULL_SETTINGS, STATUS;
        POKE, PROPOSAL, PULL_REPOSITORIES, PULL_USERS, PULL_SETTINGS, STATUS;
        public static FederationRequest fromName(String name) {
            for (FederationRequest type : values()) {
@@ -181,4 +181,16 @@
        }
    }
    /**
     * Enumeration representing the possible results of federation proposal
     * requests.
     */
    public static enum FederationProposalResult {
        ERROR, FEDERATION_DISABLED, MISSING_DATA, NO_PROPOSALS, NO_POKE, ACCEPTED;
        @Override
        public String toString() {
            return name();
        }
    }
}
src/com/gitblit/FederationServlet.java
@@ -35,6 +35,7 @@
import com.gitblit.models.FederationProposal;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.FederationUtils;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
@@ -110,6 +111,16 @@
    private void processRequest(javax.servlet.http.HttpServletRequest request,
            javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
            java.io.IOException {
        FederationRequest reqType = FederationRequest.fromName(request.getParameter("req"));
        logger.info(MessageFormat.format("Federation {0} request from {1}", reqType,
                request.getRemoteAddr()));
        if (FederationRequest.POKE.equals(reqType)) {
            // Gitblit always responds to POKE requests to verify a connection
            logger.info("Received federation POKE from " + request.getRemoteAddr());
            return;
        }
        if (!GitBlit.getBoolean(Keys.git.enableGitServlet, true)) {
            logger.warn(Keys.git.enableGitServlet + " must be set TRUE for federation requests.");
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
@@ -123,11 +134,6 @@
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return;
        }
        String token = request.getParameter("token");
        FederationRequest reqType = FederationRequest.fromName(request.getParameter("req"));
        logger.info(MessageFormat.format("Federation {0} request from {1}", reqType,
                request.getRemoteAddr()));
        if (FederationRequest.PROPOSAL.equals(reqType)) {
            // Receive a gitblit federation proposal
@@ -156,6 +162,20 @@
                logger.error(MessageFormat.format("Rejected {0} federation proposal from {1}",
                        proposal.tokenType.name(), proposal.url));
                response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                return;
            }
            // poke the origin Gitblit instance that is proposing federation
            boolean poked = false;
            try {
                poked = FederationUtils.poke(proposal.url);
            } catch (Exception e) {
                logger.error("Failed to poke origin", e);
            }
            if (!poked) {
                logger.error(MessageFormat.format("Failed to send federation poke to {0}",
                        proposal.url));
                response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
                return;
            }
@@ -207,6 +227,7 @@
        }
        // Determine the federation tokens for this gitblit instance
        String token = request.getParameter("token");
        List<String> tokens = GitBlit.self().getFederationTokens();
        if (!tokens.contains(token)) {
            logger.warn(MessageFormat.format(
src/com/gitblit/GitBlit.java
@@ -971,7 +971,8 @@
     * @param proposal
     *            the proposal
     * @param gitblitUrl
     *            the url of your gitblit instance
     *            the url of your gitblit instance to send an email to
     *            administrators
     * @return true if the proposal was submitted
     */
    public boolean submitFederationProposal(FederationProposal proposal, String gitblitUrl) {
src/com/gitblit/utils/FederationUtils.java
@@ -45,6 +45,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.Constants.FederationProposalResult;
import com.gitblit.Constants.FederationRequest;
import com.gitblit.FederationServlet;
import com.gitblit.IStoredSettings;
@@ -82,7 +83,7 @@
    private static final SSLContext SSL_CONTEXT;
    private static final DummyHostnameVerifier HOSTNAME_VERIFIER;
    private static final Logger LOGGER = LoggerFactory.getLogger(FederationUtils.class);
    static {
@@ -182,21 +183,60 @@
    }
    /**
     * Sends a federation poke to the Gitblit instance at remoteUrl. Pokes are
     * sent by an pulling Gitblit instance to an origin Gitblit instance as part
     * of the proposal process. This is to ensure that the pulling Gitblit
     * instance has an IP route to the origin instance.
     *
     * @param remoteUrl
     *            the remote Gitblit instance to send a federation proposal to
     * @param proposal
     *            a complete federation proposal
     * @return true if there is a route to the remoteUrl
     */
    public static boolean poke(String remoteUrl) throws Exception {
        String url = FederationServlet.asFederationLink(remoteUrl, null, FederationRequest.POKE);
        Gson gson = new Gson();
        String json = gson.toJson("POKE");
        int status = writeJson(url, json);
        return status == HttpServletResponse.SC_OK;
    }
    /**
     * Sends a federation proposal to the Gitblit instance at remoteUrl
     * 
     * @param remoteUrl
     *            the remote Gitblit instance to send a federation proposal to
     * @param proposal
     *            a complete federation proposal
     * @return true if the proposal was received
     * @return the federation proposal result code
     */
    public static boolean propose(String remoteUrl, FederationProposal proposal) throws Exception {
    public static FederationProposalResult propose(String remoteUrl, FederationProposal proposal)
            throws Exception {
        String url = FederationServlet
                .asFederationLink(remoteUrl, null, FederationRequest.PROPOSAL);
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        String json = gson.toJson(proposal);
        int status = writeJson(url, json);
        return status == HttpServletResponse.SC_OK;
        switch (status) {
        case HttpServletResponse.SC_FORBIDDEN:
            // remote Gitblit Federation disabled
            return FederationProposalResult.FEDERATION_DISABLED;
        case HttpServletResponse.SC_BAD_REQUEST:
            // remote Gitblit did not receive any JSON data
            return FederationProposalResult.MISSING_DATA;
        case HttpServletResponse.SC_METHOD_NOT_ALLOWED:
            // remote Gitblit not accepting proposals
            return FederationProposalResult.NO_PROPOSALS;
        case HttpServletResponse.SC_NOT_ACCEPTABLE:
            // remote Gitblit failed to poke this Gitblit instance
            return FederationProposalResult.NO_POKE;
        case HttpServletResponse.SC_OK:
            // received
            return FederationProposalResult.ACCEPTED;
        default:
            return FederationProposalResult.ERROR;
        }
    }
    /**
src/com/gitblit/wicket/pages/SendProposalPage.java
@@ -26,6 +26,7 @@
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.CompoundPropertyModel;
import com.gitblit.Constants.FederationProposalResult;
import com.gitblit.GitBlit;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.RepositoryModel;
@@ -78,17 +79,44 @@
                    error("Please enter a destination url for your proposal!");
                    return;
                }
                // build new proposal
                FederationProposal proposal = GitBlit.self().createFederationProposal(myUrl, token);
                proposal.url = myUrl;
                proposal.message = message;
                try {
                    if (FederationUtils.propose(destinationUrl, proposal)) {
                        info(MessageFormat.format("Proposal successfully received by {0}.", destinationUrl));
                    FederationProposalResult res = FederationUtils
                            .propose(destinationUrl, proposal);
                    switch (res) {
                    case ACCEPTED:
                        info(MessageFormat.format("Proposal successfully received by {0}.",
                                destinationUrl));
                        setResponsePage(RepositoriesPage.class);
                    } else {
                        error(MessageFormat.format("Sorry, {0} rejected your proposal.", destinationUrl));
                        break;
                    case NO_POKE:
                        error(MessageFormat.format(
                                "Sorry, {0} could not find a Gitblit instance at {1}.",
                                destinationUrl, myUrl));
                        break;
                    case NO_PROPOSALS:
                        error(MessageFormat.format(
                                "Sorry, {0} is not accepting proposals at this time.",
                                destinationUrl));
                        break;
                    case FEDERATION_DISABLED:
                        error(MessageFormat
                                .format("Sorry, {0} is not configured to federate with any Gitblit instances.",
                                        destinationUrl));
                        break;
                    case MISSING_DATA:
                        error(MessageFormat.format("Sorry, {0} did not receive any proposal data!",
                                destinationUrl));
                        break;
                    case ERROR:
                        error(MessageFormat.format(
                                "Sorry, {0} reports that an unexpected error occurred!",
                                destinationUrl));
                        break;
                    }
                } catch (Exception e) {
                    if (!StringUtils.isEmpty(e.getMessage())) {
tests/com/gitblit/tests/FederationTests.java
@@ -24,6 +24,7 @@
import junit.framework.TestCase;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.FederationProposalResult;
import com.gitblit.Constants.FederationRequest;
import com.gitblit.Constants.FederationToken;
import com.gitblit.FederationServlet;
@@ -91,14 +92,15 @@
                "testtoken", repositories);
        // propose federation
        assertTrue("proposal refused",
                FederationUtils.propose("http://localhost:" + port, proposal));
        assertEquals("proposal refused",
                FederationUtils.propose("http://localhost:" + port, proposal),
                FederationProposalResult.NO_PROPOSALS);
    }
    public void testPullRepositories() throws Exception {
        try {
            String url = FederationServlet.asFederationLink("http://localhost:" + port, "testtoken",
                    FederationRequest.PULL_REPOSITORIES);
            String url = FederationServlet.asFederationLink("http://localhost:" + port,
                    "testtoken", FederationRequest.PULL_REPOSITORIES);
            String json = FederationUtils.readJson(url);
        } catch (IOException e) {
            if (!e.getMessage().contains("403")) {