diff --git a/.gitignore b/.gitignore index 61bc0c38b..a1d491bc7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ *.iml .DS_Store /.idea/ -/target/ -/work/ +.vscode/launch.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..fb1e7438a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "cSpell.words": [ + "accmod", + "findbugs", + "Gitter", + "Gruetzmacher", + "hamcrest", + "instanceof", + "jenkinsci", + "kohsuke", + "lockableresources", + "Servlet" + ] +} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4cd68725f..499d452a6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,88 +1,10 @@ # Contributing -If you want to contribute to this plugin, you probably will need a Jenkins plugin development -environment. This basically means a current version of Java (Java 11 should probably be okay for now) -and [Apache Maven]. See the [Jenkins Plugin Tutorial] for details. -You could also go the [GitPod](https://gitpod.io/#https://github.com/jenkinsci/lockable-resources-plugin) way. +Any contributions are welcome. -If you have the proper environment, typing: +This repository contains lockable-resources [plugin](/plugin/) and [shared-library](/shared-library/README.md). -```sh -mvn verify -``` +The [plugin](/plugin/) is the core part. Contains all the Java, Jelly, CSS, localizations ... for the plugin self. -should create a plugin as `target/*.hpi`, which you can install in your Jenkins instance. Running - -```sh -mvn hpi:run -``` - -allows you to spin up a test Jenkins instance on [localhost] to test your -local changes before committing. - -[Apache Maven]: https://maven.apache.org/ -[Jenkins Plugin Tutorial]: https://jenkins.io/doc/developer/tutorial/prepare/ -[localhost]: http://localhost:8080/jenkins/ - -## Code Style - -This plugin tries to migrate to [Google Java Code Style], please try to adhere to that style -whenever adding new files or making big changes to existing files. If your IDE doesn't support -this style, you can use the [fmt-maven-plugin], like this: - -```sh - mvn fmt:format -DfilesNamePattern=ChangedFile\.java -``` - -to reformat Java code in the proper style. - -[Google Java Code Style]: https://google.github.io/styleguide/javaguide.html -[fmt-maven-plugin]: https://github.com/coveo/fmt-maven-plugin - -## Code coverage - -Test coverage is a percentage measure of the degree to which the source code of a program is executed when a test is run. A program with high test coverage has more of its source code executed during testing, which suggests it has a lower chance of containing undetected software bugs compared to a program with low test coverage. The best way to improve code coverage is writing of automated tests. - -To get local line-by-line coverage report execute this command - -```sh -mvn -P enable-jacoco clean verify jacoco:report -``` - -The report is then located in *target/site/jacoco/index.html*. - -## License - -The MIT License (MIT) - -- Copyright 2013-2015 6WIND -- Copyright 2016-2018 Antonio Muñiz -- Copyright 2019 TobiX -- Copyright 2017-2022 Jim Klimov - -See [LICENSE](LICENSE.txt) - -## Localization - -[![Crowdin](https://badges.crowdin.net/e/656dcffac5a09ad0fbdedcb430af1904/localized.svg)](https://jenkins.crowdin.com/lockable-resources-plugin) - -Internationalization documentation for Jelly, Java and Groovy can be found [here](https://www.jenkins.io/doc/developer/internationalization/). - -To translate this plugin we recommend to use [Crowdin](https://jenkins.crowdin.com/lockable-resources-plugin). - -Read on [how to use the crowdin web interface](https://www.jenkins.io/doc/developer/crowdin/) to translate plugins. - -When you want to help us, please create a new [feature request](https://github.com/jenkinsci/lockable-resources-plugin/issues/new?assignees=&labels=enhancement&template=2-feature-request.yml) with following content - -Title: -l10n: \ -Description -I would provide new (or update) translations for \ - -We will then add you to the Crowdin project. - -For short translations / updates we can also send you invitation (time limited) - -**Privacy policy notice** -When you start translating via Crowdin service, your browsers will send cookies to Crowdin so that Crowdin can identify translators contributing to the project. You might need to update the privacy policy to reflect this aspect of cookies usage. +The [shared-library](/shared-library/README.md) is an extension to the plugin self with many solutions and interfaces done in Groovy. diff --git a/Jenkinsfile b/Jenkinsfile index 964cdb4ce..3260ee96f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -11,3 +11,52 @@ buildPlugin(useContainerAgent: true, configurations: [ // see also https://www.jenkins.io/doc/developer/plugin-development/choosing-jenkins-baseline/ [ platform: 'linux', jdk: '17', jenkins: '2.361.4' ], ]) + +def configs = [ + [platform: 'linux', jdk: '11'], + [platform: 'windows', jdk: '11'], + [platform: 'linux', jdk: '17'] +] + +def stages = [failFast: true] + +configs.each { c -> + final String stageIdentifier = "${c.platform}-${c.jdk}" + stages[stageIdentifier] = { + testSharedLib(c, stageIdentifier) + } +} + +parallel(stages) + +void testSharedLib(Map config, String stageIdentifier) { + String platform = config.platform + String jdk = config.jdk == null ? '11' : config.jdk + def timeoutValue = config.timeoutValue == null ? 120 : config.timeoutValue + String label = 'maven-' + jdk + + if (platform == 'windows') { + label += '-windows' + } + + timestamps() { + + node(label) { + stage("Checkout (${stageIdentifier})") { + infra.checkoutSCM(null) + } + + stage('Test shared lib') { + dir('shared-library') { + String cmd = 'mvn --no-transfer-progress -B clean verify' + if (isUnix()) { + sh cmd + } else { + bat cmd + } + junit(keepLongStdio: true, testResults: 'tests/target/surefire-reports/TEST-*.xml') + } + } + } + } +} \ No newline at end of file diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index db93e0b93..7ccf7553f 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -67,14 +67,12 @@ This steps need additional automation in release management. Therefore are comme - [ ] For dependency updates, there are links to external changelogs and, if possible, full differentials. - [ ] For new APIs and extension points, there is a link to at least one consumer. - [ ] Any localizations are transferred to *.properties files. -- [ ] Changes in the interface are documented also as [examples](src/doc/examples/readme.md). +- [ ] Changes in the interface are documented also as [examples](plugin/doc/examples/readme.md), [groovy/examples](shared-library/doc/examples/readme.md). ### Maintainer checklist Before the changes are marked as `ready-for-merge`: -- [ ] There is at least one (1) approval for the pull request and no outstanding requests for change. -- [ ] Conversations in the pull request are over, or it is explicit that a reviewer is not blocking the change. - [ ] Changelog entries in the **pull request title** and/or **Proposed changelog entries** are accurate, human-readable, and in the imperative mood. - [ ] Proper changelog labels are set so that the changelog can be generated automatically. See also [release-drafter-labels](https://github.com/jenkinsci/.github/blob/ce466227c534c42820a597cb8e9cac2f2334920a/.github/release-drafter.yml#L9-L50). - [ ] If the change needs additional upgrade steps from users, the `upgrade-guide-needed` label is set and there is a **Proposed upgrade guidelines** section in the pull request title (see [example](https://github.com/jenkinsci/jenkins/pull/4387)). diff --git a/README.md b/README.md index cf99d4039..12b8e228d 100644 --- a/README.md +++ b/README.md @@ -159,9 +159,10 @@ lock( echo 'Finish' ``` -More examples are [here](src/doc/examples/readme.md). +More examples are [here](plugin/doc/examples/readme.md). ---- + ## Configuration as Code This plugin can be configured via diff --git a/plugin/.gitignore b/plugin/.gitignore new file mode 100644 index 000000000..49abe8423 --- /dev/null +++ b/plugin/.gitignore @@ -0,0 +1,6 @@ +*.iml +.DS_Store +/.idea/ +/target/ +/work/ +.vscode/launch.json diff --git a/plugin/CONTRIBUTING.md b/plugin/CONTRIBUTING.md new file mode 100644 index 000000000..ba485cf4e --- /dev/null +++ b/plugin/CONTRIBUTING.md @@ -0,0 +1,77 @@ + +# Contributing + +If you want to contribute to this plugin, you probably will need a Jenkins plugin development +environment. This basically means a current version of Java (Java 11 should probably be okay for now) +and [Apache Maven]. See the [Jenkins Plugin Tutorial] for details. +You could also go the [GitPod](https://gitpod.io/#https://github.com/jenkinsci/lockable-resources-plugin) way. + +If you have the proper environment, typing: + +```sh +mvn verify +``` + +should create a plugin as `target/*.hpi`, which you can install in your Jenkins instance. Running + +```sh +mvn hpi:run +``` + +allows you to spin up a test Jenkins instance on [localhost] to test your +local changes before committing. + +[Apache Maven]: https://maven.apache.org/ +[Jenkins Plugin Tutorial]: https://jenkins.io/doc/developer/tutorial/prepare/ +[localhost]: http://localhost:8080/jenkins/ + +## Code Style + +This plugin tries to migrate to [Google Java Code Style], please try to adhere to that style +whenever adding new files or making big changes to existing files. If your IDE doesn't support +this style, you can use the [fmt-maven-plugin], like this: + +```sh + mvn fmt:format -DfilesNamePattern=ChangedFile\.java +``` + +to reformat Java code in the proper style. + +[Google Java Code Style]: https://google.github.io/styleguide/javaguide.html +[fmt-maven-plugin]: https://github.com/coveo/fmt-maven-plugin + +## Code coverage + +Test coverage is a percentage measure of the degree to which the source code of a program is executed when a test is run. A program with high test coverage has more of its source code executed during testing, which suggests it has a lower chance of containing undetected software bugs compared to a program with low test coverage. The best way to improve code coverage is writing of automated tests. + +To get local line-by-line coverage report execute this command + +```sh +mvn -P enable-jacoco clean verify jacoco:report +``` + +The report is then located in *target/site/jacoco/index.html*. + +## Localization + +[![Crowdin](https://badges.crowdin.net/e/656dcffac5a09ad0fbdedcb430af1904/localized.svg)](https://jenkins.crowdin.com/lockable-resources-plugin) + +Internationalization documentation for Jelly, Java and Groovy can be found [here](https://www.jenkins.io/doc/developer/internationalization/). + +To translate this plugin we recommend to use [Crowdin](https://jenkins.crowdin.com/lockable-resources-plugin). + +Read on [how to use the crowdin web interface](https://www.jenkins.io/doc/developer/crowdin/) to translate plugins. + +When you want to help us, please create a new [feature request](https://github.com/jenkinsci/lockable-resources-plugin/issues/new?assignees=&labels=enhancement&template=2-feature-request.yml) with following content + +Title: +l10n: \ +Description +I would provide new (or update) translations for \ + +We will then add you to the Crowdin project. + +For short translations / updates we can also send you invitation (time limited) + +**Privacy policy notice** +When you start translating via Crowdin service, your browsers will send cookies to Crowdin so that Crowdin can identify translators contributing to the project. You might need to update the privacy policy to reflect this aspect of cookies usage. diff --git a/src/doc/examples/lock-nodes.md b/plugin/doc/examples/lock-nodes.md similarity index 100% rename from src/doc/examples/lock-nodes.md rename to plugin/doc/examples/lock-nodes.md diff --git a/src/doc/examples/locking-multiple-stages-in-declarative-pipeline.md b/plugin/doc/examples/locking-multiple-stages-in-declarative-pipeline.md similarity index 100% rename from src/doc/examples/locking-multiple-stages-in-declarative-pipeline.md rename to plugin/doc/examples/locking-multiple-stages-in-declarative-pipeline.md diff --git a/src/doc/examples/locking-random-free-resource.md b/plugin/doc/examples/locking-random-free-resource.md similarity index 100% rename from src/doc/examples/locking-random-free-resource.md rename to plugin/doc/examples/locking-random-free-resource.md diff --git a/src/doc/examples/readme.md b/plugin/doc/examples/readme.md similarity index 100% rename from src/doc/examples/readme.md rename to plugin/doc/examples/readme.md diff --git a/src/doc/examples/scripted-vs-declarative-pipeline.md b/plugin/doc/examples/scripted-vs-declarative-pipeline.md similarity index 100% rename from src/doc/examples/scripted-vs-declarative-pipeline.md rename to plugin/doc/examples/scripted-vs-declarative-pipeline.md diff --git a/plugin/pom.xml b/plugin/pom.xml new file mode 100644 index 000000000..4837ea375 --- /dev/null +++ b/plugin/pom.xml @@ -0,0 +1,199 @@ + + + 4.0.0 + + + org.jenkins-ci.plugins + plugin + 4.54 + + + + org.6wind.jenkins + lockable-resources + + hpi + + Lockable Resources plugin + + This plugin allows to define lockable resources (such as printers, phones, + computers) that can be used by builds. If a build requires an external + resource which is already locked, it will wait for the resource to be free. + + https://github.com/jenkinsci/${project.artifactId}-plugin + 2013 + + + MIT + https://opensource.org/licenses/MIT + + + + + + basil + Basil Crow + me@basilcrow.com + + + TobiX + Tobias Gruetzmacher + tobias-git@23.gs + + + amuniz + Antonio Muñiz + amuniz@cloudbees.com + + + mPokornyETM + Martin Pokorny + martin.pokorny@etm.at + + + + + scm:git:https://github.com/${gitHubRepo}.git + scm:git:git@github.com:${gitHubRepo}.git + https://github.com/${gitHubRepo} + ${scmTag} + + + + + + io.jenkins.tools.bom + bom-2.361.x + 1798.vc671fe94856f + import + pom + + + + + + + io.jenkins.plugins + caffeine-api + + + org.jenkins-ci.plugins + structs + + + org.jenkins-ci.plugins + mailer + + + org.jenkins-ci.plugins.workflow + workflow-support + + + org.jenkins-ci.plugins + matrix-project + + + javax.annotation + javax.annotation-api + + + org.apache.commons + commons-lang3 + + + + + org.jenkins-ci.plugins + script-security + + + io.jenkins.plugins + data-tables-api + + + + + io.jenkins.plugins + pipeline-groovy-lib + + + org.jenkins-ci.plugins + git + + + + + org.jenkins-ci.plugins.workflow + workflow-support + tests + test + + + org.jenkins-ci.plugins.workflow + workflow-basic-steps + test + + + org.jenkins-ci.plugins.workflow + workflow-cps + test + + + org.jenkins-ci.plugins.workflow + workflow-job + test + + + io.jenkins + configuration-as-code + test + + + io.jenkins.configuration-as-code + test-harness + test + + + org.mockito + mockito-inline + test + + + + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + + + + + + com.coveo + fmt-maven-plugin + 2.13 + + + + + + org.jenkins-ci.tools + maven-hpi-plugin + + + FINE + + 2.0 + + + + + diff --git a/src/main/java/org/jenkins/plugins/lockableresources/BackwardCompatibility.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/BackwardCompatibility.java similarity index 94% rename from src/main/java/org/jenkins/plugins/lockableresources/BackwardCompatibility.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/BackwardCompatibility.java index 0a6abe87f..e7a3d6013 100644 --- a/src/main/java/org/jenkins/plugins/lockableresources/BackwardCompatibility.java +++ b/plugin/src/main/java/org/jenkins/plugins/lockableresources/BackwardCompatibility.java @@ -34,7 +34,7 @@ private BackwardCompatibility() {} @Initializer(after = InitMilestone.JOB_LOADED) public static void compatibilityMigration() { List resources = LockableResourcesManager.get().getResources(); - LOG.log(Level.FINE, "lockable-resource-plugin compatibility migration task run for " + resources.size() + " resources"); + LOG.log(Level.FINE, "lockable-resources-plugin compatibility migration task run for " + resources.size() + " resources"); for (LockableResource resource : resources) { List queuedContexts = resource.getQueuedContexts(); if (!queuedContexts.isEmpty()) { diff --git a/src/main/java/org/jenkins/plugins/lockableresources/ExcludeFromJacocoGeneratedReport.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/ExcludeFromJacocoGeneratedReport.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/ExcludeFromJacocoGeneratedReport.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/ExcludeFromJacocoGeneratedReport.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/LockStep.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/LockStep.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/LockStep.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/LockStep.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/LockStepExecution.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/LockStepExecution.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/LockStepExecution.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/LockStepExecution.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/LockStepResource.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/LockStepResource.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/LockStepResource.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/LockStepResource.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/LockableResource.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/LockableResource.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/LockableResource.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/LockableResource.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/LockableResources.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/LockableResources.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/LockableResources.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/LockableResources.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/LockableResourcesManager.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/LockableResourcesManager.java similarity index 99% rename from src/main/java/org/jenkins/plugins/lockableresources/LockableResourcesManager.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/LockableResourcesManager.java index 262f1115a..6cc435ca7 100644 --- a/src/main/java/org/jenkins/plugins/lockableresources/LockableResourcesManager.java +++ b/plugin/src/main/java/org/jenkins/plugins/lockableresources/LockableResourcesManager.java @@ -926,6 +926,7 @@ public boolean configure(StaplerRequest req, JSONObject json) { try (BulkChange bc = new BulkChange(this)) { // reset resources to default which are not currently locked + // TODO what about reserved, and queued ? this.resources.removeIf(resource -> !resource.isLocked()); req.bindJSON(this, json); bc.commit(); diff --git a/src/main/java/org/jenkins/plugins/lockableresources/RequiredResourcesProperty.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/RequiredResourcesProperty.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/RequiredResourcesProperty.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/RequiredResourcesProperty.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/ResourceSelectStrategy.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/ResourceSelectStrategy.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/ResourceSelectStrategy.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/ResourceSelectStrategy.java diff --git a/plugin/src/main/java/org/jenkins/plugins/lockableresources/SharedLibrary.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/SharedLibrary.java new file mode 100644 index 000000000..7945b00a9 --- /dev/null +++ b/plugin/src/main/java/org/jenkins/plugins/lockableresources/SharedLibrary.java @@ -0,0 +1,77 @@ + +package org.jenkins.plugins.lockableresources; + +import hudson.init.InitMilestone; +import hudson.init.Initializer; +import hudson.Plugin; +import hudson.PluginWrapper; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import jenkins.model.Jenkins; +import jenkins.plugins.git.GitSCMSource; +import org.jenkinsci.plugins.workflow.libs.GlobalLibraries; +import org.jenkinsci.plugins.workflow.libs.LibraryConfiguration; +import org.jenkinsci.plugins.workflow.libs.SCMSourceRetriever; + +/** + * Setup the shared library. + * + * Setup global shared library, so the end-user (administrator) has nothing to do. + */ +public final class SharedLibrary { + + public final static String LIBRARY_NAME = "lockable-resources-shared-library"; + public final static String DEVELOP_VERSION = "999999-SNAPSHOT"; + + private static final Logger LOG = Logger.getLogger(SharedLibrary.class.getName()); + + private SharedLibrary() {} + + @Initializer(after = InitMilestone.SYSTEM_CONFIG_LOADED) + public static void setSharedLib() { + LOG.log(Level.FINE, "lockable-resources-plugin configure shared libraries"); + + + GlobalLibraries gl = GlobalLibraries.get(); + List libs = gl.getLibraries(); + boolean isAdded = false; + for (LibraryConfiguration lib : libs) { + if (lib != null && lib.getName().equals(LIBRARY_NAME)) { + // our release tags contains . (dot) and git branch can not contains . + // this check will allow to set as default some branch like 'hot-fix-nr1' + // or 'master' or 'what/ever'. + if ((lib.getDefaultVersion() == null) || lib.getDefaultVersion().contains(".")) { + lib.setDefaultVersion(getCurrentVersion()); + } + isAdded = true; + return; + } + } + + if (!isAdded) { + GitSCMSource scm = new GitSCMSource("https://github.com/jenkinsci/lockable-resources-plugin"); + SCMSourceRetriever retriever = new SCMSourceRetriever(scm); + retriever.setLibraryPath("shared-library/"); + LibraryConfiguration ourSharedLib = new LibraryConfiguration(LIBRARY_NAME, retriever); + // scm.setDefaultVersion("master"); + ourSharedLib.setDefaultVersion(getCurrentVersion()); + ourSharedLib.setAllowVersionOverride(true); + libs.add(ourSharedLib); + } + gl.setLibraries(libs); + } + + public static String getCurrentVersion() { + String myVersion = ""; + for (PluginWrapper plugin : Jenkins.get().pluginManager.getPlugins()) { + if (plugin.getShortName().equals("lockable-resources")) { + if (!plugin.getVersion().startsWith(DEVELOP_VERSION)) { + myVersion = plugin.getVersion(); + } + } + } + + return myVersion; + } +} diff --git a/src/main/java/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/actions/ResourceVariableNameAction.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/actions/ResourceVariableNameAction.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/actions/ResourceVariableNameAction.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/actions/ResourceVariableNameAction.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/queue/LockRunListener.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/queue/LockRunListener.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/queue/LockRunListener.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/queue/LockRunListener.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/queue/LockableResourcesCandidatesStruct.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/queue/LockableResourcesCandidatesStruct.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/queue/LockableResourcesCandidatesStruct.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/queue/LockableResourcesCandidatesStruct.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/queue/LockableResourcesQueueTaskDispatcher.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/queue/LockableResourcesQueueTaskDispatcher.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/queue/LockableResourcesQueueTaskDispatcher.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/queue/LockableResourcesQueueTaskDispatcher.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/queue/QueuedContextStruct.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/queue/QueuedContextStruct.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/queue/QueuedContextStruct.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/queue/QueuedContextStruct.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/queue/Utils.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/queue/Utils.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/queue/Utils.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/queue/Utils.java diff --git a/src/main/java/org/jenkins/plugins/lockableresources/util/SerializableSecureGroovyScript.java b/plugin/src/main/java/org/jenkins/plugins/lockableresources/util/SerializableSecureGroovyScript.java similarity index 100% rename from src/main/java/org/jenkins/plugins/lockableresources/util/SerializableSecureGroovyScript.java rename to plugin/src/main/java/org/jenkins/plugins/lockableresources/util/SerializableSecureGroovyScript.java diff --git a/src/main/resources/index.jelly b/plugin/src/main/resources/index.jelly similarity index 100% rename from src/main/resources/index.jelly rename to plugin/src/main/resources/index.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_cs.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_cs.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_cs.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_cs.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_de.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_de.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_de.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_de.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_fr.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_fr.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_fr.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_fr.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_sk.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_sk.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_sk.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/config_sk.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-inversePrecedence.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-inversePrecedence.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-inversePrecedence.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-inversePrecedence.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-label.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-label.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-label.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-label.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-quantity.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-quantity.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-quantity.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-quantity.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-resource.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-resource.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-resource.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-resource.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-resourceSelectStrategy.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-resourceSelectStrategy.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-resourceSelectStrategy.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-resourceSelectStrategy.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-skipIfLocked.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-skipIfLocked.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-skipIfLocked.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-skipIfLocked.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-variable.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-variable.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-variable.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStep/help-variable.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_cs.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_cs.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_cs.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_cs.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_de.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_de.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_de.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_de.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_fr.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_fr.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_fr.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_fr.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_sk.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_sk.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_sk.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/config_sk.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/help-label.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/help-label.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/help-label.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/help-label.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/help-quantity.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/help-quantity.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/help-quantity.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/help-quantity.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/help-resource.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/help-resource.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/help-resource.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockStepResource/help-resource.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_cs.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_cs.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_cs.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_cs.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_de.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_de.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_de.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_de.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_fr.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_fr.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_fr.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_fr.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_sk.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_sk.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_sk.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResource/config_sk.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_cs.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_cs.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_cs.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_cs.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_de.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_de.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_de.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_de.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_fr.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_fr.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_fr.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_fr.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_sk.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_sk.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_sk.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/LockableResourcesManager/config_sk.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/Messages.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/Messages.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/Messages.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/Messages.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/Messages_cs.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/Messages_cs.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/Messages_cs.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/Messages_cs.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/Messages_de.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/Messages_de.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/Messages_de.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/Messages_de.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/Messages_fr.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/Messages_fr.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/Messages_fr.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/Messages_fr.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/Messages_sk.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/Messages_sk.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/Messages_sk.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/Messages_sk.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_cs.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_cs.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_cs.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_cs.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_de.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_de.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_de.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_de.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_fr.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_fr.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_fr.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_fr.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_sk.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_sk.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_sk.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/config_sk.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-labelName.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-labelName.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-labelName.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-labelName.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceMatchScript.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceMatchScript.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceMatchScript.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceMatchScript.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceNames.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceNames.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceNames.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceNames.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceNamesVar.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceNamesVar.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceNamesVar.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceNamesVar.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceNumber.html b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceNumber.html similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceNumber.html rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/RequiredResourcesProperty/help-resourceNumber.html diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/_api.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/_api.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/_api.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/_api.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_cs.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_cs.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_cs.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_cs.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_de.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_de.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_de.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_de.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_fr.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_fr.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_fr.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_fr.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_sk.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_sk.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_sk.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/index_sk.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/noteForm.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/noteForm.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/noteForm.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/noteForm.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_cs.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_cs.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_cs.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_cs.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_de.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_de.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_de.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_de.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_fr.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_fr.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_fr.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_fr.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_sk.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_sk.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_sk.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableLabels/table_sk.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableQueue/table.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableQueue/table.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableQueue/table.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableQueue/table.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableQueue/table.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableQueue/table.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableQueue/table.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableQueue/table.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_cs.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_cs.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_cs.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_cs.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_de.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_de.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_de.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_de.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_fr.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_fr.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_fr.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_fr.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_sk.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_sk.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_sk.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootAction/tableResources/table_sk.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_cs.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_cs.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_cs.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_cs.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_de.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_de.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_de.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_de.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_fr.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_fr.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_fr.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_fr.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_sk.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_sk.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_sk.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction/index_sk.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/reason/cell.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/reason/cell.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/reason/cell.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/reason/cell.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/reason/cell.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/reason/cell.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/reason/cell.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/reason/cell.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request/cell.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request/cell.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request/cell.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request/cell.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request/cell.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request/cell.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request/cell.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request/cell.properties diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request_type/cell.jelly b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request_type/cell.jelly similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request_type/cell.jelly rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request_type/cell.jelly diff --git a/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request_type/cell.properties b/plugin/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request_type/cell.properties similarity index 100% rename from src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request_type/cell.properties rename to plugin/src/main/resources/org/jenkins/plugins/lockableresources/queue/LockableResourcesStruct/request_type/cell.properties diff --git a/src/main/webapp/css/style.css b/plugin/src/main/webapp/css/style.css similarity index 100% rename from src/main/webapp/css/style.css rename to plugin/src/main/webapp/css/style.css diff --git a/src/main/webapp/js/lockable-resources.js b/plugin/src/main/webapp/js/lockable-resources.js similarity index 100% rename from src/main/webapp/js/lockable-resources.js rename to plugin/src/main/webapp/js/lockable-resources.js diff --git a/src/test/java/org/jenkins/plugins/lockableresources/ConfigurationAsCodeTest.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/ConfigurationAsCodeTest.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/ConfigurationAsCodeTest.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/ConfigurationAsCodeTest.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/FreeStyleProjectTest.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/FreeStyleProjectTest.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/FreeStyleProjectTest.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/FreeStyleProjectTest.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/InteroperabilityTest.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/InteroperabilityTest.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/InteroperabilityTest.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/InteroperabilityTest.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/LockStepHardKillTest.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepHardKillTest.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/LockStepHardKillTest.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepHardKillTest.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/LockStepTest.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepTest.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/LockStepTest.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepTest.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/LockStepTestBase.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepTestBase.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/LockStepTestBase.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepTestBase.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/LockStepTest_manualUnreserveUnblocksJob.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepTest_manualUnreserveUnblocksJob.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/LockStepTest_manualUnreserveUnblocksJob.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepTest_manualUnreserveUnblocksJob.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/LockStepTest_reserveInsideLockHonoured.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepTest_reserveInsideLockHonoured.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/LockStepTest_reserveInsideLockHonoured.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepTest_reserveInsideLockHonoured.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/LockStepTest_setReservedByInsideLockHonoured.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepTest_setReservedByInsideLockHonoured.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/LockStepTest_setReservedByInsideLockHonoured.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepTest_setReservedByInsideLockHonoured.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/LockStepWithRestartTest.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepWithRestartTest.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/LockStepWithRestartTest.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/LockStepWithRestartTest.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/LockableResourceApiTest.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/LockableResourceApiTest.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/LockableResourceApiTest.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/LockableResourceApiTest.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/LockableResourceManagerTest.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/LockableResourceManagerTest.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/LockableResourceManagerTest.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/LockableResourceManagerTest.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/LockableResourceRootActionSEC1361Test.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/LockableResourceRootActionSEC1361Test.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/LockableResourceRootActionSEC1361Test.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/LockableResourceRootActionSEC1361Test.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/LockableResourceTest.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/LockableResourceTest.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/LockableResourceTest.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/LockableResourceTest.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/TestHelpers.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/TestHelpers.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/TestHelpers.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/TestHelpers.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootActionTest.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootActionTest.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootActionTest.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/actions/LockableResourcesRootActionTest.java diff --git a/src/test/java/org/jenkins/plugins/lockableresources/util/SerializableSecureGroovyScriptTest.java b/plugin/src/test/java/org/jenkins/plugins/lockableresources/util/SerializableSecureGroovyScriptTest.java similarity index 100% rename from src/test/java/org/jenkins/plugins/lockableresources/util/SerializableSecureGroovyScriptTest.java rename to plugin/src/test/java/org/jenkins/plugins/lockableresources/util/SerializableSecureGroovyScriptTest.java diff --git a/src/test/resources/org/jenkins/plugins/lockableresources/casc_expected_output.yml b/plugin/src/test/resources/org/jenkins/plugins/lockableresources/casc_expected_output.yml similarity index 100% rename from src/test/resources/org/jenkins/plugins/lockableresources/casc_expected_output.yml rename to plugin/src/test/resources/org/jenkins/plugins/lockableresources/casc_expected_output.yml diff --git a/src/test/resources/org/jenkins/plugins/lockableresources/configuration-as-code.yml b/plugin/src/test/resources/org/jenkins/plugins/lockableresources/configuration-as-code.yml similarity index 100% rename from src/test/resources/org/jenkins/plugins/lockableresources/configuration-as-code.yml rename to plugin/src/test/resources/org/jenkins/plugins/lockableresources/configuration-as-code.yml diff --git a/plugin/src/test/resources/plugins/jobConfigHistory.hpi b/plugin/src/test/resources/plugins/jobConfigHistory.hpi new file mode 100644 index 000000000..236a923ec Binary files /dev/null and b/plugin/src/test/resources/plugins/jobConfigHistory.hpi differ diff --git a/pom.xml b/pom.xml index 0e56180b0..21eff20cf 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ org.6wind.jenkins lockable-resources ${changelist} - hpi + pom Lockable Resources plugin @@ -45,6 +45,11 @@ Antonio Muñiz amuniz@cloudbees.com + + mPokornyETM + Martin Pokorny + martin.pokorny@etm.at + @@ -55,7 +60,7 @@ - 999999-SNAPSHOT + 999999-SNAPSHOT 2.361.4 jenkinsci/${project.artifactId}-plugin Max @@ -74,85 +79,6 @@ - - - io.jenkins.plugins - caffeine-api - - - org.jenkins-ci.plugins - structs - - - org.jenkins-ci.plugins - mailer - - - org.jenkins-ci.plugins.workflow - workflow-support - - - org.jenkins-ci.plugins - matrix-project - - - javax.annotation - javax.annotation-api - - - org.apache.commons - commons-lang3 - - - - - org.jenkins-ci.plugins - script-security - - - io.jenkins.plugins - data-tables-api - - - - - org.jenkins-ci.plugins.workflow - workflow-support - tests - test - - - org.jenkins-ci.plugins.workflow - workflow-basic-steps - test - - - org.jenkins-ci.plugins.workflow - workflow-cps - test - - - org.jenkins-ci.plugins.workflow - workflow-job - test - - - io.jenkins - configuration-as-code - test - - - io.jenkins.configuration-as-code - test-harness - test - - - org.mockito - mockito-inline - test - - - repo.jenkins-ci.org @@ -189,4 +115,9 @@ + + + plugin + shared-library + diff --git a/shared-library/CONTRIBUTING.md b/shared-library/CONTRIBUTING.md new file mode 100644 index 000000000..f050cbf2c --- /dev/null +++ b/shared-library/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing + +Fork this repository. Make your changes, tests it and provide a pull-request. That`s it. + +## Setup environment + +General you need same setup as to build the plugin. +Please refer to the plugin [CONTRIBUTING](../plugin/CONTRIBUTING.md) document for details on how to proceed! + +## Build and test + +If you have the proper environment, typing: + +```sh +mvn clean verify +``` + +should provide tests for shared library + +```sh +mvn hpi:run -Dhudson.plugins.git.GitSCM.ALLOW_LOCAL_CHECKOUT=true +``` + +allows you to spin up a test Jenkins instance on [localhost] to test your +local changes before committing. + +The plugin shall set everything you need on demand. + +> Keep in mind, that shared library works only with committed changes. + +The option `hudson.plugins.git.GitSCM.ALLOW_LOCAL_CHECKOUT=true` allows you to load changes from locale repository without pushing to remote repository. diff --git a/shared-library/README.md b/shared-library/README.md new file mode 100644 index 000000000..747e6d52c --- /dev/null +++ b/shared-library/README.md @@ -0,0 +1,64 @@ +# Jenkins lockable-resources-shared-library + +Jenkins shared library to extends [lockable-resources-plugin](https://github.com/jenkinsci/lockable-resources-plugin) as a groovy code. + +The goal is to extend lockable-resources-plugin functionality in simple way and provide a solution for end-user (administrators) with much more then one simple lock() step. + +Many users has own solutions like create resources, or check it is isFree(). +A lot of them are done in uncommon way and a lot of them make some useful magic. +This shall helps to all Jenkins administrators to interact with lockable-resources: + ++ without coding own code ++ spare maintenance on own side ++ spare testing after each plugin and jenkins update ++ customizing ++ share ideas with community ++ do not copy examples from untrusted zones in to your code ++ be sure your solutions are done in proper / supported way + +--- + +## Usage + +The lockable-resources-plugin will setup on your Jenkins instance new global shared library named **lockable-resources-shared-library**. + +**Default version** of branch use release-IDs. It will be automatically updated on every plugin update. That means you does not care about that in normal case. +This grants you, that the plugin and shared library works together. +Of cores you can change the branch name every time you need. For example to *master*. +In this case you will pull last commit from master. It is fine to get the changes before officially release. But it is potential risk, because shared-lib may not work with you plugin version. + +Enjoy in your pipelines. + + +### Customizing + +Fork this repository and change what you want. + +> You can share your ideas with community, when you create pull-request into this repository. This will help you to eliminate maintenance and help the community with more power. + +> Keep in mind, that we can not care about your changes in forked repository. It means, when this repository became an update, you need merge changes by your self. + +--- + +## Report an Issue + +Please report issues and enhancements through the [GitHub](https://github.com/jenkinsci/lockable-resources-plugin/issues/new/choose). + +If you have an example to share, please create a [new documentation issue](https://github.com/jenkinsci/lockable-resources-plugin/issues/new?assignees=&labels=documentation&template=3-documentation.yml) and provide additional examples as a [pull request](https://github.com/jenkinsci/lockable-resources-plugin/pulls) to the repository. + +If you have a question, please open a [GitHub issue](https://github.com/jenkinsci/lockable-resources-plugin/issues/new/choose) with your question. + +--- + +## Contributing + +Contributions are welcome, please refer to the separate [CONTRIBUTING](CONTRIBUTING.md) document for details on how to proceed! + +Join [Gitter channel](https://gitter.im/jenkinsci/lockable-resources) to discuss your ideas with the community. + +--- + +## License + +All source code is licensed under the MIT license. +See [LICENSE](../LICENSE.txt) diff --git a/shared-library/doc/examples/readme.md b/shared-library/doc/examples/readme.md new file mode 100644 index 000000000..f467d131a --- /dev/null +++ b/shared-library/doc/examples/readme.md @@ -0,0 +1,5 @@ +# Examples + ++ [Mirror nodes to lockable-resources](../../tests/src/main/jenkins/io/jenkins/pipeline/sample/mirrorNodesToLockableResources.groovy) + ++ [Lock node](../../tests/src/main/jenkins/io/jenkins/pipeline/sample/lockNode.groovy) diff --git a/shared-library/pom.xml b/shared-library/pom.xml new file mode 100644 index 000000000..f239964c9 --- /dev/null +++ b/shared-library/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + org.sample + foo-parent + ${changelist} + pom + Global Shared Library for lockable-resources-plugin + Extends lockable-resources-plugin with groovy helpers + + tests + + + + + + io.jenkins.tools.bom + bom-2.361.x + 1798.vc671fe94856f + import + pom + + + + + + + + io.jenkins.plugins + pipeline-groovy-lib + + + org.jenkins-ci.plugins + git + + + + diff --git a/shared-library/src/io/jenkins/library/lockableresources/Resource.groovy b/shared-library/src/io/jenkins/library/lockableresources/Resource.groovy new file mode 100644 index 000000000..7a141b223 --- /dev/null +++ b/shared-library/src/io/jenkins/library/lockableresources/Resource.groovy @@ -0,0 +1,353 @@ +#!groovy +package io.jenkins.library.lockableresources; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.model.Label; +import hudson.model.labels.LabelAtom; +import java.io.Serializable; +import java.util.Collections; +import io.jenkins.library.lockableresources.ResourcesManager as RM; +import io.jenkins.library.lockableresources.ResourceLabel; +import org.jenkins.plugins.lockableresources.LockableResource; +import org.jenkins.plugins.lockableresources.LockableResourcesManager as LRM; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +//----------------------------------------------------------------------------- +/** Provide interface to org.jenkins.plugins.lockableresources.LockableResource + + Since LockableResource contains transient variables it is not serializable, and + therefore not direct usable in the Jenkins pipelines. You will get many troubles + by using NonCPS annotations. + + All public function may be used in Groovy pipelines, except marked by NoExternalUse + annotation. +*/ +class Resource implements Serializable { + + /** Lockable resource. + + private - because you shall use only public functions from this class, + transient - because of NonCPS + */ + private transient LockableResource resource; + + //--------------------------------------------------------------------------- + /** Init Resource from resource-name. + + When the resource does not exist, it will create new object, but it will be + not added in to resource. See also Resource.create(); + */ + public Resource(@NonNull String resourceName) { + this.resource = LRM.get().fromName(resourceName); + if (this.resource == null) { + this.resource = new LockableResource(resourceName); + } + } + + //--------------------------------------------------------------------------- + public Resource(@NonNull LockableResource resource) { + this.resource = resource; + } + + //---------------------------------------------------------------------------- + /** Create new resource. + @parameter properties Resource properties as Map. To see possible values see + also Resource.fromMap(); + + @exception Raise exception, when resource exists. + */ + public void create(Map properties = null) { + if (this.exists()) { + throw new Exception('Resource ' + this.getName() + ' currently exists!' + + 'Therefore can not be created.'); + } + if (properties != null) { + this.fromMap(properties); + } + LRM.get().getResources().add(this.resource); + } + + //---------------------------------------------------------------------------- + /** Save the resource. + + Exactly save all resources. + */ + public void save() { + RM.save(); + } + + //---------------------------------------------------------------------------- + /** Check if the resource exists or not. + @return Returns true when exist, false otherwise. + */ + public boolean exists() { + return RM.resourceExists(this.getName()); + } + + //---------------------------------------------------------------------------- + /** Check if the resource is free. + + @return Returns true when resource is free for use, false otherwise. + */ + public boolean isFree() { + return (!this.resource.isLocked() && !this.resource.isReserved() && !this.resource.isQueued()); + } + + //---------------------------------------------------------------------------- + /** @return Returns resource name. + */ + @NonNull + public String getName() { + return this.resource.name; + } + + //---------------------------------------------------------------------------- + /** Converts resource to string. + @return resource object as String. + */ + @NonNull + public String toString() { + return this.getName(); + } + + //---------------------------------------------------------------------------- + /** Get resource description. + @return Resource description or null when not set. + */ + @CheckForNull + public String getDescription() { + return this.resource.description; + } + + //---------------------------------------------------------------------------- + /** Set resource description. + + To store this changes permanently call Resource.save() afterwards. + @param description Resource description to be set. + */ + public void setDescription(String description) { + this.resource.setDescription(description); + } + + //---------------------------------------------------------------------------- + /** Get resource note. + @return Resource note or null when not set. + */ + @CheckForNull + public String getNote() { + return this.resource.note; + } + + //---------------------------------------------------------------------------- + /** Set resource note. + + To store this changes permanently call Resource.save() afterwards. + @param note Resource note to be set. + */ + public void setNote(String note) { + this.resource.setNote(note); + } + + //---------------------------------------------------------------------------- + /** Checks if the resource is ephemeral (temporary). + + @return true when ephemeral, false otherwise. + */ + public boolean isEphemeral() { + return this.resource.isEphemeral(); + } + + //---------------------------------------------------------------------------- + /** Returns assigned labels. + + @return List of assigned resources. List might be empty, but newer null. + */ + public List getLabels() { + List list = []; + for(String label : this.resource.labelsAsList) { + list.push(new ResourceLabel(label)); + } + return list; + } + + //---------------------------------------------------------------------------- + /** Assign labels to resource. + + To store this changes permanently call Resource.save() afterwards. + @parameter labels Labels shall be assigned to resource. To see possible + values see also Resource.toLabelsString(); + */ + public void setLabels(@NonNull def labels) { + this.resource.setLabels(Resource.toLabelsString(labels)); + } + + //---------------------------------------------------------------------------- + /** Add label to assigned labels. + + In case the label is assigned just now, it will be ignored. + To store this changes permanently call Resource.save() afterwards. + @param label Label to be added. + */ + public void addLabel(@NonNull ResourceLabel label) { + if (this.resource.hasLabel(label.getName())) { + return; + } + this.resource.labelsAsList.push(label.getName()); + } + + //---------------------------------------------------------------------------- + /** Remove label from assigned labels. + + To store this changes permanently call Resource.save() afterwards. + @param label Label to be removed. + */ + public void removeLabel(@NonNull ResourceLabel label) { + if (!this.resource.hasLabel(label.getName())) { + return; + } + this.resource.labelsAsList.remove(label.getName()); + } + + //---------------------------------------------------------------------------- + /** Check if the label is assigned to resource. + + @param label Label to be checked. + @return true when the resource has the label. + */ + public boolean hasLabel(@NonNull String label) { + return this.resource.hasLabel(label); + } + + //---------------------------------------------------------------------------- + /** Check if the label is assigned to resource. + + @param label Label to be checked. + @return true when the resource has the label. + */ + public boolean hasLabel(@NonNull ResourceLabel label) { + return hasLabel(label.getName()); + } + + //---------------------------------------------------------------------------- + /** Convert resource to Map. + + This is used intern in the library for filtering, sorting ... + @return Resource properties as Map + */ + @Restricted(NoExternalUse.class) + public Map toMap() { + Map map = [ + 'name' : this.getName(), + 'description' : this.getDescription(), + 'note' : this.getNote(), + 'labels' : this.getLabels(), + 'isFree' : this.isFree(), + 'reservedTimestamp' : this.resource.getReservedTimestamp(), + 'isLocked' : this.resource.isLocked(), + 'lockedBy' : this.resource.getBuildName(), + 'isReserved' : this.resource.isReserved(), + 'reservedBy' : this.resource.getReservedBy(), + 'isStolen' : this.resource.isStolen(), + 'isQueued' : this.resource.isQueued() + ]; + + // in case resource is a node, add the node properties too. + if (this.hasLabel(ResourceLabel.NODE_LABEL)) { + Computer computer = jenkins.model.Jenkins.instance.getComputer(this.getName()); + if (computer != null) { + Map compMap = [:]; + compMap['isOnline'] = computer.isOnline(); + // object of OfflineCause or null + compMap['offlineCause'] = computer.getOfflineCause(); + // The time when this computer last became idle. + compMap['idleStartMilliseconds'] = computer.getIdleStartMilliseconds(); + // true if this computer has some idle executors that can take more workload + compMap['isPartiallyIdle'] = computer.isPartiallyIdle(); + // true if all the executors of this computer are idle. + compMap['isIdle'] = computer.isPartiallyIdle(); + // The current size of the executor pool for this computer. + compMap['countExecutors'] = computer.countExecutors(); + // The number of executors that are doing some work right now. + compMap['countBusy'] = computer.countBusy(); + // The number of idle {@link Executor}s that can start working immediately. + compMap['countIdle'] = computer.countIdle(); + + map['node'] = compMap; + } + } + + return map; + } + + //---------------------------------------------------------------------------- + /** Convert Map to resource. + + @param map Properties: + map.description Resource description. + map.note Resource note. + map.labels Resource labels. + */ + @Restricted(NoExternalUse.class) + public Map fromMap(@NonNull Map map) { + this.resource.setDescription(map.description); + this.resource.setNote(map.note); + this.resource.setLabels(Resource.toLabelsString(map.labels)); + } + + //---------------------------------------------------------------------------- + /** Convert labels param to string. + + Original LockableResource has stored labels as String delimited by ' '. + Therefore we need to convert List or List to string. + Of cores String is supported too. + Any other data types will raise a exception. + */ + private static String toLabelsString(def labels) { + String labelsString = ""; + if (labels == null) { + return labelsString; + } else if (labels instanceof String) { + labelsString = labels; + } else if (labels instanceof List) { + labelsString = labels.join(' '); + } else if (labels instanceof List) { + for (ResourceLabel label : labels) { + labelsString += label.name + ' '; + } + } else { + throw(new Exception("Unsupported labels conversion: " + labels.class.name + " " + labels)); + } + + return labelsString.trim(); + } + + //---------------------------------------------------------------------------- + /** Check if assigned labels matches with label-expression. + + It use exact same matching as node() step. + @return Return true when match, false otherwise. + */ + @Restricted(NoExternalUse.class) + public boolean matches(Label labelExpression) { + List ls = []; + for(ResourceLabel resourceLabel : this.getLabels()) { + ls.push(resourceLabel.getName()); + } + + return _matches(labelExpression, ls); + } + + //---------------------------------------------------------------------------- + /// NonCPS because LabelAtom is not serializable + @NonCPS + private boolean _matches(Label labelExpression, def _labels) { + Collection atomLabels = []; + for(String resourceLabel : _labels) { + atomLabels.push(new LabelAtom(resourceLabel)); + } + + return labelExpression.matches(atomLabels); + } +} diff --git a/shared-library/src/io/jenkins/library/lockableresources/ResourceLabel.groovy b/shared-library/src/io/jenkins/library/lockableresources/ResourceLabel.groovy new file mode 100644 index 000000000..0bbaae2f1 --- /dev/null +++ b/shared-library/src/io/jenkins/library/lockableresources/ResourceLabel.groovy @@ -0,0 +1,40 @@ +#!groovy +package io.jenkins.library.lockableresources; + +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.Util; +import java.io.Serializable; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +//----------------------------------------------------------------------------- +// !!!!! do not use outside this library !!!! +class ResourceLabel implements Serializable { + + /** This label will be used for all node-resources.*/ + public static final String NODE_LABEL = 'node'; + private String name; + + //--------------------------------------------------------------------------- + public ResourceLabel(@NonNull String name) { + this.name = Util.fixNull(name); + } + + //--------------------------------------------------------------------------- + /** Convert to String + */ + @NonNull + @Restricted(NoExternalUse.class) + public String toString() { + return this.name; + } + + //--------------------------------------------------------------------------- + /** Get label name. + */ + @NonNull + @Restricted(NoExternalUse.class) + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/shared-library/src/io/jenkins/library/lockableresources/ResourcesManager.groovy b/shared-library/src/io/jenkins/library/lockableresources/ResourcesManager.groovy new file mode 100644 index 000000000..1018a0bcc --- /dev/null +++ b/shared-library/src/io/jenkins/library/lockableresources/ResourcesManager.groovy @@ -0,0 +1,220 @@ +#!groovy +package io.jenkins.library.lockableresources; + +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.io.Serializable; +import java.util.Collections; +import io.jenkins.library.lockableresources.Resource; +import io.jenkins.library.lockableresources.ResourceLabel; +import io.jenkins.library.lockableresources.Utils; +import org.jenkins.plugins.lockableresources.LockableResourcesManager as LRM; +import org.jenkins.plugins.lockableresources.LockableResource; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + +//----------------------------------------------------------------------------- +// !!!!! do not use outside this library !!!! +class ResourcesManager implements Serializable { + + //--------------------------------------------------------------------------- + /** Get Resource by resource name. + + @return Resource when exists, null otherwise. + */ + @Restricted(NoExternalUse.class) + @CheckForNull + public static Resource getResource(@NonNull String resourceName) { + def r = LRM.get().fromName(resourceName); + if (r == null) { + return null; + } + return new Resource(r); + } + + //--------------------------------------------------------------------------- + /** Get Resource by resource name or die. + + @return Resource when exists, raise exception otherwise. + */ + @Restricted(NoExternalUse.class) + public static Resource getResourceOrDie(@NonNull String resourceName) { + Resource resource = ResourcesManager.getResource(resourceName); + if (resource == null) { + throw new Exception("Lockable resource '$resourceName' does not exist!"); + } + return resource; + } + + //--------------------------------------------------------------------------- + /** Get list of resources by resource names. + + @return Return list of resources matched by names. It might be empty, but + newer null. + + @exception When one resource does not exists. See also Resource.getResourceOrDie() + */ + @Restricted(NoExternalUse.class) + public static List getResources(@NonNull List resourceNames) { + List retList = []; + for (String resourceName : resourceNames) { + retList.push(ResourcesManager.getResourceOrDie(resourceName)); + } + return retList; + } + + //--------------------------------------------------------------------------- + /** Get resources matched by resource label. + + @param resourceLabel Label to be search. + @param opts See also ResourceLabel.reOrder(); + @return Return list of resources matched by resource label. It might be empty, + but newer null. + */ + @Restricted(NoExternalUse.class) + public static List getResources(ResourceLabel resourceLabel, Map opts = [:]) { + return reOrder(toSafeList(LRM.get().getResourcesWithLabel(resourceLabel.name, [:])), opts); + } + + //--------------------------------------------------------------------------- + /** Get resources matched by Groovy expression. + + @param closure Groovy expression. + @param opts See also ResourceLabel.reOrder(); + @return Return list of resources matched by resource label. It might be empty, + but newer null. + */ + @Restricted(NoExternalUse.class) + public static List getResources(Closure closure, Map opts = [:]) { + opts = Utils.fixNullMap(opts); + List matches = []; + + for(Resource resource : getAllResources()) { + boolean match = closure(resource); + Utils.echo resource.toString() + 'match: ' + match + ', closure ' + closure.toString() + if (match) { + matches.push(resource); + } + } + + return reOrder(matches, opts); + } + + //--------------------------------------------------------------------------- + /** Get all resources. + */ + @Restricted(NoExternalUse.class) + public static List getAllResources() { + return toSafeList(LRM.get().getResources()); + } + + //--------------------------------------------------------------------------- + /** Save all resources. + */ + @Restricted(NoExternalUse.class) + public static void save() { + LRM.get().save(); + } + + //--------------------------------------------------------------------------- + /** Check if resource exists. + + @param resourceName Resource name. + */ + @Restricted(NoExternalUse.class) + public static boolean resourceExists(@NonNull String resourceName) { + return LRM.get().fromName(resourceName) != null; + } + + + //--------------------------------------------------------------------------- + /** */ + private static List reOrder(List allMatches, Map opts) { + opts = Utils.fixNullMap(opts); + + final int quantity = opts.quantity != null ? opts.quantity : 0; + final int minCount = opts.minCount != null ? opts.minCount : quantity; + + if ((quantity > 0) && (minCount > quantity)) { + // TODO make specific Exception class + throw(new Exception("Parameter mismatch minCount $minCount vs quantity $quantity")); + } + if (minCount > allMatches.size()) { + // TODO make specific Exception class + throw(new Exception("You has expected $quantity resource(s), but there are currently only " + allMatches.size())); + } + + if (opts.randomize != null) { + Collections.shuffle(allMatches); + } + + if (opts.sort != null) { + allMatches = sort(allMatches); + } + + if (quantity == 0) { + // return all possible resources + return allMatches; + } + + List retList = []; + for(int i = 0; i < quantity; i++) { + retList.push(allMatches[i]); + } + + return retList; + } + + //--------------------------------------------------------------------------- + private static List sort(List resources) { + // get current state and property of resources to eliminate + // java.lang.IllegalArgumentException: Comparison method violates its general contract! + // otherwise nobody can grant, that the resource state/property has been not changed + // during sorting + // It is only sporadic issue, but it will abort our build + List list = []; + + for (Resource resource : resources) { + list.push(resource.toMap()); + } + + // in extra function, because of NonCPS + _sort(list); + + resources = []; + for (Map map : list) { + resources.add(new Resource(map.name)); + } + + return resources; + } + + //---------------------------------------------------------------------------- + // NonCps because list.sort() is NON-CPS. See https://issues.jenkins.io/browse/JENKINS-44924 + @NonCPS + private static _sort(Listlist) { + list.sort(new OrderBy([ + { !it.isFree }, + // all free nodes first + { it.node != null && !it.node.isOnline }, + // 0 executors means, there is something running + { it.node != null ? -it.node.countIdle : null }, + // keep last idle node on the end + { it.node != null ? it.node.idleStartMilliseconds : null } + ]) + ); + } + + + //---------------------------------------------------------------------------- + //maybe @NonCPS + public static List toSafeList(@NonNull List list) { + List ret = []; + for(LockableResource r : list) { + if (r != null && r.getName() != null) { + ret.push(new Resource(r)); + } + } + return ret; + } +} \ No newline at end of file diff --git a/shared-library/src/io/jenkins/library/lockableresources/Utils.groovy b/shared-library/src/io/jenkins/library/lockableresources/Utils.groovy new file mode 100644 index 000000000..8430e4267 --- /dev/null +++ b/shared-library/src/io/jenkins/library/lockableresources/Utils.groovy @@ -0,0 +1,25 @@ +#!groovy +package io.jenkins.library.lockableresources; + +class Utils { + + public static def globalScope = null; + + //--------------------------------------------------------------------------- + /** */ + public static Map fixNullMap(Map map) { + if (map == null) { + return [:]; + } + return map; + } + + //--------------------------------------------------------------------------- + /** */ + public static echo(String msg) { + if (globalScope == null) { + return; + } + globalScope.echo(msg); + } +} \ No newline at end of file diff --git a/shared-library/tests/.gitignore b/shared-library/tests/.gitignore new file mode 100644 index 000000000..7e092d6a9 --- /dev/null +++ b/shared-library/tests/.gitignore @@ -0,0 +1,3 @@ + +/target/ +/work/ diff --git a/shared-library/tests/pom.xml b/shared-library/tests/pom.xml new file mode 100644 index 000000000..8a6d00aef --- /dev/null +++ b/shared-library/tests/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + org.sample + lockable-resources-shared-lib + ${changelist} + lockable-resources-plugin Global Shared Library- Tests + Tests for lockable-resources-plugin global Shared Library + https://jenkins.io/doc/book/pipeline/shared-libraries/ + + + MIT License + https://opensource.org/licenses/MIT + + + + 1.8 + 1.8 + + 2.4.21 + 4.13.2 + 1.1 + 3.7.0 + + + + org.codehaus.groovy + groovy-all + ${groovy.version} + test + + + junit + junit + ${junit.version} + test + + + com.lesfurets + jenkins-pipeline-unit + ${jenkins-pipeline-unit.version} + test + + + + src/test/groovy + + + src/main/jenkins + + + + + ../shared-library + libs/shared-library@master + + + + + org.6wind.jenkins + lockable-resources + 999999-SNAPSHOT + + + org.codehaus.groovy + groovy-eclipse-compiler + ${groovy-eclipse-compiler.version} + true + + + maven-compiler-plugin + 3.8.1 + + groovy-eclipse-compiler + + + + org.codehaus.groovy + groovy-eclipse-compiler + ${groovy-eclipse-compiler.version} + + + + org.codehaus.groovy + groovy-eclipse-batch + 3.0.7-02 + + + + + + diff --git a/shared-library/tests/src/main/jenkins/io/jenkins/pipeline/sample/lockNode.groovy b/shared-library/tests/src/main/jenkins/io/jenkins/pipeline/sample/lockNode.groovy new file mode 100644 index 000000000..497879a23 --- /dev/null +++ b/shared-library/tests/src/main/jenkins/io/jenkins/pipeline/sample/lockNode.groovy @@ -0,0 +1,154 @@ +package io.jenkins.pipeline.sample + +@Library('lockable-resources-shared-library')_ + +// imports are for helpers +// The step lockNode() self is global and does not need it +import hudson.slaves.JNLPLauncher +import hudson.slaves.DumbSlave +import hudson.slaves.RetentionStrategy +import io.jenkins.library.lockableresources.Utils; + +// Utils.globalScope = this + +//----------------------------------------------------------------------------- +// Example 1 +// lock node by node-name +Map options = [allocateExecutor : false]; +createNode('SampleNode1'); +lockNode('SampleNode1', options) { + whereAmI(); +} + +//----------------------------------------------------------------------------- +// Example 2 +// Lock node by node-label matching +createNode('SampleNode2', 'unix ARM'); +lockNode('unix', options) { + whereAmI(); +} +// do not allocate executor, just lock node +lockNode('ARM') { + // allocate 2 executors on the same node + node(env.LOCKED_RESOURCE) { + node(env.LOCKED_RESOURCE) { + whereAmI(); + } + } +} +lockNode('unix && ARM', options) { + whereAmI(); +} + +// do not allocate executor, just lock node +lockNode('ARM') { + whereAmI(); +} + +// node AMD does not exits +try { + lockNode('AMD') { + whereAmI(); + } +} catch (Exception error) { + echo error.toString() +} + + +//----------------------------------------------------------------------------- +// Example 3 +// Lock 2 nodes by node-label matching +// and execute the scope in parallel. +createNode('SampleNode3', 'unix ARM'); + +options = [ + quantity : 2, // count of nodes to allocate + randomize : false, // do not randomize (per default true) + sort : false, // do not sort (per default true) + variable: 'LOCKED_NODES' +]; +lockNode('unix && ARM') { + Map stages = [:] + stages[env.LOCKED_NODES0] = { + node(env.LOCKED_NODES0) { + whereAmI(); + } + }; + stages[env.LOCKED_NODES1] = { + node(env.LOCKED_NODES1) { + whereAmI(); + } + }; + parallel(stages); +} + + +//----------------------------------------------------------------------------- +// helpers +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +void whereAmI() { + echo "locked resource: " + env.LOCKED_RESOURCE; + echo "locked node: " + env.NODE_NAME; +} + +//----------------------------------------------------------------------------- +/* +Approvals +JCaC + +security: + scriptApproval: + approvedSignatures: + - "method hudson.model.Computer getHostName" + - "method hudson.model.Computer isOnline" + - "method hudson.model.Node setLabelString java.lang.String" + - "method hudson.model.Slave setMode hudson.model.Node$Mode" + - "method hudson.model.Slave setNodeDescription java.lang.String" + - "method hudson.model.Slave setNumExecutors int" + - "method hudson.model.Slave setRetentionStrategy hudson.slaves.RetentionStrategy" + - "method jenkins.model.Jenkins addNode hudson.model.Node" + - "method jenkins.model.Jenkins getComputer java.lang.String" + - "method hudson.slaves.SlaveComputer getJnlpMac" + - "method jenkins.model.Jenkins getRootUrl" + - "new hudson.slaves.DumbSlave java.lang.String java.lang.String hudson.slaves.ComputerLauncher" + - "new hudson.slaves.JNLPLauncher boolean" + - "new hudson.slaves.RetentionStrategy$Always" + - "staticField hudson.model.Node$Mode NORMAL" + - "staticMethod jenkins.model.Jenkins getInstance" + - "staticMethod jenkins.model.Jenkins get" + - "staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods execute java.lang.String" + - "staticMethod org.codehaus.groovy.runtime.ProcessGroovyMethods getText java.lang.Process" +*/ +void createNode(final String nodeName, final String labels = 'LabelA labelB os:Windows country:Austria') { + + echo 'Create new node: ' + nodeName + + String tempWorkspace = 'C:\\jenkins\\tmp'; + + // Define a "Permanent Agent" + + final boolean enableWorkDir = true; // https://javadoc.jenkins.io/hudson/slaves/JNLPLauncher.html#%3Cinit%3E(boolean) + final launcher = new JNLPLauncher(enableWorkDir); + + Slave agent = new DumbSlave(nodeName, + tempWorkspace, + launcher + ); + + agent.nodeDescription = 'Auto generated agent'; + agent.numExecutors = 1; + agent.labelString = labels; + agent.mode = Node.Mode.NORMAL; + agent.retentionStrategy = new RetentionStrategy.Always(); + Jenkins.instance.addNode(agent); + + // mirror node to resource, be sure the resource exists + mirrorNodesToLockableResources(nodeName); + + // try to start the node. + // final String secret = jenkins.model.Jenkins.instance.getComputer(nodeName).getJnlpMac(); + // final String cmd = 'java -jar agent.jar -jnlpUrl ' + Jenkins.get().getRootUrl() + '/manage/computer/' + nodeName + '/jenkins-agent.jnlp -secret ' + secret + ' -workDir "' + tempWorkspace + '" & exit 0'; + // echo cmd.execute().text; +} diff --git a/shared-library/tests/src/main/jenkins/io/jenkins/pipeline/sample/mirrorNodesToLockableResources.groovy b/shared-library/tests/src/main/jenkins/io/jenkins/pipeline/sample/mirrorNodesToLockableResources.groovy new file mode 100644 index 000000000..834c00410 --- /dev/null +++ b/shared-library/tests/src/main/jenkins/io/jenkins/pipeline/sample/mirrorNodesToLockableResources.groovy @@ -0,0 +1,119 @@ +package io.jenkins.pipeline.sample + +@Library('lockable-resources-shared-library')_ + +// imports are for helpers +// The step mirrorNodesToLockableResources() self is global and does +// not need it +import hudson.slaves.JNLPLauncher +import hudson.slaves.DumbSlave +import hudson.slaves.RetentionStrategy + +//----------------------------------------------------------------------------- +// Example 1 +// mirror only one node. +// This will mirror one-by-one the node Node1 into lockable-resource with +// + name +// + descriptions +// + labels +createNode('SampleNode1') +mirrorNodesToLockableResources('SampleNode1') + +//----------------------------------------------------------------------------- +// Example 2 +// Mirror node with user defined properties +// This will mirror Node2 with customized properties: +// name: Node- +// labels: extends with label 'mirrored-node' +// description: will contains hostname in case the node is online. + +// The best way to change default behavior is to create Groovy expression +// and assign it to options.nodeToResourceProperties +// It will looks like this +Map options = [:]; +options.nodeToResourceProperties = {computer, properties -> + if (computer.isOnline()) { + properties.description += 'Hostname: ' + computer.getHostName() + } + properties.name = 'Node-' + properties.name + properties.labels += ' mirrored-node' +} +createNode('SampleNode2') +mirrorNodesToLockableResources('SampleNode2', options) + + +//----------------------------------------------------------------------------- +// Example 3 +// Mirror all nodes to lockable resources. +// When the resource does not exists it will be add automatically +// All resources mirrored from node contains label 'node' +createNode('SampleNode3') +createNode('SampleNode4') +createNode('SampleNode5') +mirrorNodesToLockableResources(); + + +//----------------------------------------------------------------------------- +// Example 4 +// Mirror all nodes with customized properties +// For *options* see also Example 2 +createNode('SampleNode6') +createNode('SampleNode7') +mirrorNodesToLockableResources(options) + +//----------------------------------------------------------------------------- +// In case the node does not exist is the mirroring skipped +mirrorNodesToLockableResources('This-node does not exist') + + + +//----------------------------------------------------------------------------- +// helpers +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/* +Approvals +JCaC + +security: + scriptApproval: + approvedSignatures: + - "method hudson.model.Computer getHostName" + - "method hudson.model.Computer isOnline" + - "method hudson.model.Node setLabelString java.lang.String" + - "method hudson.model.Slave setMode hudson.model.Node$Mode" + - "method hudson.model.Slave setNodeDescription java.lang.String" + - "method hudson.model.Slave setNumExecutors int" + - "method hudson.model.Slave setRetentionStrategy hudson.slaves.RetentionStrategy" + - "method jenkins.model.Jenkins addNode hudson.model.Node" + - "new hudson.slaves.DumbSlave java.lang.String java.lang.String hudson.slaves.ComputerLauncher" + - "new hudson.slaves.JNLPLauncher boolean" + - "new hudson.slaves.RetentionStrategy$Always" + - "staticField hudson.model.Node$Mode NORMAL" + - "staticMethod jenkins.model.Jenkins getInstance" +*/ +void createNode(final String nodeName, final String labels = 'LabelA labelB os:Windows country:Austria') { + + echo 'Create new node: ' + nodeName + + String tempWorkspace = 'C:\\jenkins\\tmp'; + + // Define a "Permanent Agent" + + final boolean enableWorkDir = true; // https://javadoc.jenkins.io/hudson/slaves/JNLPLauncher.html#%3Cinit%3E(boolean) + final launcher = new JNLPLauncher(enableWorkDir); + + Slave agent = new DumbSlave(nodeName, + tempWorkspace, + launcher + ); + + agent.nodeDescription = 'Auto generated agent'; + agent.numExecutors = 1; + agent.labelString = labels; + agent.mode = Node.Mode.NORMAL; + agent.retentionStrategy = new RetentionStrategy.Always(); + Jenkins.instance.addNode(agent); +} + diff --git a/shared-library/tests/src/test/groovy/io/jenkins/pipeline/sample/TestSharedLibrary.groovy b/shared-library/tests/src/test/groovy/io/jenkins/pipeline/sample/TestSharedLibrary.groovy new file mode 100644 index 000000000..718d0b713 --- /dev/null +++ b/shared-library/tests/src/test/groovy/io/jenkins/pipeline/sample/TestSharedLibrary.groovy @@ -0,0 +1,47 @@ +package io.jenkins.pipeline.sample + +import static com.lesfurets.jenkins.unit.global.lib.LibraryConfiguration.library +import static com.lesfurets.jenkins.unit.global.lib.LocalSource.localSource + +// import org.jenkins.plugins.lockableresources.SharedLibrary; +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder + +import com.lesfurets.jenkins.unit.BasePipelineTest + +class TestSharedLibrary extends BasePipelineTest { + + @Rule + public TemporaryFolder folder = new TemporaryFolder() + + String sharedLibs = this.class.getResource('/').getFile() + + String myBranch = 'shared-lib' + + @Override + @Before + void setUp() throws Exception { + scriptRoots += 'src/main/jenkins' + super.setUp() + binding.setVariable('scm', [branch: myBranch]) + } + + @Test + void library_annotation() throws Exception { + assert(true); +/* boolean exception = false + def library = library().name('lockable-resources-shared-library') + // .defaultVersion(SharedLibrary.myCurrentVersion()) + .defaultVersion(myBranch) + .allowOverride(false) + .implicit(false) + .targetPath(sharedLibs) + .retriever(localSource(sharedLibs)) + .build() + helper.registerSharedLibrary(library) + runScript('io/jenkins/pipeline/sample/pipelineUsingSharedLib.groovy') + printCallStack()*/ + } +} diff --git a/shared-library/vars/lockNode.groovy b/shared-library/vars/lockNode.groovy new file mode 100644 index 000000000..09ea490c3 --- /dev/null +++ b/shared-library/vars/lockNode.groovy @@ -0,0 +1,89 @@ +#!groovy + +import jenkins.model.Jenkins; +import io.jenkins.library.lockableresources.ResourceLabel; +import io.jenkins.library.lockableresources.Utils; + +//----------------------------------------------------------------------------- +void call(String nodeName, Closure closure) { + lockNode(nodeName, [:], closure); +} + +//----------------------------------------------------------------------------- +void call(final String nodeName, Map opts, Closure closure) { + opts = Utils.fixNullMap(opts); + + Map mirrorOptions = opts.mirrorOptions; + Utils.fixNullMap(mirrorOptions); + + if (Jenkins.get().getNode(nodeName) != null) { + mirrorNodesToLockableResources(nodeName, mirrorOptions); + + echo("Trying to acquire lock on node [$nodeName]"); + lockResource(nodeName, opts) { + inLockScope(nodeName, opts, closure); + } + } else { + // your node does not exists, we try to find it as label + List matched = findNodesByLabel(nodeName, opts); + List matchedNames = []; + for(Resource resource : matched) { + matchedNames.push(resource.getName()); + } + + if (matched.size() == 0) { + throw(new Exception('No matches for: ' + nodeName)); + } else if (matched.size() == 1) { + // exact one node, so call me back recursive, but with exact node name + lockNode(matchedNames[0], opts, closure); + } else { + // mirror all requested nodes + for(String name : matchedNames) { + mirrorNodesToLockableResources(name, mirrorOptions); + } + } + + echo("Trying to acquire lock on nodes [$matchedNames]"); + lockResource(matchedNames, opts, closure); + echo("Lock released on nodes [$matchedNames]"); + } +} + +//----------------------------------------------------------------------------- +/** */ +List findNodesByLabel(String labelExpression, Map opts) { + final Label parsed = Label.parseExpression(labelExpression); + if (opts.quantity == null) { + opts.quantity = 1; // per default lock only 1 node + } + + if (opts.quantity > 1) { + if (opts.randomize == null) { + opts.randomize = true; // make sense to randomize the node usage per default + } + if (opts.sort == null) { + opts.sort = true; + } + } + + return lockableResource.find(opts) {it -> return it.hasLabel(ResourceLabel.NODE_LABEL) && it.matches(parsed)}; +} + +//----------------------------------------------------------------------------- +void inLockScope(String nodeName, Map opts, Closure closure) { + + if (opts.allocateExecutor) { + opts.remove('allocateExecutor'); + echo("Trying to acquire executor on node [$nodeName]"); + node(nodeName) { + echo("Executor acquired on node [$nodeName]"); + inLockScope(nodeName, opts, closure); + } + echo("Executor released on resource [$nodeName]"); + return; + } + + echo("Lock acquired on node [$nodeName]"); + closure(); + echo("Lock released on node [$nodeName]"); +} diff --git a/shared-library/vars/lockResource.groovy b/shared-library/vars/lockResource.groovy new file mode 100644 index 000000000..481833c79 --- /dev/null +++ b/shared-library/vars/lockResource.groovy @@ -0,0 +1,167 @@ +#!groovy + + +import edu.umd.cs.findbugs.annotations.NonNull; +import io.jenkins.library.lockableresources.Resource; +import io.jenkins.library.lockableresources.ResourcesManager; +import io.jenkins.library.lockableresources.Utils; + +//----------------------------------------------------------------------------- +void call(@NonNull String resourceName, @NonNull Closure closure) { + lockResource(resourceName, [:], closure); +} + +//----------------------------------------------------------------------------- +// createOnDemand: create resource when does not exists +// /** */ +void call(@NonNull String resourceName, @NonNull Map opts, @NonNull Closure closure) { + Resource resource = new Resource(resourceName); + opts = Utils.fixNullMap(opts); + + if (opts.createOnDemand != null && !ResourcesManager.resourceExists(resourceName)) { + final Map props = (opts.createOnDemand instanceof Map) ? opts.createOnDemand : [:]; + resource.create(props); + opts.remove('createOnDemand'); + } + _lock(resource, opts, closure); +} + +//----------------------------------------------------------------------------- +// createOnDemand: create resource when does not exists +// /** */ +void call(@NonNull List resourceNames, @NonNull Map opts, @NonNull Closure closure) { + lockResources(lockableResource.find(resourceNames), opts, closure); +} + +//----------------------------------------------------------------------------- +void lockResources(@NonNull List resources, @NonNull Map opts, @NonNull Closure closure) { + + opts = Utils.fixNullMap(opts); + if (opts.createOnDemand) { + for(Resource resource : resources) { + + if (!resource.exists()) { + final Map props = (opts.createOnDemand instanceof Map) ? opts.createOnDemand : [:]; + resource.create(props); + } + } + } + opts.remove('createOnDemand'); + _multipleLock(resources, opts, closure); +} + +//------------------------------------------------------------------------------ +void _fixDefaultOpts(Map opts) { + if (opts.variable == null) { + opts.variable = 'LOCKED_RESOURCE'; + } + if (opts.inversePrecedence == null) { + opts.inversePrecedence = false; + } + if (opts.skipIfLocked == null) { + opts.skipIfLocked = false; + } +} + +//------------------------------------------------------------------------------ +// /** */ +void _lock(@NonNull String resourceName, @NonNull Map opts, @NonNull Closure closure) { + _lock(new Resource(resourceName), opts, closure); +} + + +//------------------------------------------------------------------------------ +// /** */ +void _lock(@NonNull Resource resource, @NonNull Map opts, @NonNull Closure closure) { + + _fixDefaultOpts(opts); + + try { + if (opts.beforeLock != null) { + opts.beforeLock(resource); + } + + lock( + resource: resource.getName(), + variable: opts.variable, + inversePrecedence: opts.inversePrecedence, + skipIfLocked: opts.skipIfLocked + ) { + _insideLock(resource, opts, closure); + } + if (opts.afterRelease != null) { + opts.afterRelease(resource); + } + } catch (error) { + boolean accepted = false; + if (opts.onFailure != null) { + accepted = opts.onFailure(resource, error) + } + if (!accepted) { + // not handled exception + throw error; + } + } +} + +//------------------------------------------------------------------------------ +// /** */ +void _multipleLock(@NonNull List resources, @NonNull Map opts, @NonNull Closure closure) { + + _fixDefaultOpts(opts); + + def extra = []; + for(Resource resource : resources) { + extra.push(resource: resource.getName()); + } + + try { + if (opts.beforeLock != null) { + opts.beforeLock(resources); + } + + lock( + variable: opts.variable, + inversePrecedence: opts.inversePrecedence, + skipIfLocked: opts.skipIfLocked, + extra : extra + ) { + _insideLock(resources, opts, closure); + } + if (opts.afterRelease != null) { + opts.afterRelease(resources); + } + } catch (error) { + boolean accepted = false; + if (opts.onFailure != null) { + accepted = opts.onFailure(resources, error) + } + if (!accepted) { + // not handled exception + throw error; + } + } +} + +//------------------------------------------------------------------------------ +// resource might be : Resource|List +void _insideLock(@NonNull def resource, @NonNull Map opts, @NonNull Closure closure) { + + if (opts.timeout != null) { + timeout(opts.timeout) { + opts.remove('timeout'); + _insideLock(resource, opts, closure); + } + return; + } + + if (opts.onLock != null) { + opts.onLock(resource); + } + + closure(); + + if (opts.beforeRelease != null) { + opts.beforeRelease(resource); + } +} \ No newline at end of file diff --git a/shared-library/vars/lockableResource.groovy b/shared-library/vars/lockableResource.groovy new file mode 100644 index 000000000..ded76908c --- /dev/null +++ b/shared-library/vars/lockableResource.groovy @@ -0,0 +1,87 @@ +#!groovy + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import io.jenkins.library.lockableresources.Resource; +import io.jenkins.library.lockableresources.ResourceLabel; +import io.jenkins.library.lockableresources.ResourcesManager as RM; + +//----------------------------------------------------------------------------- +/** +*/ +/** */ +@CheckForNull +Resource call(String resourceName) { + return new Resource(RM.getResourceOrDie(resourceName)); +} + +//----------------------------------------------------------------------------- +/** +*/ +/** */ +@CheckForNull +Resource find(String resourceName) { + return new Resource(RM.getResource(resourceName)); +} + +//----------------------------------------------------------------------------- +/** +*/ +/** */ +List find(List resourceNames) { + return RM.getResources(resourceNames); +} + +//----------------------------------------------------------------------------- +/** +*/ +/** */ +List find(ResourceLabel resourceLabel, int quantity = 0) { + return RM.getResources(resourceLabel, ['quantity' : quantity]); +} + +//----------------------------------------------------------------------------- +/** +*/ +/** */ +List find(ResourceLabel resourceLabel, Map opts) { + return RM.getResources(resourceLabel, opts); +} + +//----------------------------------------------------------------------------- +/** +*/ +/** */ +List find(int quantity, Closure closure) { + return RM.getResources(closure, ['quantity' : quantity]); +} + +//----------------------------------------------------------------------------- +/** +*/ +/** */ +List find(Map opts, Closure closure) { + return RM.getResources(closure, opts); +} + +//----------------------------------------------------------------------------- +/** +*/ +/** */ +List find(Closure closure) { + return RM.getResources(closure, ['quantity' : 0]); +} + +//----------------------------------------------------------------------------- +/** */ +List getAll() { + return RM.getResources(); +} + +//------------------------------------------------------------------------------ +/** */ +boolean isFree(@NonNull String resourceName) { + Resource resource = new Resource(resourceName); + return resource.isFree(); +} + diff --git a/shared-library/vars/mirrorNodesToLockableResources.groovy b/shared-library/vars/mirrorNodesToLockableResources.groovy new file mode 100644 index 000000000..6d56779d7 --- /dev/null +++ b/shared-library/vars/mirrorNodesToLockableResources.groovy @@ -0,0 +1,153 @@ +#!groovy + + +import static java.text.DateFormat.MEDIUM; +import static java.text.DateFormat.SHORT; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import jenkins.model.Jenkins; +import io.jenkins.library.lockableresources.Resource; +import io.jenkins.library.lockableresources.ResourceLabel; +import io.jenkins.library.lockableresources.Utils; + +//----------------------------------------------------------------------------- +void call(@NonNull String nodeName) { + mirrorNodeToLockableResource(nodeName, [:]); +} + +//----------------------------------------------------------------------------- +void call(@NonNull String nodeName, @NonNull Map opts) { + mirrorNodeToLockableResource(nodeName, opts); +} + +//----------------------------------------------------------------------------- +void call() { + mirrorNodesToLockableResources([:]); +} + +//----------------------------------------------------------------------------- +/** + Currently are nodes only added or updated, but not removed. There is very good reason. + We would destroy your instance. + Maybe when we add enabled option in the 'LockableResource', we can disable it. + Also is the question what shall happens, when is currently locked and node does not + exists? +*/ +void call(@NonNull Map opts) { + + opts = Utils.fixNullMap(opts); + + // synchronized over all jobs + lockResource('mirrorNodes') { + // mirror existing nodes + List mirrored = []; + jenkins.model.Jenkins.instance.computers.each { c -> + String resourceName = mirrorNodeToLockableResource(c, opts); + if (resourceName != null) { + mirrored.push(resourceName); + } + } + + // step all resources and check if the node has been removed + final ResourceLabel nodeLabel = new ResourceLabel(ResourceLabel.NODE_LABEL); + for(Resource resource : lockableResource.find(nodeLabel)) { + + if (mirrored.contains(resource.getName())) { + return; + } + echo('The resource ' + resource.getName() + ' is not a ' + nodeLabel.getName()); + if (resource.isFree()) { + resource.removeLabel(nodeLabel); + } else { + echo('The resource ' + resource.getName() + ' is not free, therefore the label ' + nodeLabel.getName() + ' can not be removed'); + } + resource.save(); + } + } +} + +//----------------------------------------------------------------------------- +/** */ +@CheckForNull +Map nodeToResourceProperties(Computer computer) { + if (computer == null || computer.node == null) { + return null; // this node does not exists + } + + final String nodeName = computer.node.selfLabel; + + final DateFormat format = SimpleDateFormat.getDateTimeInstance(MEDIUM, SHORT); + final String url = Jenkins.get().getRootUrl() + computer.getUrl(); + String note = ''; + def formatter = Jenkins.get().getMarkupFormatter(); + if (formatter != null && formatter.class.name.toLowerCase().contains('markdown')) { + // markdown formatter (like https://github.com/jenkinsci/markdown-formatter-plugin) + note += 'Mirrored from [' + nodeName + '](' + url + ')' + '\n'; + note += '\n'; + note += 'Last update at ' + format.format(new Date()); + } else if (formatter != null && (formatter.class.name.toLowerCase().contains('html') || formatter.class.name.contains('UnsafeMarkupFormatter'))) { + // html formatter + note += '
'
+    note += '

Mirrored from ' + note += '

'; + note += '
' + } else { + // no formatter chosen (or not supported) + note += 'Mirrored from ' + url + '\n'; + note += 'Last update at ' + format.format(new Date()); + } + + return [ + 'description' : computer.getDescription(), + 'labels' : ResourceLabel.NODE_LABEL + ' ' + computer.node.labelString, + 'note' : note, + 'name' : nodeName + ]; +} + +//----------------------------------------------------------------------------- +/** */ +String mirrorNodeToLockableResource(@NonNull String nodeName, @NonNull Map opts) { + return _mirrorNodeToLockableResource(jenkins.model.Jenkins.instance.getComputer(nodeName), opts); +} + +//----------------------------------------------------------------------------- +/** */ +String mirrorNodeToLockableResource(@NonNull Computer computer, @NonNull Map opts) { + return _mirrorNodeToLockableResource(computer, opts); +} + +//----------------------------------------------------------------------------- +String _mirrorNodeToLockableResource(@NonNull Computer computer, @NonNull Map opts) { + if (computer == null) { + return null; // this node does not exists + } + + opts = Utils.fixNullMap(opts); + + Map properties = nodeToResourceProperties(computer); + if (opts.nodeToResourceProperties != null) { + opts.nodeToResourceProperties(computer, properties); + } + + Resource resource = new Resource(properties.name ? properties.name : computer.name); + if (!resource.exists()) { + resource.create(properties); + } else { + resource.fromMap(properties); + resource.save(); + } + + return resource.getName(); +}