Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support jcasc and renovate plugin #125

Merged
merged 39 commits into from Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
7f08537
support jcasc and renovate plugin
StefanSpieker Feb 7, 2024
25f4d24
Merge branch 'master' into jcasc
StefanSpieker Feb 14, 2024
df397e4
mark incompatible with older versions
StefanSpieker Feb 14, 2024
9d0afe3
mark incompatible with older versions
StefanSpieker Feb 16, 2024
51075ce
add JCasC documentation
StefanSpieker Feb 16, 2024
1c1d941
plugin works now with JCasC
StefanSpieker Feb 16, 2024
fd28bce
handle get more robust if no jenkins instance is running, e.g. in uni…
StefanSpieker Feb 18, 2024
49e8763
Merge remote-tracking branch 'origin/master' into jcasc
basil Mar 1, 2024
db5e912
Update src/main/java/org/jvnet/hudson/plugins/thinbackup/ThinBackupPl…
StefanSpieker Mar 1, 2024
84410bc
Update src/main/java/org/jvnet/hudson/plugins/thinbackup/ThinBackupPl…
StefanSpieker Mar 1, 2024
cb431a2
move config to new location if no new config present
StefanSpieker Mar 1, 2024
18a645d
rename Extension also in examples
StefanSpieker Mar 1, 2024
4d0bb50
fixed test
StefanSpieker Mar 1, 2024
2b150a4
restored deleted method
StefanSpieker Mar 1, 2024
4fd25cf
Merge branch 'master' into jcasc
StefanSpieker Mar 11, 2024
ac13e7a
added testcase for checking conversion of old to new config file
StefanSpieker Mar 11, 2024
4f8e0c9
fixed format issues
StefanSpieker Mar 11, 2024
d5916c2
fixed spotless issues
StefanSpieker Mar 11, 2024
4e88cb0
Merge branch 'master' into jcasc
StefanSpieker Mar 12, 2024
13099de
fixed restore
StefanSpieker Mar 24, 2024
fae20b0
introduce test with htmlUnit
StefanSpieker Mar 24, 2024
7d70e4a
Merge branch 'master' into jcasc
StefanSpieker Mar 29, 2024
8e39dd3
moved config to global configuration and improved documentation
StefanSpieker Mar 29, 2024
d0f5c65
Merge branch 'jcasc' of https://github.com/jenkinsci/thin-backup-plug…
StefanSpieker Mar 29, 2024
9568847
applied suggestions to apply new styling
StefanSpieker Mar 31, 2024
7f0dbec
replaced StringUtils functions with plain java
StefanSpieker Mar 31, 2024
556594d
fixed spotless
StefanSpieker Mar 31, 2024
442443f
use Jenkins.get() and rewrote tests to use JenkinsRule to test with r…
StefanSpieker Apr 3, 2024
a0860ca
fixed spotless
StefanSpieker Apr 3, 2024
54ebe94
apllied jelly refactoring and simplified configure(...) method
StefanSpieker Apr 3, 2024
cdbc068
moved accidently copied helpfiles back
StefanSpieker Apr 3, 2024
4c6febe
Merge branch 'master' into jcasc
StefanSpieker Apr 3, 2024
bb59154
removed now unused Mockito dependency
StefanSpieker Apr 3, 2024
fcfe7db
Update pom.xml
StefanSpieker Apr 3, 2024
9cba41c
Merge branch 'master' into jcasc
StefanSpieker Apr 4, 2024
b513ac3
added missing permission checks and added missing formValidation
StefanSpieker Apr 4, 2024
4ebf35c
added @POST where missing
StefanSpieker Apr 4, 2024
a3ba767
Merge branch 'master' into jcasc
StefanSpieker Apr 15, 2024
5cc958f
use POST for triggering manual backup
StefanSpieker Apr 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 46 additions & 3 deletions README.md
Expand Up @@ -6,17 +6,28 @@

This plugin simply backs up the global and job specific configurations (not the archive or the workspace). It can be scheduled and only backs up the most vital configuration info. It also let's you decide what vital means for you.

<!-- TOC -->
* [Thin Backup Plugin](#thin-backup-plugin)
* [Documentation](#documentation)
* [Backup Now](#backup-now)
* [Restore](#restore)
* [Settings](#settings)
* [Jenkins Configuration as Code (JCasC) support](#jenkins-configuration-as-code-jcasc-support)
* [Backup process](#backup-process)
* [Feature requests or bug reports](#feature-requests-or-bug-reports)
<!-- TOC -->
## Documentation

This plugin adds another management link to "Manage Jenkins" in the Tools section in newer Jenkins versions, called ThinBackup which looks like
this:
This plugin adds a management link to "Manage Jenkins" in the Tools section, called ThinBackup which looks like this:

![](images/ManagementLink.png)

This new link provides the following actions:
This link provides the following actions:

![](images/thinBackup.png)

**Note:** If you are using a version older than 2.0 this will also contain a settings button, as illustrated in [this screenshot](/images/thinBackupOld.png)

### Backup Now

Triggers a manual full back up right now.
Expand Down Expand Up @@ -48,6 +59,8 @@ update server, because plugins will be downloaded from the update server to keep

### Settings

**Note:** The settings are present in the global configuration since version 2.0

![](/images/thinBackupSettings.png)

#### Backup directory
Expand Down Expand Up @@ -137,6 +150,36 @@ as well.
**Note**: In case "Clean up differential backups" is checked, differential cleanup will be performed
before zipping is done, and therefore no differential backups will be in the ZIP files.

## Jenkins Configuration as Code (JCasC) support

Since version 2.0 the plugin fully supports JCasC. An example config as a basis can be used from here.

**Note**: Remember to escape backslashes in the YAML

```yaml
unclassified:
thinBackup:
backupAdditionalFiles: false
backupAdditionalFilesRegex: "^.*\\.(txt)$"
backupBuildArchive: false
backupBuildResults: true
backupBuildsToKeepOnly: false
backupConfigHistory: false
backupNextBuildNumber: false
backupPath: "c:\\temp\\thin-backup"
backupPluginArchives: false
backupUserContents: false
cleanupDiff: false
diffBackupSchedule: "0 12 * * 1-5"
excludedFilesRegex: "^.*\\.(log)$"
failFast: true
forceQuietModeTimeout: 120
fullBackupSchedule: "0 12 * * 1"
moveOldBackupsToZipFile: false
nrMaxStoredFull: -1
waitForIdle: true
```

## Backup process

Because many of you are asking why Jenkins is going to shutdown when a backup is triggered, I
Expand Down
Binary file modified images/restore.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/thinBackup.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/thinBackupOld.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/thinBackupSettings.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 8 additions & 2 deletions pom.xml
Expand Up @@ -73,6 +73,7 @@
<jenkins.version>2.401.3</jenkins.version>
<gitHubRepo>jenkinsci/thin-backup-plugin</gitHubRepo>
<spotless.check.skip>false</spotless.check.skip>
<hpi.compatibleSinceVersion>2.0</hpi.compatibleSinceVersion>
</properties>

<dependencyManagement>
Expand All @@ -93,8 +94,13 @@
<artifactId>ionicons-api</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<groupId>io.jenkins</groupId>
<artifactId>configuration-as-code</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jenkins.configuration-as-code</groupId>
<artifactId>test-harness</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Expand Up @@ -20,6 +20,7 @@
import hudson.Extension;
import hudson.model.ManagementLink;
import hudson.model.TaskListener;
import hudson.util.ListBoxModel;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
Expand All @@ -45,6 +46,7 @@
@Extension
public class ThinBackupMgmtLink extends ManagementLink {
private static final String THIN_BACKUP_SUBPATH = "/thinBackup";

private static final Logger LOGGER = Logger.getLogger("hudson.plugins.thinbackup");

@Override
Expand All @@ -70,128 +72,78 @@
public void doBackupManual(final StaplerRequest res, final StaplerResponse rsp) throws IOException {
LOGGER.info("Starting manual backup.");

final Jenkins jenkins = Jenkins.getInstanceOrNull();
if (jenkins == null) {
return;
}
final Jenkins jenkins = Jenkins.get();
jenkins.checkPermission(Jenkins.ADMINISTER);

final ThinBackupPeriodicWork manualBackupWorker = new ThinBackupPeriodicWork() {
@Override
protected void execute(final TaskListener arg0) {
backupNow(BackupType.FULL);
}
};
Timer.get().schedule(manualBackupWorker, 0, TimeUnit.SECONDS);

rsp.sendRedirect(res.getContextPath() + THIN_BACKUP_SUBPATH);
}

public void doRestore(
final StaplerRequest res,
final StaplerResponse rsp,
@QueryParameter("restoreBackupFrom") final String restoreBackupFrom,
@QueryParameter("restoreNextBuildNumber") final String restoreNextBuildNumber,
@QueryParameter("restorePlugins") final String restorePlugins)
throws IOException {
LOGGER.info("Starting restore operation.");

final Jenkins jenkins = Jenkins.getInstanceOrNull();
if (jenkins == null) {
return;
}
final Jenkins jenkins = Jenkins.get();
jenkins.checkPermission(Jenkins.ADMINISTER);

jenkins.doQuietDown();
LOGGER.fine("Waiting until executors are idle to perform restore...");
Utils.waitUntilIdle();

try {
final File hudsonHome = jenkins.getRootDir();
final Date restoreFromDate = new SimpleDateFormat(Utils.DISPLAY_DATE_FORMAT).parse(restoreBackupFrom);

final HudsonRestore hudsonRestore = new HudsonRestore(
hudsonHome,
ThinBackupPluginImpl.getInstance().getExpandedBackupPath(),
ThinBackupPluginImpl.get().getExpandedBackupPath(),
restoreFromDate,
"on".equals(restoreNextBuildNumber),
"on".equals(restorePlugins));
hudsonRestore.restore();

LOGGER.info("Restore finished.");
} catch (ParseException e) {
LOGGER.severe("Cannot parse restore option. Aborting.");
} catch (final Exception ise) {
LOGGER.severe("Could not restore. Aborting.");
} finally {
jenkins.doCancelQuietDown();
rsp.sendRedirect(res.getContextPath() + THIN_BACKUP_SUBPATH);
}
}

public void doSaveSettings(
final StaplerRequest res,
final StaplerResponse rsp,
@QueryParameter("backupPath") final String backupPath,
@QueryParameter("fullBackupSchedule") final String fullBackupSchedule,
@QueryParameter("diffBackupSchedule") final String diffBackupSchedule,
@QueryParameter("nrMaxStoredFull") final String nrMaxStoredFull,
@QueryParameter("excludedFilesRegex") final String excludedFilesRegex,
@QueryParameter("moveOldBackupsToZipFile") final boolean moveOldBackupsToZipFile,
@QueryParameter("cleanupDiff") final boolean cleanupDiff,
@QueryParameter("backupBuildResults") final boolean backupBuildResults,
@QueryParameter("backupBuildArchive") final boolean backupBuildArchive,
@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("backupConfigHistory") final boolean backupConfigHistory,
@QueryParameter("forceQuietModeTimeout") final String forceQuietModeTimeout,
@QueryParameter("failFast") final boolean failFast)
throws IOException {
Jenkins jenkins = Jenkins.getInstanceOrNull();
if (jenkins == null) {
return;
}
jenkins.checkPermission(Jenkins.ADMINISTER);

final ThinBackupPluginImpl plugin = ThinBackupPluginImpl.getInstance();
plugin.setBackupPath(backupPath);
plugin.setFullBackupSchedule(fullBackupSchedule);
plugin.setDiffBackupSchedule(diffBackupSchedule);
plugin.setNrMaxStoredFullAsString(nrMaxStoredFull);
plugin.setExcludedFilesRegex(excludedFilesRegex);
plugin.setCleanupDiff(cleanupDiff);
plugin.setMoveOldBackupsToZipFile(moveOldBackupsToZipFile);
plugin.setBackupBuildResults(backupBuildResults);
plugin.setBackupBuildArchive(backupBuildArchive);
plugin.setBackupBuildsToKeepOnly(backupBuildsToKeepOnly);
plugin.setBackupUserContents(backupUserContents);
plugin.setBackupConfigHistory(backupConfigHistory);
plugin.setBackupNextBuildNumber(backupNextBuildNumber);
plugin.setBackupPluginArchives(backupPluginArchives);
plugin.setBackupAdditionalFiles(backupAdditionalFiles);
plugin.setBackupAdditionalFilesRegex(backupAdditionalFilesRegex);
plugin.setWaitForIdle(waitForIdle);
plugin.setForceQuietModeTimeout(Integer.parseInt(forceQuietModeTimeout));
plugin.setFailFast(failFast);
plugin.save();
LOGGER.finest("Saving backup settings done.");
rsp.sendRedirect(res.getContextPath() + THIN_BACKUP_SUBPATH);
}

public ThinBackupPluginImpl getConfiguration() {
return ThinBackupPluginImpl.getInstance();
return ThinBackupPluginImpl.get();
}

public List<String> getAvailableBackups() {
final ThinBackupPluginImpl plugin = ThinBackupPluginImpl.getInstance();
final ThinBackupPluginImpl plugin = ThinBackupPluginImpl.get();
return Utils.getBackupsAsDates(new File(plugin.getExpandedBackupPath()));
}

public ListBoxModel doFillBackupItems() {
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
final ThinBackupPluginImpl plugin = ThinBackupPluginImpl.get();
final List<String> backupsAsDates = Utils.getBackupsAsDates(new File(plugin.getExpandedBackupPath()));
var model = new ListBoxModel();
for (String entry : backupsAsDates) {
model.add(new ListBoxModel.Option(entry));
}
return model;

Check warning on line 144 in src/main/java/org/jvnet/hudson/plugins/thinbackup/ThinBackupMgmtLink.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 75-144 are not covered by tests
}

/**
* Name of the category for this management link. Exists so that plugins with core dependency pre-dating the version
* when this was introduced can define a category.
Expand Down
Expand Up @@ -16,7 +16,6 @@
*/
package org.jvnet.hudson.plugins.thinbackup;

import antlr.ANTLRException;
import hudson.Extension;
import hudson.model.AsyncPeriodicWork;
import hudson.model.TaskListener;
Expand All @@ -29,7 +28,6 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
import org.jvnet.hudson.plugins.thinbackup.backup.HudsonBackup;
import org.jvnet.hudson.plugins.thinbackup.utils.Utils;

Expand All @@ -38,7 +36,7 @@

private static final Logger LOGGER = Logger.getLogger("hudson.plugins.thinbackup");

private final ThinBackupPluginImpl plugin = ThinBackupPluginImpl.getInstance();
private final ThinBackupPluginImpl plugin = ThinBackupPluginImpl.get();

public enum BackupType {
NONE,
Expand Down Expand Up @@ -68,17 +66,14 @@
}

protected void backupNow(final BackupType type) {
final Jenkins jenkins = Jenkins.getInstanceOrNull();
if (jenkins == null) {
return;
}
final Jenkins jenkins = Jenkins.get();
final boolean inQuietModeBeforeBackup = jenkins.isQuietingDown();

String backupPath = null;
try {
backupPath = plugin.getExpandedBackupPath();

if (StringUtils.isNotEmpty(backupPath)) {
if (backupPath != null && !backupPath.isEmpty()) {

Check warning on line 76 in src/main/java/org/jvnet/hudson/plugins/thinbackup/ThinBackupPeriodicWork.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 69-76 are not covered by tests
if (plugin.isWaitForIdle()) {
LOGGER.fine("Wait until executors are idle to perform backup.");
Utils.waitUntilIdleAndSwitchToQuietMode(plugin.getForceQuietModeTimeout(), TimeUnit.MINUTES);
Expand Down Expand Up @@ -107,7 +102,7 @@
}
}

BackupType getNextScheduledBackupType(final long currentTime, final String fullCron, final String diffCron) {
static BackupType getNextScheduledBackupType(final long currentTime, final String fullCron, final String diffCron) {
final long fullDelay = calculateDelay(currentTime, BackupType.FULL, fullCron);
final long diffDelay = calculateDelay(currentTime, BackupType.DIFF, diffCron);

Expand All @@ -133,10 +128,10 @@
return delay < MIN ? res : BackupType.NONE;
}

long calculateDelay(final long currentTime, final BackupType backupType, final String cron) {
static long calculateDelay(final long currentTime, final BackupType backupType, final String cron) {
CronTab cronTab;
try {
if (StringUtils.isEmpty(cron)) {
if (cron == null || cron.isEmpty()) {

Check warning on line 134 in src/main/java/org/jvnet/hudson/plugins/thinbackup/ThinBackupPeriodicWork.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 134 is only partially covered, 2 branches are missing
return -1;
}

Expand All @@ -162,7 +157,7 @@
}

return delay;
} catch (final ANTLRException e) {
} catch (final IllegalArgumentException e) {

Check warning on line 160 in src/main/java/org/jvnet/hudson/plugins/thinbackup/ThinBackupPeriodicWork.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 160 is not covered by tests
LOGGER.warning(MessageFormat.format(
"Cannot parse the specified ''Backup schedule for {0} backups''. Check cron notation.",
backupType));
Expand Down