Skip to content

Commit

Permalink
Add more examples (#403)
Browse files Browse the repository at this point in the history
Add more examples
  • Loading branch information
mPokornyETM committed Nov 27, 2022
1 parent 71ed6c4 commit 7a87199
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 21 deletions.
32 changes: 15 additions & 17 deletions PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,6 @@ For frontend changes, include screenshots of the relevant page(s) before and aft
For refactoring and code cleanup changes, exercise the code before and after the change and verify the behavior remains the same.
-->

### Proposed changelog entries

- Entry 1: Issue, human-readable text
- []

<!-- Comment:
The changelog entry should be in the imperative mood; e.g., write "do this"/"return that" rather than "does this"/"returns that".
For examples, see: https://www.jenkins.io/changelog/
-->

### Proposed upgrade guidelines

N/A
Expand All @@ -46,13 +36,16 @@ N/A

<!-- Comment:
To translate this plugin we used an awesome tool named [Crowdin](https://crowdin.jenkins.io/lockable-resources-plugin). At the moment there is a limited number of users allowed to validate the proposed translations, but anybody having a Crowdin account (created in a heartbeat) can participate in the translation effort.
Be sure any localization files are moved to *.properties files.
Please describe here which language has been translated by you.
English text's are mandatory for new entries.
+ Be sure any localization files are moved to *.properties files.
+ Please describe here which language has been translated by you.
+ English text's are mandatory for new entries.
Please note, that changes in non-default localizations files without Crowdin translations leads to misleading and merge conflicts.
-->

- [ ] English
- [ ] Franc
- [ ] German
- [ ] French
- [ ] Slovak
- [ ] Czech
- [ ] ...
Expand All @@ -61,23 +54,28 @@ English text's are mandatory for new entries.

- [ ] The Jira / Github issue, if it exists, is well-described.
- [ ] The changelog entries and upgrade guidelines are appropriate for the audience affected by the change (users or developers, depending on the change) and are in the imperative mood (see [examples](https://github.com/jenkins-infra/jenkins.io/blob/master/content/_data/changelogs/weekly.yml)).
- The changelog generator for plugins uses the **pull request title as the changelog entry**.
- Fill in the **Proposed upgrade guidelines** section only if there are breaking changes or changes that may require extra steps from users during the upgrade.
- [ ] There is automated testing or an explanation that explains why this change has no tests.
- [ ] New public classes, fields, and methods are annotated with `@Restricted` or have `@since TODO` Javadocs, as appropriate.
- [ ] New public functions for internal use only are annotated with `@NoExternalUse`. In case it is used by non java code the `Used by {@code <panel>.jelly}` Javadocs are annotated.
<!-- Comment:
This steps need additional automation in release management. Therefore are commented out for now.
- [ ] New public classes, fields, and methods are annotated with `@Restricted` or have `@since TODO` Javadocs, as appropriate.
- [ ] New deprecations are annotated with `@Deprecated(since = "TODO")` or `@Deprecated(forRemoval = true, since = "TODO")`, if applicable.
-->
- [ ] New or substantially changed JavaScript is not defined inline and does not call `eval` to ease the future introduction of Content Security Policy (CSP) directives (see [documentation](https://www.jenkins.io/doc/developer/security/csp/)).
- [ ] 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).

### 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.
- [ ] 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)).
- [ ] java code changes are tested by automated test.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ Each lockable resource has the following properties:
the resource will be unavailable for jobs. i.e. `All printers are currently not available due to maintenance.`
This option is still possible, but we recommend to use the page `<jenkinsRootUrl>/lockable-resources/`

A resource is always the one thing that is locked (or free or reserved).
It exists once and has an unique name (if we take the hardware example, this may be `office_printer_14`).
Every resource can have multiple labels (the printer could be labeled `dot-matrix-printer`, `in-office-printer`, `a4-printer`, etc.).
All resources with the same label form a "pool", so if you try to lock an `a4-printer`, one of the resources with the label `a4-printer` will be locked when it is available.
If all resources with the label `a4-printer` are in use, your job waits until one is available.
This is similar to nodes and node labels.

### Using a resource in a freestyle job

When configuring the job, select **This build requires lockable resources**.
Expand Down Expand Up @@ -129,6 +136,8 @@ lock(
echo 'Finish'
```

More examples are [here](src/doc/examples/readme.md).

## Configuration as Code

This plugin can be configured via
Expand All @@ -140,11 +149,10 @@ This plugin can be configured via
unclassified:
lockableResourcesManager:
declaredResources:
- name: "S7_1200_1 "
- name: "S7_1200_1"
description: "S7 PLC model 1200"
labels: "plc:S7 model:1200"
reservedBy: "Reserved due maintenance window"
declaredResources:
- name: "S7_1200_2"
labels: "plc:S7 model:1200"
```
Expand Down
185 changes: 185 additions & 0 deletions src/doc/examples/lock-nodes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Examples

## Node dependent resources

Locking a resource that depends on a specific node can be very helpful in many cases.
That means a job must pick a target node that has the requested resource available.

```groovy
// allocate node
node('some-build-node') {
// Lock resource named *whatever-resource-some-build-node*
lock("whatever-resource-${env.NODE_NAME}") {
echo "Running on node ${env.NODE_NAME} with locked resource ${env.LOCKED_RESOURCE}"
}
}
```

But much more useful is lock node first.

```groovy
// Lock resource named *some-build-node*
lock('some-build-node') {
// allocate node
node(env.LOCKED_RESOURCE) {
echo "I am no node ${env.NODE_NAME} and locked resource ${env.LOCKED_RESOURCE}"
}
}
```

Let explain in more complex use case.

*Request:*
Our job tests server-client integration. That means we need 2 nodes (1 server and 1 client).
On every node must be test sources up-to-date.
Tests are running only on client side.

*Solution:*
Create 2 nodes:

- node-server
- node-client

and execute it parallel like this:

```groovy
Map stages = [:];
stages['server'] = {
node('node-server') {
prepareTests()
startServer()
}
}
stages['client'] = {
node('node-client') {
prepareTests()
startClientTest()
}
}
// execute all prepare stages synchronous
parallel stages
// Prepare tests on node
void prepareTests() {
checkout([$class: 'GitSCM',
branches: [[name: 'master']]
])
}
// Start server
void startServer() {
echo 'Server will be started in few seconds'
sh 'mvn clean hpi:run'
echo 'Server is done'
}
// Start client
void startClientTest() {
sleep 20
sh 'mvn clean verify'
}
```

It looks pretty fine and easy, but !!!.

Executing all steps parallel might leads to timing issues, because checkout on server-node might takes much longer then on client-node. This is serious issue because the client starts before the server and can not connect to server.

The solution is to synchronized parallel stages like this.

```groovy
Map prepareStages = [:];
prepareStages['server'] = {
node('node-server') {
prepareTests()
}
}
prepareStages['client'] = {
node('node-client') {
startServer()
}
}
// execute all prepare stages synchronous
parallel prepareStages
Map testStages = [:]
testStages['server'] = {
node('node-server') {
prepareTests();
}
}
testStages['client'] = {
node('node-client') {
sleep 20
startClientTest();
}
}
// execute all test stages at the same time
testStages.failFast = true
parallel testStages
...
```

Ok we solve the timing issue, but what is wrong here?

When the step *parallel prepareStages* is done then are on both nodes executors free. At this moment
it might happen, that some other job allocate one of the nodes. This will leads to more side effects, like:

- no body can grant, that currently checked out workspace will be untouched
- no body can grant how long will be the node allocated
- ... and many others

Instead, we lock both nodes with a single call to `lock`.

Create two resources:
name | Labels |
---------------|--------|
nodes-server-1 | server-node |
nodes-client-1 | client-node |


```groovy
lock(variable: 'locked_resources',
extra: [
[label: 'server-node', quantity: 1],
[label: 'client-node', quantity: 1]
) {
final String serverNodeName = env.LOCKED_RESOURCE0;
final String clientNodeName = env.LOCKED_RESOURCE1;
Map prepareStages = [:];
prepareStages['server'] = {
node(serverNodeName) {
prepareTests()
}
}
prepareStages['client'] = {
node(clientNodeName) {
startServer()
}
}
// execute all prepare stages synchronous
parallel prepareStages
Map testStages = [:]
testStages['server'] = {
node(serverNodeName) {
prepareTests();
}
}
testStages['client'] = {
node(clientNodeName) {
sleep 20
startClientTest();
}
}
// execute all test stages at the same time
testStages.failFast = true
parallel testStages
}
...
```

Keep in mind, that `lock()` only helps when locks are consistently requested for resources.
33 changes: 33 additions & 0 deletions src/doc/examples/locking-multiple-stages-in-declarative-pipeline
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Locking multiple stages in declarative pipeline

You can lock the entire job in the options block of the pipeline:

```groovy
pipeline {
options {
lock resource: 'lockable resource'
}


agent any

stages {
stage('Build') {
steps {
sh 'make'
}
}
stage('Test'){
steps {
sh 'make check'
junit 'reports/**/*.xml'
}
}
stage('Deploy') {
steps {
sh 'make publish'
}
}
}
}
```
9 changes: 9 additions & 0 deletions src/doc/examples/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Examples

Examples of lockable resources include:

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.

- [Node depended resources](lock-nodes.md)
- [Locking multiple stages in declarative pipeline](locking-multiple-stages-in-declarative-pipeline.md)
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,7 @@ The user can unreserve only own resource. Administrator can unreserve any resour
btn.reassign=Reassign
btn.reassign.detail=Reserves a resource that may be or not be reserved by some person already.<br>\
Giving it away to currently logged user indefinitely \
(until that person, or some explicit scripted action, decides to release the resource).<br>\
This action can be done only by administrator.
(until that person, or some explicit scripted action, decides to release the resource).
btn.editNote=Note
btn.editNote.detail=Edit resource note.
#table labels
Expand Down

0 comments on commit 7a87199

Please sign in to comment.