diff --git a/src/main/java/org/jenkinsci/plugins/envinject/EnvInjectPrebuildJobProperty.java b/src/main/java/org/jenkinsci/plugins/envinject/EnvInjectPrebuildJobProperty.java index be2e354e..fe5b8627 100644 --- a/src/main/java/org/jenkinsci/plugins/envinject/EnvInjectPrebuildJobProperty.java +++ b/src/main/java/org/jenkinsci/plugins/envinject/EnvInjectPrebuildJobProperty.java @@ -1,8 +1,10 @@ package org.jenkinsci.plugins.envinject; +import hudson.DescriptorExtensionList; import hudson.Extension; import hudson.FilePath; import hudson.model.BuildListener; +import hudson.model.Environment; import hudson.model.JobProperty; import hudson.model.JobPropertyDescriptor; import hudson.model.AbstractBuild; @@ -10,16 +12,31 @@ import hudson.model.Descriptor; import hudson.model.Job; import hudson.model.Node; +import hudson.model.Result; +import hudson.model.Run; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import javax.annotation.CheckForNull; + import jenkins.model.Jenkins; +import net.sf.json.JSON; +import net.sf.json.JSONException; import net.sf.json.JSONObject; +import org.jenkinsci.lib.envinject.EnvInjectException; import org.jenkinsci.lib.envinject.EnvInjectLogger; +import org.jenkinsci.plugins.envinject.model.EnvInjectJobPropertyContributor; +import org.jenkinsci.plugins.envinject.model.EnvInjectJobPropertyContributorDescriptor; +import org.jenkinsci.plugins.envinject.service.EnvInjectActionSetter; +import org.jenkinsci.plugins.envinject.service.EnvInjectContributorManagement; import org.jenkinsci.plugins.envinject.service.EnvInjectEnvVars; import org.jenkinsci.plugins.envinject.service.EnvInjectVariableGetter; import org.kohsuke.stapler.StaplerRequest; @@ -32,6 +49,12 @@ public class EnvInjectPrebuildJobProperty> extends JobProper private EnvInjectJobPropertyInfo info = new EnvInjectJobPropertyInfo(); private boolean on; + private boolean keepJenkinsSystemVariables; + private boolean keepBuildVariables; + private boolean overrideBuildParameters; + private EnvInjectJobPropertyContributor[] contributors; + + private transient EnvInjectJobPropertyContributor[] contributorsComputed; @SuppressWarnings("unused") public EnvInjectJobPropertyInfo getInfo() { @@ -43,6 +66,67 @@ public boolean isOn() { return on; } + @SuppressWarnings("unused") + public boolean isKeepJenkinsSystemVariables() { + return keepJenkinsSystemVariables; + } + + @SuppressWarnings("unused") + public boolean isKeepBuildVariables() { + return keepBuildVariables; + } + + @SuppressWarnings("unused") + public boolean isOverrideBuildParameters() { + return overrideBuildParameters; + } + + @SuppressWarnings("unused") + public EnvInjectJobPropertyContributor[] getContributors() { + if (contributorsComputed == null) { + try { + contributorsComputed = computeEnvInjectContributors(); + } catch (org.jenkinsci.lib.envinject.EnvInjectException e) { + e.printStackTrace(); + } + contributors = contributorsComputed; + } + + return Arrays.copyOf(contributors, contributors.length); + } + + private EnvInjectJobPropertyContributor[] computeEnvInjectContributors() throws org.jenkinsci.lib.envinject.EnvInjectException { + + DescriptorExtensionList + descriptors = EnvInjectJobPropertyContributor.all(); + + // If the config are loaded with success (this step) and the descriptors size doesn't have change + // we considerate, they are the same, therefore we retrieve instances + if (contributors != null && contributors.length == descriptors.size()) { + return contributors; + } + + EnvInjectContributorManagement envInjectContributorManagement = new EnvInjectContributorManagement(); + EnvInjectJobPropertyContributor[] contributorsInstance = envInjectContributorManagement.getNewContributorsInstance(); + + //No jobProperty Contributors ==> new configuration + if (contributors == null || contributors.length == 0) { + return contributorsInstance; + } + + List result = new ArrayList(); + for (EnvInjectJobPropertyContributor contributor1 : contributorsInstance) { + for (EnvInjectJobPropertyContributor contributor2 : contributors) { + if (contributor1.getDescriptor().getClass() == contributor2.getDescriptor().getClass()) { + result.add(contributor2); + } else { + result.add(contributor1); + } + } + } + return result.toArray(new EnvInjectJobPropertyContributor[result.size()]); + } + public void setInfo(EnvInjectJobPropertyInfo info) { this.info = info; } @@ -51,14 +135,31 @@ public void setOn(boolean on) { this.on = on; } + public void setKeepJenkinsSystemVariables(boolean keepJenkinsSystemVariables) { + this.keepJenkinsSystemVariables = keepJenkinsSystemVariables; + } + + public void setKeepBuildVariables(boolean keepBuildVariables) { + this.keepBuildVariables = keepBuildVariables; + } + + public void setOverrideBuildParameters(boolean overrideBuildParameters) { + this.overrideBuildParameters = overrideBuildParameters; + } + + public void setContributors(EnvInjectJobPropertyContributor[] jobPropertyContributors) { + this.contributors = jobPropertyContributors; + } + @Override public JobProperty reconfigure(StaplerRequest req, JSONObject form) throws Descriptor.FormException { EnvInjectPrebuildJobProperty property = (EnvInjectPrebuildJobProperty) super.reconfigure(req, form); if (property != null && property.info != null && !Jenkins.getInstance().hasPermission(Jenkins.RUN_SCRIPTS)) { // Don't let non RUN_SCRIPT users set arbitrary groovy script property.info = new EnvInjectJobPropertyInfo(property.info.propertiesFilePath, property.info.propertiesContent, - property.info.getScriptFilePath(), property.info.getScriptContent(), - this.info != null ? this.info.getGroovyScriptContent() : "", property.info.isLoadFilesFromMaster()); + property.info.getScriptFilePath(), property.info.getScriptContent(), + this.info != null ? this.info.getGroovyScriptContent() : "", + property.info.isLoadFilesFromMaster()); } return property; } @@ -69,7 +170,7 @@ public static final class DescriptorImpl extends JobPropertyDescriptor { @Override public String getDisplayName() { - return "[Environment Inject] -" + Messages.envinject_set_displayName(); + return "[Environment Inject (Prebuild)] -" + Messages.envinject_set_displayName(); } @Override @@ -88,16 +189,60 @@ public EnvInjectPrebuildJobProperty newInstance(StaplerRequest req, JSONObject f if (onObject != null) { EnvInjectPrebuildJobProperty envInjectJobProperty = new EnvInjectPrebuildJobProperty(); - EnvInjectJobPropertyInfo info = req.bindParameters(EnvInjectJobPropertyInfo.class, - "envInjectInfoPrebuildJobProperty."); + EnvInjectJobPropertyInfo info = req.bindParameters(EnvInjectJobPropertyInfo.class, "envInjectInfoPrebuildJobProperty."); envInjectJobProperty.setInfo(info); envInjectJobProperty.setOn(true); + if (onObject instanceof JSONObject) { + JSONObject onJSONObject = (JSONObject) onObject; + envInjectJobProperty.setKeepJenkinsSystemVariables(onJSONObject.getBoolean("keepJenkinsSystemVariables")); + envInjectJobProperty.setKeepBuildVariables(onJSONObject.getBoolean("keepBuildVariables")); + envInjectJobProperty.setOverrideBuildParameters(onJSONObject.getBoolean("overrideBuildParameters")); - return envInjectJobProperty; + //Process contributions + setContributors(req, envInjectJobProperty, onJSONObject); + + return envInjectJobProperty; + } } return null; } + + private void setContributors(StaplerRequest req, EnvInjectPrebuildJobProperty envInjectJobProperty, JSONObject onJSONObject) { + if (!onJSONObject.containsKey("contributors")) { + envInjectJobProperty.setContributors(new EnvInjectJobPropertyContributor[0]); + } else { + JSON contribJSON; + try { + contribJSON = onJSONObject.getJSONArray("contributors"); + } catch (JSONException jsone) { + contribJSON = onJSONObject.getJSONObject("contributors"); + } + List contributions = req.bindJSONToList(EnvInjectJobPropertyContributor.class, contribJSON); + EnvInjectJobPropertyContributor[] contributionsArray = contributions.toArray(new EnvInjectJobPropertyContributor[contributions.size()]); + envInjectJobProperty.setContributors(contributionsArray); + } + } + + public DescriptorExtensionList getEnvInjectContributors() { + return EnvInjectJobPropertyContributor.all(); + } + + public @CheckForNull EnvInjectJobPropertyContributor[] getContributorsInstance() { + EnvInjectContributorManagement envInjectContributorManagement = new EnvInjectContributorManagement(); + try { + return envInjectContributorManagement.getNewContributorsInstance(); + } catch (org.jenkinsci.lib.envinject.EnvInjectException e) { + e.printStackTrace(); + } + return null; + } + + public boolean isEnvInjectContributionActivated() { + EnvInjectContributorManagement envInjectContributorManagement = new EnvInjectContributorManagement(); + return envInjectContributorManagement.isEnvInjectContributionActivated(); + } + } /* @@ -108,6 +253,9 @@ public EnvInjectPrebuildJobProperty newInstance(StaplerRequest req, JSONObject f */ @Override public boolean prebuild(AbstractBuild build, BuildListener listener) { + if(!isOn()) { + return true; + } EnvInjectLogger logger = new EnvInjectLogger(listener); FilePath ws = build.getWorkspace(); @@ -130,33 +278,54 @@ public boolean prebuild(AbstractBuild build, BuildListener listener) { } } - // Add Jenkins System variables - logger.info("Keeping Jenkins system variables."); - infraEnvVarsMaster.putAll(variableGetter.getJenkinsSystemVariables(true)); - infraEnvVarsNode.putAll(variableGetter.getJenkinsSystemVariables(false)); + //Add Jenkins System variables + if (isKeepJenkinsSystemVariables()) { + logger.info("Keeping Jenkins system variables."); + infraEnvVarsMaster.putAll(variableGetter.getJenkinsSystemVariables(true)); + infraEnvVarsNode.putAll(variableGetter.getJenkinsSystemVariables(false)); + } - // Add build variables - logger.info("Keeping Jenkins build variables."); - Map buildVariables = variableGetter.getBuildVariables(build, logger); - infraEnvVarsMaster.putAll(buildVariables); - infraEnvVarsNode.putAll(buildVariables); + //Add build variables + if (isKeepBuildVariables()) { + logger.info("Keeping Jenkins build variables."); + Map buildVariables = variableGetter.getBuildVariables(build, logger); + infraEnvVarsMaster.putAll(buildVariables); + infraEnvVarsNode.putAll(buildVariables); + } final FilePath rootPath = getNodeRootPath(); if (rootPath != null) { final EnvInjectEnvVars envInjectEnvVarsService = new EnvInjectEnvVars(logger); - // Execute script - int resultCode = envInjectEnvVarsService.executeScript(info.isLoadFilesFromMaster(), info.getScriptContent(), - rootPath, info.getScriptFilePath(), infraEnvVarsMaster, infraEnvVarsNode, - rootPath.createLauncher(listener), listener); + //Execute script + int resultCode = envInjectEnvVarsService.executeScript(info.isLoadFilesFromMaster(), + info.getScriptContent(), + rootPath, info.getScriptFilePath(), infraEnvVarsMaster, infraEnvVarsNode, rootPath.createLauncher(listener), listener); if (resultCode != 0) { - logger.error("Remote script " + rootPath + " returned an error: " + resultCode); - return false; + build.setResult(Result.FAILURE); + throw new Run.RunnerAbortedException(); } - // Evaluate Groovy script - envInjectEnvVarsService.executeAndGetMapGroovyScript(logger, info.getGroovyScriptContent(), infraEnvVarsNode); + //Evaluate Groovy script + Map groovyMapEnvVars = envInjectEnvVarsService.executeAndGetMapGroovyScript(logger, info.getGroovyScriptContent(), infraEnvVarsNode); + + final Map propertiesVariables = envInjectEnvVarsService.getEnvVarsPropertiesJobProperty(rootPath, + logger, info.isLoadFilesFromMaster(), + info.getPropertiesFilePath(), info.getPropertiesContentMap(previousEnvVars), + infraEnvVarsMaster, infraEnvVarsNode); + + //Get variables get by contribution + Map contributionVariables = getEnvVarsByContribution(build, this, logger, listener); + + final Map mergedVariables = envInjectEnvVarsService.getMergedVariables( + infraEnvVarsNode, + propertiesVariables, + groovyMapEnvVars, + contributionVariables); + + //Add an action to share injected environment variables + new EnvInjectActionSetter(rootPath).addEnvVarsToEnvInjectBuildAction(build, mergedVariables); } } catch (Throwable t) { StringWriter sw = new StringWriter(); @@ -184,4 +353,38 @@ private FilePath getNodeRootPath() { } return null; } + + private Map getEnvVarsByContribution(AbstractBuild build, EnvInjectPrebuildJobProperty envInjectJobProperty, + EnvInjectLogger logger, BuildListener listener) throws EnvInjectException { + + assert envInjectJobProperty != null; + Map contributionVariables = new HashMap(); + + EnvInjectJobPropertyContributor[] contributors = envInjectJobProperty.getContributors(); + if (contributors != null) { + logger.info("Injecting contributions."); + for (EnvInjectJobPropertyContributor contributor : contributors) { + contributionVariables.putAll(contributor.getEnvVars(build, listener)); + } + } + return contributionVariables; + } + + + @Deprecated + private transient boolean injectGlobalPasswords; + @Deprecated + private transient EnvInjectPasswordEntry[] passwordEntries; + @Deprecated + private transient boolean keepSystemVariables; + + @Deprecated + public boolean isInjectGlobalPasswords() { + return injectGlobalPasswords; + } + + @Deprecated + public EnvInjectPasswordEntry[] getPasswordEntries() { + return passwordEntries; + } } diff --git a/src/test/java/org/jenkinsci/plugins/envinject/EnvInjectPrebuildJobPropertyTest.java b/src/test/java/org/jenkinsci/plugins/envinject/EnvInjectPrebuildJobPropertyTest.java new file mode 100644 index 00000000..91e57ff8 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/envinject/EnvInjectPrebuildJobPropertyTest.java @@ -0,0 +1,130 @@ +package org.jenkinsci.plugins.envinject; + +import hudson.model.Cause; +import hudson.model.FreeStyleBuild; +import hudson.model.FreeStyleProject; +import hudson.model.ParametersAction; +import hudson.model.StringParameterValue; +import hudson.model.TaskListener; +import hudson.model.queue.QueueTaskFuture; +import java.io.IOException; +import javax.annotation.Nonnull; +import org.junit.Rule; +import org.jvnet.hudson.test.JenkinsRule; + +import static org.junit.Assert.*; +import org.junit.Ignore; +import org.junit.Test; +import org.jvnet.hudson.test.CaptureEnvironmentBuilder; +import org.jvnet.hudson.test.Issue; + +/** + * Tests for {@link EnvInjectPrebuildJobProperty} + * @author Oleg Nenashev + */ +public class EnvInjectPrebuildJobPropertyTest { + + @Rule + public JenkinsRule jenkinsRule = new JenkinsRule(); + + @Test + public void shouldInjectBuildVarsFromPropertiesContent() throws Exception { + FreeStyleProject project = jenkinsRule.createFreeStyleProject(); + EnvInjectPrebuildJobProperty prop = forPropertiesContent(project, "FOO=BAR"); + + FreeStyleBuild build = jenkinsRule.buildAndAssertSuccess(project); + assertEquals("The FOO variable has not been injected properly", "BAR", + build.getEnvironment(TaskListener.NULL).get("FOO")); + } + + @Test + public void shouldNotInjectVariablesIfPropertyIsDisabled() throws Exception { + FreeStyleProject project = jenkinsRule.createFreeStyleProject(); + + EnvInjectPrebuildJobProperty prop = new EnvInjectPrebuildJobProperty(); + prop.setInfo(new EnvInjectJobPropertyInfo(null, "FOO=BAR", null, null, null, false)); + // prop.setOn(false); // It is default + project.addProperty(prop); + + FreeStyleBuild build = jenkinsRule.buildAndAssertSuccess(project); + assertNull("The plugin should not inject properties by default", + build.getEnvironment(TaskListener.NULL).get("FOO")); + } + + @Test + public void shouldKeepBuildVariablesByDefault() throws Exception { + FreeStyleProject project = jenkinsRule.createFreeStyleProject(); + // We assign a value to another parameter just to enable the engine + EnvInjectPrebuildJobProperty prop = forPropertiesContent(project, "PARAM2=Overridden"); + + QueueTaskFuture scheduled = project.scheduleBuild2(0, new Cause.UserIdCause(), + new ParametersAction(new StringParameterValue("PARAM", "Value"))); + assertNotNull(scheduled); + FreeStyleBuild build = scheduled.get(); + jenkinsRule.assertBuildStatusSuccess(build); + assertEquals("The build parameter has been overridden", "Value", + build.getEnvironment(TaskListener.NULL).get("PARAM")); + } + + @Test + @Ignore("The value is being actually contributed by other env contributors. The feature seems to be obsolete") + public void shouldNotKeepBuildVariablesIfDisabled() throws Exception { + FreeStyleProject project = jenkinsRule.createFreeStyleProject(); + + EnvInjectPrebuildJobProperty prop = forPropertiesContent(project, "PARAM2=Overridden"); + prop.setKeepBuildVariables(false); + + QueueTaskFuture scheduled = project.scheduleBuild2(0, new Cause.UserIdCause(), + new ParametersAction(new StringParameterValue("PARAM", "Value"))); + assertNotNull(scheduled); + FreeStyleBuild build = scheduled.get(); + jenkinsRule.assertBuildStatusSuccess(build); + assertNull("We expect that the PARAM is not specified", + build.getEnvironment(TaskListener.NULL).get("PARAM")); + } + + @Test + @Ignore("It should not override vars according to the manual testing. But it does... " + + "Manual tests also show the wrong value in InjectedVarsAction") + @Issue("JENKINS-29905") + public void shouldNotOverrideBuildParametersByDefault() throws Exception { + FreeStyleProject project = jenkinsRule.createFreeStyleProject(); + EnvInjectPrebuildJobProperty prop = forPropertiesContent(project, "PARAM=Overridden"); + prop.setOverrideBuildParameters(false); + + final CaptureEnvironmentBuilder envCapture = new CaptureEnvironmentBuilder(); + project.getBuildersList().add(envCapture); + + QueueTaskFuture scheduled = project.scheduleBuild2(0, new Cause.UserIdCause(), + new ParametersAction(new StringParameterValue("PARAM", "ValueFromParameter"))); + assertNotNull(scheduled); + FreeStyleBuild build = scheduled.get(); + jenkinsRule.assertBuildStatusSuccess(build); + assertEquals("The variable has been overridden in the environment", "ValueFromParameter", envCapture.getEnvVars().get("PARAM")); + assertEquals("The variable has been overridden in the API", "ValueFromParameter", build.getEnvironment(TaskListener.NULL).get("PARAM")); + } + + @Test + public void shouldOverrideBuildParametersIfEnabled() throws Exception { + FreeStyleProject project = jenkinsRule.createFreeStyleProject(); + EnvInjectPrebuildJobProperty prop = forPropertiesContent(project, "PARAM=Overridden"); + prop.setOverrideBuildParameters(true); + + QueueTaskFuture scheduled = project.scheduleBuild2(0, new Cause.UserIdCause(), + new ParametersAction(new StringParameterValue("PARAM", "ValueFromParameter"))); + assertNotNull(scheduled); + FreeStyleBuild build = scheduled.get(); + jenkinsRule.assertBuildStatusSuccess(build); + assertEquals("The build parameter value has not been overridden", "Overridden", build.getEnvironment(TaskListener.NULL).get("PARAM")); + } + + @Nonnull + public EnvInjectPrebuildJobProperty + forPropertiesContent(@Nonnull FreeStyleProject job, @Nonnull String content) throws IOException { + final EnvInjectPrebuildJobProperty prop = new EnvInjectPrebuildJobProperty(); + prop.setInfo(new EnvInjectJobPropertyInfo(null, content, null, null, null, false)); + prop.setOn(true); // Property becomes enabled by default + job.addProperty(prop); + return prop; + } +}