Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use build parameters as resource, label and number. #214

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8

[*.{html,java,jelly,xml}]
[*.{java}]
indent_style = space
indent_size = 4

[*.{html,jelly,xml}]
indent_style = space
indent_size = 2
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.List;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.jenkins.plugins.lockableresources.queue.Utils;
import org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript;
import org.jenkinsci.plugins.scriptsecurity.scripts.ApprovalContext;
import org.kohsuke.stapler.AncestorInPath;
Expand All @@ -48,33 +49,20 @@ public RequiredResourcesProperty(
@CheckForNull SecureGroovyScript resourceMatchScript) {
super();

if (resourceNames == null || resourceNames.trim().isEmpty()) {
this.resourceNames = null;
} else {
this.resourceNames = resourceNames.trim();
}
if (resourceNamesVar == null || resourceNamesVar.trim().isEmpty()) {
this.resourceNamesVar = null;
} else {
this.resourceNamesVar = resourceNamesVar.trim();
}
if (resourceNumber == null || resourceNumber.trim().isEmpty()) {
this.resourceNumber = null;
} else {
this.resourceNumber = resourceNumber.trim();
}
String labelNamePreparation = (labelName == null || labelName.trim().isEmpty()) ? null : labelName.trim();
this.resourceNames = Util.fixEmptyAndTrim(resourceNames);
this.resourceNamesVar = Util.fixEmptyAndTrim(resourceNamesVar);
this.resourceNumber = Util.fixEmptyAndTrim(resourceNumber);
if (resourceMatchScript != null) {
this.resourceMatchScript = resourceMatchScript.configuringWithKeyItem();
this.labelName = labelNamePreparation;
this.labelName = Util.fixEmptyAndTrim(labelName);
} else if (labelName != null && labelName.startsWith(LockableResource.GROOVY_LABEL_MARKER)) {
this.resourceMatchScript = new SecureGroovyScript(
labelName.substring(LockableResource.GROOVY_LABEL_MARKER.length()), false, null)
.configuring(ApprovalContext.create());
this.labelName = null;
} else {
this.resourceMatchScript = null;
this.labelName = labelNamePreparation;
this.labelName = Util.fixEmptyAndTrim(labelName);
}
}

Expand Down Expand Up @@ -171,9 +159,9 @@ public FormValidation doCheckResourceNames(
@QueryParameter String value,
@QueryParameter String labelName,
@QueryParameter boolean script,
@AncestorInPath Item item) {
@AncestorInPath AbstractProject<?, ?> project) {
// check permission, security first
checkPermission(item);
checkPermission(project);

String labelVal = Util.fixEmptyAndTrim(labelName);
String names = Util.fixEmptyAndTrim(value);
Expand All @@ -184,14 +172,30 @@ public FormValidation doCheckResourceNames(
return FormValidation.error(Messages.error_labelAndNameOrGroovySpecified());
} else {
List<String> wrongNames = new ArrayList<>();
List<String> varNames = new ArrayList<>();
List<String> unknownParams = new ArrayList<>();
for (String name : names.split("\\s+")) {
boolean found = LockableResourcesManager.get().resourceExist(name);
if (!found) wrongNames.add(name);
if (!found) {
if (Utils.containsParameter(name)) {
List<String> badParams = Utils.checkParameters(name, project);
if (!badParams.isEmpty()) {
unknownParams.addAll(badParams);
}
varNames.add(name);
} else {
wrongNames.add(name);
}
}
}
if (wrongNames.isEmpty()) {
if (wrongNames.isEmpty() && varNames.isEmpty() && unknownParams.isEmpty()) {
return FormValidation.ok();
} else {
} else if (!wrongNames.isEmpty()) {
return FormValidation.error(Messages.error_resourceDoesNotExist(wrongNames));
} else if (!unknownParams.isEmpty()) {
return FormValidation.error(Messages.error_parameterDoesNotExist(unknownParams));
} else {
return FormValidation.warning(Messages.warn_resourceNotValidated(varNames));
}
}
}
Expand All @@ -201,9 +205,9 @@ public FormValidation doCheckLabelName(
@QueryParameter String value,
@QueryParameter String resourceNames,
@QueryParameter boolean script,
@AncestorInPath Item item) {
@AncestorInPath AbstractProject<?, ?> project) {
// check permission, security first
checkPermission(item);
checkPermission(project);

String label = Util.fixEmptyAndTrim(value);
String names = Util.fixEmptyAndTrim(resourceNames);
Expand All @@ -215,6 +219,12 @@ public FormValidation doCheckLabelName(
} else {
if (LockableResourcesManager.get().isValidLabel(label)) {
return FormValidation.ok();
} else if (Utils.containsParameter(label)) {
List<String> badParams = Utils.checkParameters(label, project);
if (!badParams.isEmpty()) {
return FormValidation.error(Messages.error_parameterDoesNotExist(badParams));
}
return FormValidation.warning(Messages.warn_labelNotValidated(label));
} else {
return FormValidation.error(Messages.error_labelDoesNotExist(label));
}
Expand All @@ -227,35 +237,47 @@ public FormValidation doCheckResourceNumber(
@QueryParameter String resourceNames,
@QueryParameter String labelName,
@QueryParameter String resourceMatchScript,
@AncestorInPath Item item) {
@AncestorInPath AbstractProject<?, ?> project) {
// check permission, security first
checkPermission(item);
checkPermission(project);

String number = Util.fixEmptyAndTrim(value);
String names = Util.fixEmptyAndTrim(resourceNames);
String label = Util.fixEmptyAndTrim(labelName);
String script = Util.fixEmptyAndTrim(resourceMatchScript);

if (number == null || number.isEmpty() || number.trim().equals("0")) {
if (number == null || number.equals("0")) {
return FormValidation.ok();
}

int numAsInt;
int numAsInt = 0;
try {
numAsInt = Integer.parseInt(number);
} catch (NumberFormatException e) {
return FormValidation.error(Messages.error_couldNotParseToint());
if (Utils.isParameter(number)) {
List<String> badParams = Utils.checkParameters(number, project);
if (!badParams.isEmpty()) {
return FormValidation.error(Messages.error_parameterDoesNotExist(badParams));
}
return FormValidation.warning(Messages.warn_valueNotValidated(number));
}
return FormValidation.error(Messages.error_couldNotParseToInt());
}

int numResources = 0;
if (names != null) {
numResources = names.split("\\s+").length;
} else if (label != null || script != null) {
} else if (label != null) {
numResources = LockableResourcesManager.get()
.getResourcesWithLabel(label, null)
.size();
} else if (script != null) {
numResources = Integer.MAX_VALUE;
}

if (numResources < numAsInt) {
if (numAsInt > numResources) {
return FormValidation.error(String.format(
Messages.error_givenAmountIsGreaterThatResourcesAmount(), numAsInt, numResources));
Messages.error_givenAmountIsGreaterThanResourcesAmount(), numAsInt, numResources));
}
return FormValidation.ok();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
package org.jenkins.plugins.lockableresources.queue;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.EnvVars;
import hudson.Extension;
import hudson.matrix.MatrixBuild;
import hudson.model.AbstractBuild;
Expand Down Expand Up @@ -43,8 +44,8 @@ public void onStarted(Run<?, ?> build, TaskListener listener) {
synchronized (lrm.syncResources) {
Job<?, ?> proj = Utils.getProject(build);
List<LockableResource> required = new ArrayList<>();

LockableResourcesStruct resources = Utils.requiredResources(proj);
EnvVars env = new EnvVars(((AbstractBuild<?, ?>) build).getBuildVariables());
LockableResourcesStruct resources = Utils.requiredResources(proj, env);

if (resources != null) {
if (resources.requiredNumber != null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.EnvVars;
import hudson.Extension;
import hudson.matrix.MatrixConfiguration;
import hudson.matrix.MatrixProject;
Expand Down Expand Up @@ -51,19 +52,38 @@ public CauseOfBlockage canRun(Queue.Item item) {
Job<?, ?> project = Utils.getProject(item);
if (project == null) return null;

LockableResourcesStruct resources = Utils.requiredResources(project);
EnvVars env = new EnvVars();
for (ParametersAction pa : item.getActions(ParametersAction.class)) {
for (ParameterValue p : pa.getParameters()) {
try {
String value = p.createVariableResolver(null).resolve(p.getName());
if (value != null) env.put(p.getName(), value);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Unable to resolve parameter, " + p.getName(), e);
}
}
}

LockableResourcesStruct resources = Utils.requiredResources(project, env);

if (resources == null
|| (resources.required.isEmpty()
&& resources.label.isEmpty()
&& resources.getResourceMatchScript() == null)) {
return null;
}

int resourceNumber;
try {
resourceNumber = Integer.parseInt(resources.requiredNumber);
} catch (NumberFormatException e) {
resourceNumber = 0;
int resourceNumber = 0;
if (resources.requiredNumber != null) {
try {
resourceNumber = Integer.parseInt(resources.requiredNumber);
} catch (NumberFormatException e) {
LOGGER.log(
Level.WARNING,
"Failed to convert the required number to an integer, " + resources.requiredNumber,
e);
resourceNumber = 0;
}
}

LOGGER.finest(project.getName() + " trying to get resources with these details: " + resources);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public LockableResourcesStruct(RequiredResourcesProperty property, EnvVars env)

requiredVar = property.getResourceNamesVar();

requiredNumber = property.getResourceNumber();
requiredNumber = env.expand(property.getResourceNumber());
if (requiredNumber != null && requiredNumber.equals("0")) requiredNumber = null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,36 @@
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.EnvVars;
import hudson.matrix.Axis;
import hudson.matrix.MatrixConfiguration;
import hudson.matrix.MatrixProject;
import hudson.model.AbstractProject;
import hudson.model.Job;
import hudson.model.ParametersDefinitionProperty;
import hudson.model.Queue;
import hudson.model.Run;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import org.jenkins.plugins.lockableresources.RequiredResourcesProperty;

public final class Utils {
private Utils() {}

/**
* Pattern for capturing variables. Either $xyz, ${xyz} or ${a.b} but not $a.b
*/
private static final Pattern VARIABLE = Pattern.compile("\\$([A-Za-z0-9_]+|\\{[A-Za-z0-9_.]+\\})");

/**
* Pattern for capturing parameters. ${xyz} but not $${xyz}
*/
private static final Pattern PARAMETER = Pattern.compile("(?<!\\$)\\$\\{([A-Za-z0-9_.]+)\\}");

@CheckForNull
public static Job<?, ?> getProject(@NonNull Queue.Item item) {
if (item.task instanceof Job) return (Job<?, ?>) item.task;
Expand All @@ -32,8 +53,7 @@ private Utils() {}
}

@CheckForNull
public static LockableResourcesStruct requiredResources(@NonNull Job<?, ?> project) {
EnvVars env = new EnvVars();
public static LockableResourcesStruct requiredResources(@NonNull Job<?, ?> project, EnvVars env) {

if (project instanceof MatrixConfiguration) {
env.putAll(((MatrixConfiguration) project).getCombination());
Expand All @@ -45,4 +65,48 @@ public static LockableResourcesStruct requiredResources(@NonNull Job<?, ?> proje

return null;
}

public static boolean isVariable(String name) {
return VARIABLE.matcher(name).matches();
}

@Nonnull
public static List<String> getProjectParameterNames(AbstractProject<?, ?> project) {
List<String> names = new ArrayList<>();

if (project instanceof MatrixProject) {
Iterator<Axis> axisIter = ((MatrixProject) project).getAxes().iterator();
while (axisIter.hasNext()) {
names.add(axisIter.next().getName());
}
}

ParametersDefinitionProperty params = project.getProperty(ParametersDefinitionProperty.class);
if (params != null) names.addAll(params.getParameterDefinitionNames());

if (!names.isEmpty()) return names;
return Collections.emptyList();
}

public static boolean isParameter(String s) {
return PARAMETER.matcher(s).matches();
}

public static boolean containsParameter(String s) {
return PARAMETER.matcher(s).find();
}

@Nonnull
public static List<String> checkParameters(String s, AbstractProject<?, ?> project) {
List<String> unknownParameters = new ArrayList<>();
List<String> paramNames = getProjectParameterNames(project);
Matcher m = PARAMETER.matcher(s);
while (m.find()) {
String param = m.group(1);
if (!paramNames.contains(param)) {
unknownParameters.add(param);
}
}
return unknownParameters;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,19 @@ LockableResourcesRootAction.ViewPermission.Description=This permission grants th
LockableResourcesRootAction.QueueChangeOrderPermission=Queue
LockableResourcesRootAction.QueueChangeOrderPermission.Description=This permission grants the ability to \
manually manipulate the lockable resources queue..
LockedResourcesBuildAction.displayName=Lockable resources
# Java warnings
warn.labelNotValidated=The resource label cannot be validated as it contains parameters: {0}.
warn.resourceNotValidated=The resource cannot be validated as it contains parameters: {0}.
warn.valueNotValidated=The value cannot be validated as it is a parameter value: {0}.
# Java errors
error.labelDoesNotExist=The resource label does not exist: {0}.
error.resourceDoesNotExist=The resource does not exist: {0}.
error.parameterDoesNotExist=The parameter does not exist: {0}.
error.labelOrNameMustBeSpecified=Either resource label or resource name must be specified.
error.labelAndNameSpecified=Resource label and resource name cannot be specified simultaneously.
error.labelAndNameOrGroovySpecified=Only resource label, groovy expression, or resource names can be defined, not more than one.
error.couldNotParseToint=Could not parse the given value as integer.
error.givenAmountIsGreaterThatResourcesAmount=Given amount %d is greater than amount of resources: %d.
error.couldNotParseToInt=Could not parse the given value as integer.
error.givenAmountIsGreaterThanResourcesAmount=Given amount %d is greater than amount of resources: %d.
error.resourceAlreadyLocked=Resource {0} already reserved or locked!
error.invalidResourceSelectionStrategy=The strategy "{0}" is not supported. Valid options are {1}.
error.isNotANumber=The queue position must be a number. Given: {0}
Expand All @@ -43,4 +47,5 @@ LockStep.displayName=Lock shared resource
LockStepResource.displayName=Resource
LockableResource.displayName=Resource
LockableResourcesManager.displayName=External Resources
LockedResourcesBuildAction.displayName=Lockable resources
RequiredResourcesProperty.displayName=Required Lockable Resources
Loading