]> source.dussan.org Git - gitblit.git/commitdiff
Federation pull_scripts request. Documentation.
authorJames Moger <james.moger@gitblit.com>
Wed, 4 Jan 2012 13:42:54 +0000 (08:42 -0500)
committerJames Moger <james.moger@gitblit.com>
Wed, 4 Jan 2012 13:42:54 +0000 (08:42 -0500)
docs/00_index.mkd
docs/01_features.mkd
docs/02_federation.mkd
docs/04_releases.mkd
src/com/gitblit/Constants.java
src/com/gitblit/FederationPullExecutor.java
src/com/gitblit/FederationServlet.java
src/com/gitblit/GitBlit.java
src/com/gitblit/utils/FederationUtils.java
src/com/gitblit/utils/JsonUtils.java
tests/com/gitblit/tests/FederationTests.java

index bee0635ed1ceb1975ab3dbb783ef1077692cf311..df3ac850c651c924f60eacae321ba561bc210c69 100644 (file)
@@ -73,7 +73,7 @@ Administrators can create and manage all repositories, user accounts, and teams
 \r
 ### Backup Strategy\r
 \r
-Gitblit includes a backup mechanism (*federation*) which can be used to backup repositories and, optionally, user accounts & server settings from your Gitblit instance to another Gitblit instance or to a [Gitblit Federation Client](http://code.google.com/p/gitblit/downloads/detail?name=%FEDCLIENT%).  Similarly, you can use the federation mechanism to aggregate individual workspace Gitblit instances to a common, centralized server.\r
+Gitblit includes a backup mechanism (*federation*) which can be used to backup repositories and, optionally, user accounts, team definitions, server settings, & Groovy push hook scripts from your Gitblit instance to another Gitblit instance or to a [Gitblit Federation Client](http://code.google.com/p/gitblit/downloads/detail?name=%FEDCLIENT%).  Similarly, you can use the federation mechanism to aggregate individual workspace Gitblit instances to a common, centralized server.\r
 \r
 ### Java Runtime Requirement\r
 \r
index 7d33911b6d4a065282ddce90bb460bcd427b4cd2..a5856a1ca419a5bef0673bcb2361ae87e5d48f21 100644 (file)
@@ -17,6 +17,7 @@
 - Administrators may create, edit, rename, or delete users through the web UI or RPC interface\r
 - Administrators may create, edit, rename, or delete teams through the web UI or RPC interface\r
 - Repository Owners may edit repositories through the web UI\r
+- Gravatar integration\r
 - Git-notes display support\r
 - Branch metrics (uses Google Charts)\r
 - HEAD and Branch RSS feeds\r
index 6525000e2cb78d4338fb4bf71a879f7a13863453..0aaae4fa4ec9bbbe22b68927075ae3e9cc1e2d2a 100644 (file)
@@ -13,7 +13,7 @@ Please review all the documentation to understand how it works and its limitatio
 \r
 ### Important Changes to Note\r
 \r
-The *Gitblit 0.8.0* federation protocol adds retrieval of team definitions.  Older clients will not know to request team information. \r
+The *Gitblit 0.8.0* federation protocol adds retrieval of teams and referenced push scripts.  Older clients will not know to request team or push script information. \r
 \r
 The *Gitblit 0.7.0* federation protocol is incompatible with the 0.6.0 federation protocol because of a change in the way timestamps are formatted.\r
 \r
@@ -54,7 +54,7 @@ String usersAndRepositoriesToken = SHA1(passphrase + "-USERS_AND_REPOSITORIES");
 String repositoriesToken = SHA1(passphrase + "-REPOSITORIES");\r
 %ENDCODE%\r
     \r
-The *ALL* token allows another Gitblit instance to pull all your repositories, user accounts, and server settings.  \r
+The *ALL* token allows another Gitblit instance to pull all your repositories, user accounts, server settings, and referenced push scripts.  \r
 The *USERS_AND_REPOSITORIES* token allows another Gitblit instance to pull all your repositories and  user accounts.  \r
 The *REPOSITORIES* token only allows pulling of the repositories.\r
 \r
@@ -173,9 +173,17 @@ The pulling Gitblit instance will store a registration-specific `gitblit.propert
 \r
 These settings are unused by the pulling Gitblit instance.\r
 \r
+#### Push Scripts \r
+\r
+Your Groovy push scripts are only pulled when using the *ALL* token.\r
+\r
+The pulling Gitblit instance will retrieve any referenced (i.e. used) push script and store it locally as *registration_scriptName.groovy* in the *federation.N.folder* folder.\r
+\r
+These scripts are unused by the pulling Gitblit instance.\r
+\r
 ### Collisions and Conflict Resolution\r
 \r
-Gitblit does **not** detect conflict and it does **not** offer conflict resolution of repositories, users, or settings.\r
+Gitblit does **not** detect conflict and it does **not** offer conflict resolution of repositories, users, teams, or settings.\r
 \r
 If an object exists locally that has the same name as the remote object, it is assumed they are the same and the contents of the remote object are merged into the local object.  If you can not guarantee that this is the case, then you should not store any federated repositories directly in *git.repositoriesFolder* and you should not enable *mergeAccounts*.\r
 \r
@@ -250,9 +258,9 @@ These examples would be entered into the `gitblit.properties` file of the pullin
 \r
 This assumes that the *token* is the *ALL* token from the origin gitblit instance.\r
 \r
-The repositories, example1_users.conf, and example1_gitblit.properties will be put in *git.repositoriesFolder* and the origin user accounts will be merged into the local user accounts, including passwords and all roles.  The Gitblit instance will also send a status acknowledgment to the origin Gitblit instance at the end of the pull operation.  The status report will include the state of each repository pull (EXCLUDED, SKIPPED, NOCHANGE, PULLED, MIRRORED).  This way the origin Gitblit instance can monitor the health of its mirrors.\r
+The repositories, example1_users.conf, example1_gitblit.propertiesn and all example1_scripts.groovy will be put in *git.repositoriesFolder* and the origin user accounts will be merged into the local user accounts, including passwords and all roles.  The Gitblit instance will also send a status acknowledgment to the origin Gitblit instance at the end of the pull operation.  The status report will include the state of each repository pull (EXCLUDED, SKIPPED, NOCHANGE, PULLED, MIRRORED).  This way the origin Gitblit instance can monitor the health of its mirrors.\r
 \r
-This example is considered *nearly* perfect because while the origin Gitblit's server settings are pulled and saved locally, they are not merged with your server settings so its not a true mirror, but its likely the mirror you'd want to configure.\r
+This example is considered *nearly* perfect because while the origin Gitblit's server settings & push scripts are pulled and saved locally, they are not merged with your server settings so its not a true mirror.\r
 \r
     federation.example1.url = https://go.gitblit.com\r
     federation.example1.token = 6f3b8a24bf970f17289b234284c94f43eb42f0e4\r
index db47d4164d181debd21e593c876f405309321902..b87ebbb04dfcc5f33d76d68635281b7aef7747e9 100644 (file)
@@ -5,7 +5,7 @@
 \r
 - updated: Gitblit GO is now monolithic like the WAR build. (issue 30)  \r
 This change helps adoption of GO in environments without an internet connection or with a restricted connection.\r
-- added: Groovy 1.8.4 and a push hook script mechanism.  Hook scripts can be set per-repository, pre-team, or globally for all repositories.  \r
+- added: Groovy 1.8.4 and a push hook script mechanism.  Hook scripts can be set per-repository, per-team, or globally for all repositories.  \r
 Unfortunately this adds another 6 MB to the 8MB Gitblit package, but it allows for a *very* powerful, flexible, platform-independent hook script mechanism.  \r
     **New:** *groovy.scriptsFolder = groovy*  \r
     **New:** *groovy.preReceiveScripts =*  \r
@@ -18,11 +18,11 @@ Unfortunately this adds another 6 MB to the 8MB Gitblit package, but it allows f
 This user service implementation allows for serialization and deserialization of more sophisticated Gitblit User objects and will open the door for more advanced Gitblit features. For upgrading installations, a `users.conf` file will automatically be created for you from your existing `users.properties` file on your first launch of Gitblit.  You will have to manually set *realm.userService=users.conf* to switch to the new user service.  \r
 The original `users.properties` file and it's corresponding implementation are **deprecated**.  \r
     **New:** *realm.userService = users.conf*\r
-- added: Teams for specifying user-repository access in bulk\r
+- added: Teams for specifying user-repository access in bulk.  Teams may also specify mailing lists addresses and pre- & post- receive hook scripts.\r
 - added: Gitblit Express bundle to get started running Gitblit on RedHat's OpenShift cloud\r
 - added: optional Gravatar integration  \r
     **New:** *web.allowGravatar = true*   \r
-- added: multi-repository activity page.  this is a timeline of commit activity over the last N days for one or more repositories.  \r
+- added: aggregated repository activity page.  this is a timeline of commit activity over the last N days for one or more repositories.  \r
    **New:** *web.activityDuration = 14*  \r
    **New:** *web.timeFormat = HH:mm*  \r
    **New:** *web.datestampLongFormat = EEEE, MMMM d, yyyy*  \r
index 8171d66756ecb4d5f2e37ac68df2655356756b7f..602a6aef2c0a0b3125f270f1f987e2b957a5b1c4 100644 (file)
@@ -117,7 +117,7 @@ public class Constants {
         * Enumeration representing the types of federation requests.\r
         */\r
        public static enum FederationRequest {\r
-               POKE, PROPOSAL, PULL_REPOSITORIES, PULL_USERS, PULL_TEAMS, PULL_SETTINGS, STATUS;\r
+               POKE, PROPOSAL, PULL_REPOSITORIES, PULL_USERS, PULL_TEAMS, PULL_SETTINGS, PULL_SCRIPTS, STATUS;\r
 \r
                public static FederationRequest fromName(String name) {\r
                        for (FederationRequest type : values()) {\r
index 2976c402758b085945430e3dd33d5669a07a114b..d54395e48841e6c9a8ba6af3054a57d9c8b23776 100644 (file)
@@ -50,6 +50,7 @@ import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.TeamModel;\r
 import com.gitblit.models.UserModel;\r
 import com.gitblit.utils.FederationUtils;\r
+import com.gitblit.utils.FileUtils;\r
 import com.gitblit.utils.JGitUtils;\r
 import com.gitblit.utils.JGitUtils.CloneResult;\r
 import com.gitblit.utils.StringUtils;\r
@@ -281,6 +282,8 @@ public class FederationPullExecutor implements Runnable {
                        r.close();\r
                }\r
 \r
+               IUserService userService = null;\r
+\r
                try {\r
                        // Pull USERS\r
                        // TeamModels are automatically pulled because they are contained\r
@@ -290,7 +293,7 @@ public class FederationPullExecutor implements Runnable {
                        if (users != null && users.size() > 0) {\r
                                File realmFile = new File(registrationFolderFile, registration.name + "_users.conf");\r
                                realmFile.delete();\r
-                               ConfigUserService userService = new ConfigUserService(realmFile);\r
+                               userService = new ConfigUserService(realmFile);\r
                                for (UserModel user : users) {\r
                                        userService.updateUserModel(user.username, user);\r
 \r
@@ -357,6 +360,27 @@ public class FederationPullExecutor implements Runnable {
                                        registration.name, registration.url), e);\r
                }\r
 \r
+               try {\r
+                       // Pull TEAMS\r
+                       // We explicitly pull these even though they are embedded in\r
+                       // UserModels because it is possible to use teams to specify\r
+                       // mailing lists or push scripts without specifying users.\r
+                       if (userService != null) {\r
+                               Collection<TeamModel> teams = FederationUtils.getTeams(registration);\r
+                               if (teams != null && teams.size() > 0) {\r
+                                       for (TeamModel team : teams) {\r
+                                               userService.updateTeamModel(team);\r
+                                       }\r
+                               }\r
+                       }\r
+               } catch (ForbiddenException e) {\r
+                       // ignore forbidden exceptions\r
+               } catch (IOException e) {\r
+                       logger.warn(MessageFormat.format(\r
+                                       "Failed to retrieve TEAMS from federated gitblit ({0} @ {1})",\r
+                                       registration.name, registration.url), e);\r
+               }\r
+\r
                try {\r
                        // Pull SETTINGS\r
                        Map<String, String> settings = FederationUtils.getSettings(registration);\r
@@ -375,6 +399,27 @@ public class FederationPullExecutor implements Runnable {
                                        "Failed to retrieve SETTINGS from federated gitblit ({0} @ {1})",\r
                                        registration.name, registration.url), e);\r
                }\r
+               \r
+               try {\r
+                       // Pull SCRIPTS\r
+                       Map<String, String> scripts = FederationUtils.getScripts(registration);\r
+                       if (scripts != null && scripts.size() > 0) {\r
+                               for (Map.Entry<String, String> script : scripts.entrySet()) {\r
+                                       String scriptName = script.getKey();\r
+                                       if (scriptName.endsWith(".groovy")) {\r
+                                               scriptName = scriptName.substring(0,  scriptName.indexOf(".groovy"));\r
+                                       }\r
+                                       File file = new File(registrationFolderFile, registration.name + "_" + scriptName + ".groovy");\r
+                                       FileUtils.writeContent(file, script.getValue());\r
+                               }\r
+                       }\r
+               } catch (ForbiddenException e) {\r
+                       // ignore forbidden exceptions\r
+               } catch (IOException e) {\r
+                       logger.warn(MessageFormat.format(\r
+                                       "Failed to retrieve SCRIPTS from federated gitblit ({0} @ {1})",\r
+                                       registration.name, registration.url), e);\r
+               }\r
        }\r
 \r
        /**\r
index f2ed903ca25d98ece0aa549b148070aaf9157439..e77205087d4cfbf678240bbd91ce4efadb92770f 100644 (file)
  */\r
 package com.gitblit;\r
 \r
+import java.io.File;\r
 import java.text.MessageFormat;\r
 import java.util.ArrayList;\r
 import java.util.Date;\r
 import java.util.HashMap;\r
+import java.util.HashSet;\r
 import java.util.List;\r
 import java.util.Map;\r
+import java.util.Set;\r
 \r
 import javax.servlet.http.HttpServletResponse;\r
 \r
@@ -30,6 +33,7 @@ import com.gitblit.models.FederationProposal;
 import com.gitblit.models.TeamModel;\r
 import com.gitblit.models.UserModel;\r
 import com.gitblit.utils.FederationUtils;\r
+import com.gitblit.utils.FileUtils;\r
 import com.gitblit.utils.HttpUtils;\r
 import com.gitblit.utils.StringUtils;\r
 import com.gitblit.utils.TimeUtils;\r
@@ -216,6 +220,41 @@ public class FederationServlet extends JsonServlet {
                                        teams.add(user);\r
                                }\r
                                result = teams;\r
+                       } else if (FederationRequest.PULL_SCRIPTS.equals(reqType)) {\r
+                               // pull scripts\r
+                               if (!GitBlit.self().validateFederationRequest(reqType, token)) {\r
+                                       // invalid token to pull script\r
+                                       logger.warn(MessageFormat.format(\r
+                                                       "Federation token from {0} not authorized to pull SCRIPTS",\r
+                                                       request.getRemoteAddr()));\r
+                                       response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+                                       return;\r
+                               }\r
+                               Map<String, String> scripts = new HashMap<String, String>();\r
+                               \r
+                               Set<String> names = new HashSet<String>();\r
+                               names.addAll(GitBlit.getStrings(Keys.groovy.preReceiveScripts));\r
+                               names.addAll(GitBlit.getStrings(Keys.groovy.postReceiveScripts));\r
+                               for (TeamModel team :  GitBlit.self().getAllTeams()) {\r
+                                       names.addAll(team.preReceiveScripts);\r
+                                       names.addAll(team.postReceiveScripts);\r
+                               }\r
+                               File scriptsFolder = GitBlit.getFileOrFolder(Keys.groovy.scriptsFolder, "groovy");\r
+                               for (String name : names) {\r
+                                       File file = new File(scriptsFolder, name);\r
+                                       if (!file.exists() && !file.getName().endsWith(".groovy")) {\r
+                                               file = new File(scriptsFolder, name + ".groovy");\r
+                                       }\r
+                                       if (file.exists()) {\r
+                                               // read the script\r
+                                               String content = FileUtils.readContent(file, "\n");\r
+                                               scripts.put(name, content);\r
+                                       } else {\r
+                                               // missing script?!\r
+                                               logger.warn(MessageFormat.format("Failed to find push script \"{0}\"", name));\r
+                                       }\r
+                               }\r
+                               result = scripts;\r
                        }\r
                }\r
 \r
index 6271a0d69ed98534c5020d1c502c1a8dfe68d06c..89dcbf1a9252239e7d02610ac73570a89f99bfca 100644 (file)
@@ -1253,6 +1253,7 @@ public class GitBlit implements ServletContextListener {
                case PULL_TEAMS:\r
                        return token.equals(all) || token.equals(unr);\r
                case PULL_SETTINGS:\r
+               case PULL_SCRIPTS:\r
                        return token.equals(all);\r
                }\r
                return false;\r
index 8207962af18fa769b9d68ff98d5a25caa1c37321..4d6060dd666ce860a3b7e8c1cde5404acdaafa82 100644 (file)
@@ -285,7 +285,8 @@ public class FederationUtils {
        }\r
 \r
        /**\r
-        * Tries to pull the gitblit team definitions from the remote gitblit instance.\r
+        * Tries to pull the gitblit team definitions from the remote gitblit\r
+        * instance.\r
         * \r
         * @param registration\r
         * @return a collection of TeamModel objects\r
@@ -312,6 +313,19 @@ public class FederationUtils {
                return settings;\r
        }\r
 \r
+       /**\r
+        * Tries to pull the referenced scripts from the remote gitblit instance.\r
+        * \r
+        * @param registration\r
+        * @return a map of the remote gitblit scripts by script name\r
+        * @throws Exception\r
+        */\r
+       public static Map<String, String> getScripts(FederationModel registration) throws Exception {\r
+               String url = asLink(registration.url, registration.token, FederationRequest.PULL_SCRIPTS);\r
+               Map<String, String> scripts = JsonUtils.retrieveJson(url, SETTINGS_TYPE);\r
+               return scripts;\r
+       }\r
+\r
        /**\r
         * Send an status acknowledgment to the remote Gitblit server.\r
         * \r
index 3cb43eb1bf973deb6bc24684934fd94ac3d87a3a..da9c99d293639badfdd6b8622b5ec196602af66c 100644 (file)
@@ -108,6 +108,19 @@ public class JsonUtils {
                        UnauthorizedException {\r
                return retrieveJson(url, type, null, null);\r
        }\r
+       \r
+       /**\r
+        * Reads a gson object from the specified url.\r
+        * \r
+        * @param url\r
+        * @param type\r
+        * @return the deserialized object\r
+        * @throws {@link IOException}\r
+        */\r
+       public static <X> X retrieveJson(String url, Class<? extends X> clazz) throws IOException,\r
+                       UnauthorizedException {\r
+               return retrieveJson(url, clazz, null, null);\r
+       }\r
 \r
        /**\r
         * Reads a gson object from the specified url.\r
index 499c61097fbedb17fe96e546669523d0536a8f27..2c4ffdc9b7f40b419ed2894bdab2b8649eb7ff82 100644 (file)
@@ -156,4 +156,11 @@ public class FederationTests {
                assertNotNull(teams);\r
                assertTrue(teams.size() > 0);\r
        }\r
+       \r
+       @Test\r
+       public void testPullScripts() throws Exception {\r
+               Map<String, String> scripts = FederationUtils.getScripts(getRegistration());\r
+               assertNotNull(scripts);\r
+               assertTrue(scripts.keySet().contains("sendmail"));\r
+       }\r
 }\r