Skip to content

Commit

Permalink
Visualize the queue (#452)
Browse files Browse the repository at this point in the history
Visualize the queue
  • Loading branch information
mPokornyETM authored Feb 1, 2023
1 parent 6b7b959 commit 14c3d52
Show file tree
Hide file tree
Showing 16 changed files with 570 additions and 10 deletions.
54 changes: 47 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ This plugin allows defining lockable resources (such as printers, phones,
computers, etc.) that can be used by builds. If a build requires a resource
which is already locked, it will wait for the resource to be free.

----

## Usage

### Adding lockable resources
Expand Down Expand Up @@ -89,7 +91,6 @@ pipeline {

#### Take first position in queue


```groovy
lock(resource: 'staging-server', inversePrecedence: true) {
node {
Expand All @@ -109,7 +110,6 @@ lock(label: 'some_resource', variable: 'LOCKED_RESOURCE') {

When multiple locks are acquired, each will be assigned to a numbered variable:


```groovy
lock(label: 'some_resource', variable: 'LOCKED_RESOURCE', quantity: 2) {
// comma separated names of all acquired locks
Expand Down Expand Up @@ -161,6 +161,7 @@ echo 'Finish'

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

----
## Configuration as Code

This plugin can be configured via
Expand All @@ -182,9 +183,15 @@ unclassified:
Properties *description*, *labels* and *reservedBy* are optional.
----
## lockable-resources overview
The page `<jenkinsRootUrl>/lockable-resources/` provides an overview over all resources and actions to change resource status.
The page `<jenkinsRootUrl>/lockable-resources/` provides an overview over all lockable-resources.

### Resources

Provides an status overview over all resources and actions to change resource status.

Name | Permission | Description
-----|------------|------------
Expand All @@ -196,47 +203,80 @@ Reassign | STEAL | Reserves a resource that may be or not be reserved by some pe
Reset | UNLOCK | Reset a resource that may be reserved, locked or queued.
Note | RESERVE | Add or edit resource note.

### Labels

Provides an overview over all lockable-resources labels.

> *Note:* Please keep in mind, that lockable-resource-label is not the same as node-label!

### Queue

Provides an overview over currently queued requests.
A request is queued by the pipeline step `lock()`. When the requested resource(s) is currently in use (not free), then any new request for this resource will be added into the queue.

A resource may be requested by:

- name, such as in `lock('ABC') { ... }`
- label, such as in `lock(label : 'COFFEE_MACHINE')`

> *Note:* Please keep in mind that groovy expressions are currently supported only in free-style jobs. Free-style jobs do not update this queue and therefore can not be shown in this view.

> *Note:* An empty value in the column 'Requested at' means that this build has been started in an older plugin version - [1117.v157231b_03882](https://github.com/jenkinsci/lockable-resources-plugin/releases/tag/1117.v157231b_03882) and early. In this case we cannot recognize the timestamp.

----

## Upgrading from 1102.vde5663d777cf

Due an [issue](https://github.com/jenkinsci/lockable-resources-plugin/issues/434) **is not possible anymore to read resource-labels** from the config file org.jenkins.plugins.lockableresources.LockableResourcesManager.xml, **which is generated in the release** [1102.vde5663d777cf](https://github.com/jenkinsci/lockable-resources-plugin/releases/tag/1102.vde5663d777cf)

This issue does not **effect** instances configured by [Configuration-as-Code](https://github.com/jenkinsci/configuration-as-code-plugin) plugin.

A possible solution is, to remove the `<string>` tags from your `org.jenkins.plugins.lockableresources.LockableResourcesManager.xml`config file manually, before you upgrade to new version (Keep in mind, that a backup is still good idea).

A possible solution is to remove the `<string>` tags from your `org.jenkins.plugins.lockableresources.LockableResourcesManager.xml` config file manually, before you upgrade to new version (Keep in mind that a backup is still good idea).

Example:

change this one
```

```xml
<labels>
<string>tests-integration-installation</string>
</labels>
```

to
```

```xml
<labels>
tests-integration-installation
</labels>
```

----

## Changelog

- See [GitHub Releases](https://github.com/jenkinsci/lockable-resources-plugin/releases)
for recent versions.
- See the [old changelog](CHANGELOG.old.md) for versions 2.5 and older.

----

## Report an Issue

Please report issues and enhancements through the [Jenkins issue tracker in GitHub](https://github.com/jenkinsci/lockable-resources-plugin/issues/new/choose)

----

## 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)
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,12 @@ private QueuedContextStruct getNextQueuedContext(
return newestEntry;
}

/** Returns current queue */
@Restricted(NoExternalUse.class) // used by jelly
public List<QueuedContextStruct> getCurrentQueuedContext() {
return Collections.unmodifiableList(this.queuedContexts);
}

/** Creates the resource if it does not exist. */
public synchronized boolean createResource(String name) {
name = Util.fixEmptyAndTrim(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.util.Set;
import javax.servlet.ServletException;
import jenkins.model.Jenkins;
import org.jenkins.plugins.lockableresources.queue.LockableResourcesStruct;
import org.jenkins.plugins.lockableresources.queue.QueuedContextStruct;
import org.jenkins.plugins.lockableresources.LockableResource;
import org.jenkins.plugins.lockableresources.LockableResourcesManager;
import org.jenkins.plugins.lockableresources.Messages;
Expand Down Expand Up @@ -158,6 +160,32 @@ public int getAssignedResourceAmount(String label) {
return LockableResourcesManager.get().getResourcesWithLabel(label, null).size();
}

/** Returns current queue */
@Restricted(NoExternalUse.class) // used by jelly
public List<QueuedContextStruct> getCurrentQueuedContext() {
return LockableResourcesManager.get().getCurrentQueuedContext();
}

/** Returns current queue */
@Restricted(NoExternalUse.class) // used by jelly
@CheckForNull
public LockableResourcesStruct getOldestQueue() {
LockableResourcesStruct oldest = null;
for (QueuedContextStruct context : this.getCurrentQueuedContext()) {
for(LockableResourcesStruct resourceStruct : context.getResources()) {
if (resourceStruct.queuedAt == 0) {
// Older versions of this plugin might miss this information.
// Therefore skip it here.
continue;
}
if (oldest == null || oldest.queuedAt > resourceStruct.queuedAt) {
oldest = resourceStruct;
}
}
}
return oldest;
}

@RequirePOST
public void doUnlock(StaplerRequest req, StaplerResponse rsp)
throws IOException, ServletException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
import hudson.EnvVars;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.jenkins.plugins.lockableresources.LockableResource;
import org.jenkins.plugins.lockableresources.LockableResourcesManager;
import org.jenkins.plugins.lockableresources.RequiredResourcesProperty;
import org.jenkins.plugins.lockableresources.util.SerializableSecureGroovyScript;
import org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

public class LockableResourcesStruct implements Serializable {

Expand All @@ -30,6 +33,7 @@ public class LockableResourcesStruct implements Serializable {
public String label;
public String requiredVar;
public String requiredNumber;
public long queuedAt = 0;

@CheckForNull private final SerializableSecureGroovyScript serializableResourceMatchScript;

Expand All @@ -39,6 +43,7 @@ public class LockableResourcesStruct implements Serializable {
private static final long serialVersionUID = 1L;

public LockableResourcesStruct(RequiredResourcesProperty property, EnvVars env) {
queuedAt = new Date().getTime();
required = new ArrayList<>();

LockableResourcesManager resourcesManager = LockableResourcesManager.get();
Expand Down Expand Up @@ -81,6 +86,7 @@ public LockableResourcesStruct(

public LockableResourcesStruct(
@Nullable List<String> resources, @Nullable String label, int quantity) {
queuedAt = new Date().getTime();
required = new ArrayList<>();
if (resources != null) {
for (String resource : resources) {
Expand Down Expand Up @@ -138,4 +144,18 @@ public String toString() {
+ ", Number of resources: "
+ this.requiredNumber;
}

/** Returns timestamp when the resource has been added into queue.*/
@Restricted(NoExternalUse.class) // used by jelly
public Date getQueuedTimestamp() {
return new Date(this.queuedAt);
}

/** Check if the queue takes too long.
At the moment "too long" means over 1 hour.
*/
@Restricted(NoExternalUse.class) // used by jelly
public boolean takeTooLong() {
return (new Date().getTime() - this.queuedAt) > 3600000L;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package org.jenkins.plugins.lockableresources.queue;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import hudson.model.Run;
import java.io.IOException;
import java.io.Serializable;
import java.util.List;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

/*
* This class is used to queue pipeline contexts
Expand Down Expand Up @@ -56,6 +61,18 @@ public StepContext getContext() {
return this.context;
}

/** Return build, where is the resource used.*/
@CheckForNull
@Restricted(NoExternalUse.class) // used by jelly
public Run<?, ?> getBuild() {
try {
return this.getContext().get(Run.class);
} catch (IOException | InterruptedException e) {
// for some reason there is no Run object for this context
return null;
}
}

/*
* Gets the required resources.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@
xmlns:f="/lib/form"
xmlns:i="jelly:fmt"
xmlns:st="jelly:stapler"
xmlns:t="/lib/hudson"
>
<l:layout title="${it.displayName}" type="one-column">
<l:app-bar title="${%header.resources}">
<t:help href="https://github.com/jenkinsci/lockable-resources-plugin#lockable-resources-overview" />
</l:app-bar>

<l:main-panel>
<h1>${%header.resources}</h1>
<j:choose>
<j:when test="${it.resources.size() == 0}">
<p>
Expand All @@ -39,7 +43,11 @@
<button class="nav-link" id="labels-tab" data-bs-toggle="tab" data-bs-target="#labels" type="button"
role="tab" aria-controls="labels" aria-selected="false">${%tab.labels}</button>
</li>
<!-- more tabs like logs, queue can be added here -->
<li class="nav-item" role="presentation">
<button class="nav-link" id="queue-tab" data-bs-toggle="tab" data-bs-target="#queue" type="button"
role="tab" aria-controls="queue" aria-selected="false">${%tab.queue}</button>
</li>
<!-- more tabs like logs can be added here -->
</ul>
<!-- Tab panes -->
<div class="tab-content">
Expand All @@ -52,6 +60,11 @@
<st:include page="tableLabels/table"/>
</div>
</div>
<div class="tab-content">
<div class="tab-pane jenkins-!-margin-top-1" id="queue" role="tabpanel" aria-labelledby="queue-tab">
<st:include page="tableQueue/table"/>
</div>
</div>
</div>

<script type="text/javascript" src="${resURL}/plugin/data-tables-api/js/table.js"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ header.resources=Lockable Resources
# tabs
tab.resources=Resources
tab.labels=Labels
tab.queue=Queue
#warning resources not configured
resources.not_configured=There are no resources configured at the moment.
resources.configure.here=You can configure it <a href="{0}">here</a>.
Loading

0 comments on commit 14c3d52

Please sign in to comment.