Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
JENKINS-21221: Add option for backing up of plugin archives, as well …
…as arbitrary files.

Proposed implementation
  • Loading branch information
Patrice Matignon committed Jan 4, 2014
1 parent 0900a7b commit e336023
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 4 deletions.
Expand Up @@ -126,6 +126,9 @@ public void doSaveSettings(final StaplerRequest res, final StaplerResponse rsp,
@QueryParameter("backupBuildsToKeepOnly") final boolean backupBuildsToKeepOnly,
@QueryParameter("backupUserContents") final boolean backupUserContents,
@QueryParameter("backupNextBuildNumber") final boolean backupNextBuildNumber,
@QueryParameter("backupPluginArchives") final boolean backupPluginArchives,
@QueryParameter("backupAdditionalFiles") final boolean backupAdditionalFiles,
@QueryParameter("backupAdditionalFilesRegex") final String backupAdditionalFilesRegex,
@QueryParameter("waitForIdle") final boolean waitForIdle,
@QueryParameter("forceQuietModeTimeout") final String forceQuietModeTimeout) throws IOException {
Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
Expand All @@ -143,6 +146,9 @@ public void doSaveSettings(final StaplerRequest res, final StaplerResponse rsp,
plugin.setBackupBuildsToKeepOnly(backupBuildsToKeepOnly);
plugin.setBackupUserContents(backupUserContents);
plugin.setBackupNextBuildNumber(backupNextBuildNumber);
plugin.setBackupPluginArchives(backupPluginArchives);
plugin.setBackupAdditionalFiles(backupAdditionalFiles);
plugin.setBackupAdditionalFilesRegex(backupAdditionalFilesRegex);
plugin.setWaitForIdle(waitForIdle);
plugin.setForceQuietModeTimeout(Integer.parseInt(forceQuietModeTimeout));
plugin.save();
Expand Down
Expand Up @@ -53,7 +53,10 @@ public class ThinBackupPluginImpl extends Plugin {
private boolean moveOldBackupsToZipFile = false;
private boolean backupBuildResults = true;
private boolean backupBuildArchive = false;
private boolean backupPluginArchives = false;
private boolean backupUserContents = false;
private boolean backupAdditionalFiles = false;
private String backupAdditionalFilesRegex = null;
private boolean backupNextBuildNumber = false;
private boolean backupBuildsToKeepOnly = false;

Expand Down Expand Up @@ -221,6 +224,30 @@ public void setBackupUserContents(boolean backupUserContents) {
public String getExcludedFilesRegex() {
return excludedFilesRegex;
}

public void setBackupPluginArchives(final boolean backupPluginArchives) {
this.backupPluginArchives = backupPluginArchives;
}

public boolean isBackupPluginArchives() {
return backupPluginArchives;
}

public void setBackupAdditionalFiles(final boolean backupAdditionalFiles) {
this.backupAdditionalFiles = backupAdditionalFiles;
}

public boolean isBackupAdditionalFiles() {
return backupAdditionalFiles;
}

public void setBackupAdditionalFilesRegex(final String backupAdditionalFilesRegex) {
this.backupAdditionalFilesRegex = backupAdditionalFilesRegex;
}

public String getBackupAdditionalFilesRegex() {
return backupAdditionalFilesRegex;
}

public void setWaitForIdle(boolean waitForIdle) {
this.waitForIdle = waitForIdle;
Expand Down
Expand Up @@ -61,11 +61,17 @@ public class HudsonBackup {
public static final String ARCHIVE_DIR_NAME = "archive";
public static final String USERSCONTENTS_DIR_NAME = "userContent";
public static final String NEXT_BUILD_NUMBER_FILE_NAME = "nextBuildNumber";
public static final String PLUGINS_DIR_NAME = "plugins";
public static final String CONFIG_XML = "config.xml";
public static final String XML_FILE_EXTENSION = ".xml";
public static final String JPI_FILE_EXTENSION = ".jpi";
public static final String HPI_FILE_EXTENSION = ".hpi";
public static final String DISABLED_EXTENSION = ".disabled";
public static final String ZIP_FILE_EXTENSION = ".zip";
public static final String INSTALLED_PLUGINS_XML = "installedPlugins" + XML_FILE_EXTENSION;
public static final String CHANGELOG_HISTORY_PLUGIN_DIR_NAME = "changelog-history";
public static final String SVN_CREDENTIALS_FILE_NAME = "subversion.credentials";
public static final String SVN_EXTERNALS_FILE_NAME = "svnexternals.txt";

private final ThinBackupPluginImpl plugin;
private final File hudsonHome;
Expand All @@ -74,6 +80,7 @@ public class HudsonBackup {
private final BackupType backupType;
private final Date latestFullBackupDate;
private Pattern excludedFilesRegexPattern = null;
private Pattern backupAdditionalFilesRegexPattern = null;
private ItemGroup<TopLevelItem> hudson;

public HudsonBackup(final ThinBackupPluginImpl plugin, final BackupType backupType) {
Expand All @@ -89,7 +96,7 @@ protected HudsonBackup(final ThinBackupPluginImpl plugin, final BackupType backu
this.hudsonHome = plugin.getHudsonHome();

final String excludedFilesRegex = plugin.getExcludedFilesRegex();
if ((excludedFilesRegex != null) && !excludedFilesRegex.isEmpty()) {
if ((excludedFilesRegex != null) && !excludedFilesRegex.trim().isEmpty()) {
try {
excludedFilesRegexPattern = Pattern.compile(excludedFilesRegex);
} catch (final PatternSyntaxException pse) {
Expand All @@ -98,6 +105,16 @@ protected HudsonBackup(final ThinBackupPluginImpl plugin, final BackupType backu
}
}

final String backupAdditionalFilesRegex = plugin.getBackupAdditionalFilesRegex();
if ((backupAdditionalFilesRegex != null) && !backupAdditionalFilesRegex.trim().isEmpty()) {
try {
backupAdditionalFilesRegexPattern = Pattern.compile(backupAdditionalFilesRegex);
} catch (final PatternSyntaxException pse) {
LOGGER.log(Level.SEVERE, String.format("Regex pattern '%s' for including additional files to back up, is invalid, and will be disregarded.", backupAdditionalFilesRegex), pse);
backupAdditionalFilesRegexPattern = null;
}
}

this.backupRoot = new File(plugin.getExpandedBackupPath());
if (!backupRoot.exists()) {
backupRoot.mkdirs();
Expand Down Expand Up @@ -141,12 +158,18 @@ public void backup() throws IOException {
backupGlobalXmls();
backupJobs();
backupRootFolder(USERS_DIR_NAME);
storePluginListIfChanged();

if (plugin.isBackupUserContents())
backupRootFolder(USERSCONTENTS_DIR_NAME);

new DirectoryCleaner().removeEmptyDirectories(backupDirectory);
if (plugin.isBackupPluginArchives())
backupPluginArchives();
storePluginListIfChanged();

if (plugin.isBackupAdditionalFiles())
backupAdditionalFiles();

(new DirectoryCleaner()).removeEmptyDirectories(backupDirectory);

if (backupType == BackupType.FULL) {
cleanupDiffs();
Expand Down Expand Up @@ -219,6 +242,50 @@ private void backupJob(final File jobDirectory, final File jobsBackupDirectory,
}
}

private void backupPluginArchives() throws IOException {
LOGGER.fine("Backing up actual plugin archives...");

final IOFileFilter pluginArchivesFilter = FileFilterUtils.or(
FileFilterUtils.suffixFileFilter(JPI_FILE_EXTENSION),
FileFilterUtils.suffixFileFilter(HPI_FILE_EXTENSION));
final IOFileFilter disabledPluginMarkersFilter = FileFilterUtils.or(
FileFilterUtils.suffixFileFilter(JPI_FILE_EXTENSION+DISABLED_EXTENSION),
FileFilterUtils.suffixFileFilter(HPI_FILE_EXTENSION+DISABLED_EXTENSION));

final IOFileFilter filter = FileFilterUtils.and(
FileFileFilter.FILE,
FileFilterUtils.or(pluginArchivesFilter, disabledPluginMarkersFilter));

backupRootFolder(PLUGINS_DIR_NAME, filter);

LOGGER.fine("DONE backing up actual plugin archives.");
}

private void backupAdditionalFiles() throws IOException {
LOGGER.info("Backing up additional files...");



if (backupAdditionalFilesRegexPattern != null) {
final IOFileFilter addFilesFilter = new RegexFileFilter(backupAdditionalFilesRegexPattern);

final IOFileFilter filter = FileFilterUtils.and(
addFilesFilter,
FileFilterUtils.or(
DirectoryFileFilter.DIRECTORY,
FileFilterUtils.and(
getFileAgeDiffFilter(),
getExcludedFilesFilter())));

FileUtils.copyDirectory(hudsonHome, backupDirectory, filter);
}
else {
LOGGER.info("No Additional File regex was provided: selecting no Additional Files to back up.");
}

LOGGER.info("DONE backing up Additional Files.");
}

private File createConfigurationBackupDirectory(File jobBackupdirectory, File jobDirectory, File configurationDirectory) {
String pathToConfiguration = configurationDirectory.getAbsolutePath();
String pathToJob = jobDirectory.getAbsolutePath();
Expand All @@ -242,7 +309,14 @@ private boolean isMatrixJob(File jobDirectory) {
}

private void backupJobConfigFor(final File jobDirectory, final File jobBackupDirectory) throws IOException {
IOFileFilter filter = FileFilterUtils.and(FileFilterUtils.suffixFileFilter(XML_FILE_EXTENSION), getFileAgeDiffFilter(), getExcludedFilesFilter());
final IOFileFilter filter = FileFilterUtils.and(
FileFilterUtils.or(
FileFilterUtils.suffixFileFilter(XML_FILE_EXTENSION),
FileFilterUtils.nameFileFilter(SVN_CREDENTIALS_FILE_NAME),
FileFilterUtils.nameFileFilter(SVN_EXTERNALS_FILE_NAME)),
getFileAgeDiffFilter(),
getExcludedFilesFilter());

FileUtils.copyDirectory(jobDirectory, jobBackupDirectory, filter);
backupNextBuildNumberFile(jobDirectory, jobBackupDirectory);
}
Expand Down Expand Up @@ -326,11 +400,16 @@ private void backupBuildArchive(final File buildSrcDir, final File buildDestDir)
}

private void backupRootFolder(String folderName) throws IOException {
backupRootFolder(folderName, TrueFileFilter.INSTANCE);
}

private void backupRootFolder(String folderName, IOFileFilter fileFilter) throws IOException {
final File srcDirectory = new File(hudsonHome.getAbsolutePath(), folderName);
if (srcDirectory.exists() && srcDirectory.isDirectory()) {
LOGGER.fine(String.format("Backing up %s...", folderName));
final File destDirectory = new File(backupDirectory.getAbsolutePath(), folderName);
IOFileFilter filter = FileFilterUtils.and(
fileFilter,
getFileAgeDiffFilter(),
getExcludedFilesFilter());
filter = FileFilterUtils.or(filter, DirectoryFileFilter.DIRECTORY);
Expand Down
Expand Up @@ -91,6 +91,22 @@
checked="${it.configuration.backupNextBuildNumber}"
name="backupNextBuildNumber" />

<f:optionalBlock title="Backup plugins archives"
help="/plugin/thinBackup/help/help-backupPlugins.html"
checked="${it.configuration.backupPluginArchives}"
name="backupPluginArchives" >
</f:optionalBlock>

<f:optionalBlock title="Backup additional files"
help="/plugin/thinBackup/help/help-backupAdditionalFiles.html"
checked="${it.configuration.backupAdditionalFiles}"
name="backupAdditionalFiles">
<f:entry title="Files included in backup (regular expression)" field="backupAdditionalFilesRegex">
<f:textbox value="${it.configuration.backupAdditionalFilesRegex}" name="backupAdditionalFilesRegex"
checkUrl="'${rootURL}/plugin/thinBackup/checkExcludedFilesRegex?value='+escape(this.value)"/>
</f:entry>
</f:optionalBlock>

<f:optionalBlock title="Clean up differential backups"
help="/plugin/thinBackup/help/help-cleanupDiff.html"
checked="${it.configuration.cleanupDiff}"
Expand Down
40 changes: 40 additions & 0 deletions src/main/webapp/help/help-backupAdditionalFiles.html
@@ -0,0 +1,40 @@
<!--
The MIT License
Copyright (c) 2011, Borland (a Micro Focus Company), Matthias Steinkogler, Thomas Fuerer
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.
-->

<div>
<p>
If this option is enabled, the build archive within the build results will also be backed up. Even more data!
</p>
<p>
If you have specific files not normally backed up, that you also want to back up, entering a regex here which identifies those files will include them. All files with a name matching this regular expression will be backed up. If the expression is invalid, it will be disregarded.
<br/>
Note that this only allows filename patterns, (not full ant-style patterns) but positive and negative look-aheads are effective at filtering out the primary folder.
<br/>
<b>Example:</b><br/>
<i>^(email\-templates|.*\.jelly)$</i> will include email-templates/*.jelly .
<br/>
<i>^(?!jobs)(?!plugins)(?!logs)(?!users)(log|.*\.xml)$</i> will include *.xml and log/*.xml while skipping the jobs, logs, users and plugins folders under the home folder.
</p>

</div>
29 changes: 29 additions & 0 deletions src/main/webapp/help/help-backupPlugins.html
@@ -0,0 +1,29 @@
<!--
The MIT License
Copyright (c) 2011, Borland (a Micro Focus Company), Matthias Steinkogler, Thomas Fuerer
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.
-->

<div>
<p>
If this option is enabled, the actual plugin archives can be backed up as well. Depending on your plugin mix, this could add quiter a few MB's!
</p>
</div>

0 comments on commit e336023

Please sign in to comment.