Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,12 @@ lock(resource: 'staging-server', inversePrecedence: true) {
}
```

*Resolve a variable configured with the resource name*
*Resolve a variable configured with the resource name and properties*

```groovy
lock(label: 'some_resource', variable: 'LOCKED_RESOURCE') {
echo env.LOCKED_RESOURCE
echo env.LOCKED_RESOURCE_PROP_ABC
}
```

Expand All @@ -84,9 +85,11 @@ lock(label: 'some_resource', variable: 'LOCKED_RESOURCE', quantity: 2) {

// first lock
echo env.LOCKED_RESOURCE0
echo env.LOCKED_RESOURCE0_PROP_ABC

// second lock
echo env.LOCKED_RESOURCE1
echo env.LOCKED_RESOURCE1_PROP_ABC
}
```

Expand Down Expand Up @@ -119,6 +122,9 @@ unclassified:
description: "Description_A"
labels: "Label_A"
reservedBy: "Reserved_A"
properties:
- name: "Property_A"
value: "Value"
```

## Changelog
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import java.io.PrintStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -99,7 +99,7 @@ public boolean start() throws Exception {
}

public static void proceed(
final List<String> resourcenames,
final LinkedHashMap<String, List<LockableResourceProperty>> lockedResources,
StepContext context,
String resourceDescription,
final String variable,
Expand All @@ -124,7 +124,7 @@ public static void proceed(
BodyInvoker bodyInvoker =
context
.newBodyInvoker()
.withCallback(new Callback(resourcenames, resourceDescription, inversePrecedence));
.withCallback(new Callback(new ArrayList<>(lockedResources.keySet()), resourceDescription, inversePrecedence));
if (variable != null && variable.length() > 0) {
// set the variable for the duration of the block
bodyInvoker.withContext(
Expand All @@ -135,11 +135,18 @@ public static void proceed(

@Override
public void expand(@NonNull EnvVars env) {
final Map<String, String> variables = new HashMap<>();
final String resources = String.join(",", resourcenames);
variables.put(variable, resources);
for (int index = 0; index < resourcenames.size(); ++index) {
variables.put(variable + index, resourcenames.get(index));
final LinkedHashMap<String, String> variables = new LinkedHashMap<>();
final String resourceNames = lockedResources.keySet().stream().collect(Collectors.joining(","));
variables.put(variable, resourceNames);
int index = 0;
for (Entry<String, List<LockableResourceProperty>> lockResourceEntry : lockedResources.entrySet()) {
String lockEnvName = variable + index;
variables.put(lockEnvName, lockResourceEntry.getKey());
for (LockableResourceProperty lockProperty : lockResourceEntry.getValue()) {
String propEnvName = lockEnvName + "_" + lockProperty.getName();
variables.put(propEnvName, lockProperty.getValue());
}
++index;
}
LOGGER.finest("Setting "
+ variables.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(", "))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public class LockableResource extends AbstractDescribableImpl<LockableResource>
* being held, it becomes ephemeral and will disappear when freed.
*/
private boolean ephemeral;
private List<LockableResourceProperty> properties = new ArrayList<>();

private long queueItemId = NOT_QUEUED;
private String queueItemProject = null;
Expand Down Expand Up @@ -173,6 +174,16 @@ public boolean isEphemeral() {
return ephemeral;
}

@Exported
public List<LockableResourceProperty> getProperties() {
return properties;
}

@DataBoundSetter
public void setProperties(List<LockableResourceProperty> properties) {
this.properties = (properties == null ? new ArrayList<>() : properties);
}

public boolean isValidLabel(String candidate, Map<String, Object> params) {
return labelsContain(candidate);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.jenkins.plugins.lockableresources;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import java.io.Serializable;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.export.Exported;

public class LockableResourceProperty extends AbstractDescribableImpl<LockableResourceProperty>
implements Serializable {

private String name;
private String value;

@DataBoundConstructor
public LockableResourceProperty() {
}

@DataBoundSetter
public void setName(String name) {
this.name = name;
}

@DataBoundSetter
public void setValue(String value) {
this.value = value;
}

@Exported
public String getName() {
return name;
}

@Exported
public String getValue() {
return value;
}

@Override
public String toString() {
return name;
}

@Extension
public static class DescriptorImpl extends Descriptor<LockableResourceProperty> {

@NonNull
@Override
public String getDisplayName() {
return "Property";
}
}

private static final long serialVersionUID = 1L;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import jenkins.model.GlobalConfiguration;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
Expand Down Expand Up @@ -432,10 +434,10 @@ public synchronized boolean lock(
}
if (context != null) {
// since LockableResource contains transient variables, they cannot be correctly serialized
// hence we use their unique resource names
List<String> resourceNames = new ArrayList<>();
// hence we use their unique resource names and properties
LinkedHashMap<String, List<LockableResourceProperty>> resourceNames = new LinkedHashMap<>();
for (LockableResource resource : resources) {
resourceNames.add(resource.getName());
resourceNames.put(resource.getName(), resource.getProperties());
}
LockStepExecution.proceed(resourceNames, context, logmessage, variable, inversePrecedence);
}
Expand Down Expand Up @@ -544,13 +546,13 @@ public synchronized void unlockNames(
// remove context from queue and process it
unqueueContext(nextContext.getContext());

List<String> resourceNamesToLock = new ArrayList<>();
LinkedHashMap<String, List<LockableResourceProperty>> resourcesToLock = new LinkedHashMap<>();

// lock all (old and new resources)
for (LockableResource requiredResource : requiredResourceForNextContext) {
try {
requiredResource.setBuild(nextContext.getContext().get(Run.class));
resourceNamesToLock.add(requiredResource.getName());
resourcesToLock.put(requiredResource.getName(), requiredResource.getProperties());
} catch (Exception e) {
// skip this context, as the build cannot be retrieved (maybe it was deleted while
// running?)
Expand Down Expand Up @@ -589,7 +591,7 @@ public synchronized void unlockNames(

// continue with next context
LockStepExecution.proceed(
resourceNamesToLock,
resourcesToLock,
nextContext.getContext(),
nextContext.getResourceDescription(),
nextContext.getVariableName(),
Expand Down Expand Up @@ -695,6 +697,27 @@ public synchronized boolean createResourceWithLabel(String name, String label) {
return false;
}

public synchronized boolean createResourceWithLabelAndProperties(String name, String label, Map<String, String> properties) {
if (name != null && label != null && properties != null) {
LockableResource existent = fromName(name);
if (existent == null) {
LockableResource resource = new LockableResource(name);
resource.setLabels(label);
resource.setProperties(
properties.entrySet().stream().map(e -> {
LockableResourceProperty p = new LockableResourceProperty();
p.setName(e.getKey());
p.setValue(e.getValue());
return p;
}).collect(Collectors.toList()));
getResources().add(resource);
save();
return true;
}
}
return false;
}

/**
* Reserves an available resource for the userName indefinitely (until that person, or some
* explicit scripted action, decides to release the resource).
Expand Down Expand Up @@ -824,13 +847,13 @@ public synchronized void unreserve(List<LockableResource> resources) {
return;
} else {
unreserveResources(resources);
List<String> resourceNamesToLock = new ArrayList<>();
LinkedHashMap<String, List<LockableResourceProperty>> resourcesToLock = new LinkedHashMap<>();

// lock all (old and new resources)
for (LockableResource requiredResource : requiredResourceForNextContext) {
try {
requiredResource.setBuild(nextContext.getContext().get(Run.class));
resourceNamesToLock.add(requiredResource.getName());
resourcesToLock.put(requiredResource.getName(), requiredResource.getProperties());
} catch (Exception e) {
// skip this context, as the build cannot be retrieved (maybe it was deleted while
// running?)
Expand All @@ -848,7 +871,7 @@ public synchronized void unreserve(List<LockableResource> resources) {

// continue with next context
LockStepExecution.proceed(
resourceNamesToLock,
resourcesToLock,
nextContext.getContext(),
nextContext.getResourceDescription(),
nextContext.getVariableName(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
import hudson.model.TaskListener;
import hudson.model.listeners.RunListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.jenkins.plugins.lockableresources.LockableResource;
import org.jenkins.plugins.lockableresources.LockableResourceProperty;
import org.jenkins.plugins.lockableresources.LockableResourcesManager;
import org.jenkins.plugins.lockableresources.actions.LockedResourcesBuildAction;
import org.jenkins.plugins.lockableresources.actions.ResourceVariableNameAction;
Expand Down Expand Up @@ -74,10 +74,15 @@ public void onStarted(Run<?, ?> build, TaskListener listener) {
.map(LockableResource::getName)
.collect(Collectors.joining(","))));

// also add a numbered variable for each acquired lock
// also add a numbered variable for each acquired lock along with properties of the lock
int index = 0;
for (LockableResource lr : required) {
envsToSet.add(new StringParameterValue(resources.requiredVar + index, lr.getName()));
String lockEnvName = resources.requiredVar + index;
envsToSet.add(new StringParameterValue(lockEnvName, lr.getName()));
for (LockableResourceProperty lockProperty : lr.getProperties()) {
String propEnvName = lockEnvName + "_" + lockProperty.getName();
envsToSet.add(new StringParameterValue(propEnvName, lockProperty.getValue()));
}
++index;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,13 @@
<f:entry title="${%Reserved by}" field="reservedBy">
<f:textbox/>
</f:entry>
<f:entry title="${%Properties}">
<f:repeatableProperty field="properties" header="" minimum="0" add="${%Add Property}">
<f:block>
<div align="right">
<f:repeatableDeleteButton value="${%Delete Property}" />
</div>
</f:block>
</f:repeatableProperty>
</f:entry>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?jelly escape-by-default='true'?>
<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">
<f:entry title="${%Name}" field="name">
<f:textbox/>
</f:entry>
<f:entry title="${%Value}" field="value">
<f:textbox/>
</f:entry>
</j:jelly>
Loading