Skip to content

Commit

Permalink
Show used resources in build page (#583)
Browse files Browse the repository at this point in the history
  • Loading branch information
mPokornyETM authored Nov 24, 2023
1 parent ce79091 commit 1ad65ec
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.jenkins.plugins.lockableresources.actions.LockedResourcesBuildAction;
import org.jenkins.plugins.lockableresources.queue.LockableResourcesStruct;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.steps.AbstractStepExecutionImpl;
Expand Down Expand Up @@ -110,19 +111,21 @@ public static void proceed(
String resourceDescription,
final String variable,
boolean inversePrecedence) {
Run<?, ?> r;
Run<?, ?> build;
FlowNode node;
try {
r = context.get(Run.class);
build = context.get(Run.class);
node = context.get(FlowNode.class);
context.get(TaskListener.class).getLogger().println("Lock acquired on [" + resourceDescription + "]");
} catch (Exception e) {
context.onFailure(e);
return;
}

LOGGER.finest("Lock acquired on [" + resourceDescription + "] by " + r.getExternalizableId());
LOGGER.finest("Lock acquired on [" + resourceDescription + "] by " + build.getExternalizableId());
try {

LockedResourcesBuildAction.updateAction(build, new ArrayList<>(lockedResources.keySet()));
PauseAction.endCurrentPause(node);
BodyInvoker bodyInvoker = context.newBodyInvoker()
.withCallback(new Callback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,65 +9,145 @@
package org.jenkins.plugins.lockableresources.actions;

import hudson.model.Action;
import hudson.model.Run;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.jenkins.plugins.lockableresources.LockableResource;
import org.jenkins.plugins.lockableresources.LockableResourcesManager;
import org.jenkins.plugins.lockableresources.Messages;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

// -----------------------------------------------------------------------------
/** BuildAction for lockable resources.
* Shows usage of resources in the build page.
* url: jobUrl/buildNr/locked-resources/
*/
@Restricted(NoExternalUse.class)
public class LockedResourcesBuildAction implements Action {

// -------------------------------------------------------------------------
private final List<ResourcePOJO> lockedResources;

// -------------------------------------------------------------------------
public LockedResourcesBuildAction(List<ResourcePOJO> lockedResources) {
this.lockedResources = lockedResources;
}

// -------------------------------------------------------------------------
public List<ResourcePOJO> getLockedResources() {
return lockedResources;
}

// -------------------------------------------------------------------------
@Override
public String getIconFileName() {
return LockableResourcesRootAction.ICON;
}

// -------------------------------------------------------------------------
@Override
public String getDisplayName() {
return "Locked Resources";
return Messages.LockedResourcesBuildAction_displayName();

Check warning on line 52 in src/main/java/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 52 is not covered by tests
}

// -------------------------------------------------------------------------
@Override
public String getUrlName() {
return "locked-resources";
}

// -------------------------------------------------------------------------
/** Adds *resourceNames* to *build*.
* When the action does not exists, will be created as well.
* When the resource has been used by this build just now, the counter will
* increased to eliminate multiple entries.
* Used in pipelines - lock() step
*/
@Restricted(NoExternalUse.class)
public static void updateAction(Run<?, ?> build, List<String> resourceNames) {
LockedResourcesBuildAction action = build.getAction(LockedResourcesBuildAction.class);

if (action == null) {
List<ResourcePOJO> resPojos = new ArrayList<>();
action = new LockedResourcesBuildAction(resPojos);
build.addAction(action);
}

for (String name : resourceNames) {
LockableResource r = LockableResourcesManager.get().fromName(name);
action.add(new ResourcePOJO(r));
}
}

// -------------------------------------------------------------------------
/** Add the resource to build action.*/
@Restricted(NoExternalUse.class)
private void add(ResourcePOJO r) {
for (ResourcePOJO pojo : this.lockedResources) {
if (pojo.getName().equals(r.getName())) {
pojo.inc();
return;
}
}
this.lockedResources.add(r);
}

// -------------------------------------------------------------------------
/** Create action from resources.
* Used in free-style projects.
*/
@Restricted(NoExternalUse.class)
public static LockedResourcesBuildAction fromResources(Collection<LockableResource> resources) {
List<ResourcePOJO> resPojos = new ArrayList<>();
for (LockableResource r : resources) resPojos.add(new ResourcePOJO(r));
return new LockedResourcesBuildAction(resPojos);
}

// -------------------------------------------------------------------------
public static class ResourcePOJO {

// ---------------------------------------------------------------------
private String name;
private String description;
private int count = 1;

// ---------------------------------------------------------------------
public ResourcePOJO(String name, String description) {
this.name = name;
this.description = description;
}

// ---------------------------------------------------------------------
public ResourcePOJO(LockableResource r) {
this.name = r.getName();
this.description = r.getDescription();
}

// ---------------------------------------------------------------------
public String getName() {
return name;
return this.name;
}

// ---------------------------------------------------------------------
public String getDescription() {
return description;
return this.description;
}

// ---------------------------------------------------------------------
/** Return the counter, how many was / is the resource used in the build.
* Example: you can use the lock() function in parallel stages for the
* same resource.
*/
public int getCounter() {
return this.count;

Check warning on line 144 in src/main/java/org/jenkins/plugins/lockableresources/actions/LockedResourcesBuildAction.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 135-144 are not covered by tests
}

// ---------------------------------------------------------------------
/** Increment counter */
public void inc() {
this.count++;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ LockableResourcesRootAction.StealPermission.Description=This permission grants t
LockableResourcesRootAction.ViewPermission=View
LockableResourcesRootAction.ViewPermission.Description=This permission grants the ability to view \
lockable resources.
LockedResourcesBuildAction.displayName=Used lockable resources
# Java errors
error.labelDoesNotExist=The resource label does not exist: {0}.
error.resourceDoesNotExist=The resource does not exist: {0}.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,83 @@
<?jelly escape-by-default='true'?>
<!--
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright (c) 2013, 6WIND S.A. All rights reserved. *
* *
* This file is part of the Jenkins Lockable Resources Plugin and is *
* published under the MIT license. *
* *
* See the "LICENSE.txt" file for more information. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
The MIT License
Copyright 2023 Martin Pokorny.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->

<!-- LockedResourcesBuildAction
Shows used lockable-resources in the build.
-->
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define"
xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form"
xmlns:i="jelly:fmt" xmlns:p="/lib/hudson/project">
<l:layout title="${it.displayName}">
<?jelly escape-by-default='true'?>
<j:jelly
xmlns:i="jelly:fmt" xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout" xmlns:t="/lib/hudson">
<j:set var="title" value="${it.displayName}" />
<l:layout title="${title}">
<!-- side panel -->
<j:invokeStatic var="currentThread" className="java.lang.Thread" method="currentThread" />
<j:invoke var="buildClass" on="${currentThread.contextClassLoader}" method="loadClass">
<j:arg value="hudson.model.AbstractBuild" />
<j:arg value="hudson.model.Run" />
</j:invoke>
<j:set var="build" value="${request.findAncestorObject(buildClass)}" />
<st:include page="sidepanel.jelly" it="${build}" />
<!-- main panel -->
<l:main-panel>
<h1>${%header.resources}</h1>
<p>${%header.resources.list}</p>
<ul>
<j:forEach var="resource" items="${it.lockedResources}" varStatus="loop">
<li>
<strong>${resource.name}</strong> - <em>${resource.description}</em>
</li>
</j:forEach>
</ul>
<l:app-bar title="${%app.bar.used.resources}">
<a class="jenkins-button" href="${resURL}/lockable-resources/">
<l:icon src="${it.getIconFileName()}" />
${%app.bar.resources}
</a>
<t:help href="https://github.com/jenkinsci/lockable-resources-plugin#locked-resources-build-page" />
</l:app-bar>

<st:adjunct includes="io.jenkins.plugins.data-tables"/>
<link rel="stylesheet" href="${resURL}/plugin/lockable-resources/css/style.css"/>

<div class="table-responsive">
<table
class="jenkins-table jenkins-!-margin-bottom-4 data-table"
id="used-lockable-resources-by-build"
isLoaded="true"
data-remember-search-text="true"
data-columns-definition="[null, null, null, null]"
data-table-configuration="{}"
>
<thead>
<th>${%table.column.index}</th>
<th>${%table.column.name}</th>
<th>${%table.column.description}</th>
<th>${%table.column.counter}</th>
</thead>
<tbody>
<j:forEach var="resource" items="${it.lockedResources}" varStatus="idx">
<tr>
<td>${idx.index + 1}</td>
<td>${resource.name}</td>
<td>${resource.description}</td>
<td>${resource.counter}</td>
</tr>
</j:forEach>
</tbody>
</table>
</div>
<script type="text/javascript" src="${resURL}/plugin/data-tables-api/js/table.js"/>
</l:main-panel>
</l:layout>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# The MIT License
#
# Copyright 2022 Martin Pokorny.
# Copyright 2023 Martin Pokorny.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
Expand All @@ -20,5 +20,10 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

header.resources=Lockable Resources
header.resources.list=This build has locked the following resources:
app.bar.used.resources=Used lockable resources
app.bar.resources=Lockable resources

table.column.index=Index
table.column.name=Resource Name
table.column.description=Description
table.column.counter=Counter

0 comments on commit 1ad65ec

Please sign in to comment.