Skip to content
Permalink
Browse files

Merge pull request #10 from oleg-nenashev/feature/JENKINS-42745

[JENKINS-42745] - Windows Agent Installer should generate valid jenkins-slave.xml for old APIs
  • Loading branch information...
oleg-nenashev committed Apr 25, 2017
2 parents 981a038 + 89cffce commit a3b83c82f27e41f089d2d79b930d2c92e27919f1
@@ -26,9 +26,14 @@
import static hudson.util.jna.SHELLEXECUTEINFO.*;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
@@ -45,6 +50,13 @@

private final static Logger LOGGER = Logger.getLogger(WindowsSlaveInstaller.class.getName());

/**
* Lists the new required macros, which has been added to the pattern since 1.6.
* All of these macros are expected to have a default value.
*/
private static final Set<String> ADDITIONAL_REQUIRED_MACROS = new TreeSet<>(
Arrays.asList(AgentURLMacroProvider.MACRO_NAME));

public WindowsSlaveInstaller() {
}

@@ -173,23 +185,27 @@ public static String generateServiceId(String slaveRoot) throws IOException {
* @deprecated Use {@link #generateSlaveXml(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.util.Map)}
*/
@Deprecated
@Restricted(NoExternalUse.class)
public static String generateSlaveXml(String id, String java, String vmargs, String args) throws IOException {
return generateSlaveXml(id, java, vmargs, args, Collections.<String, String>emptyMap());
}

/**
* Generates WinSW configuration for the agent.
* This method takes a template from the resources and injects macro values there.
* Macro values can be contributed by {@code extraMacroValues} or by {@link MacroValueProvider}s.
* @param id Service Id
* @param java Path to Java
* @param vmargs JVM args arguments to be passed
* @param args slave.jar arguments to be passed
* @param extraMacroValues Additional macro values to be injected.
* The list of required macros is provided in {@link #ADDITIONAL_REQUIRED_MACROS}.
* If the macro value is not provided, the implementation will look up for the default value in
* available {@link MacroValueProvider}s.
*
* @return Generated WinSW configuration file.
* @throws IOException The file cannot be generated
* @throws IOException The file cannot be generated or if not all macro variables can be resolved
* @since TODO
*/
@Restricted(NoExternalUse.class)
public static String generateSlaveXml(String id, String java, String vmargs, String args, @Nonnull Map<String, String> extraMacroValues) throws IOException {
// Just a legacy behavior for the obsolete installer
String xml = IOUtils.toString(WindowsSlaveInstaller.class.getResourceAsStream("jenkins-slave.xml"), "UTF-8");
@@ -199,9 +215,36 @@ public static String generateSlaveXml(String id, String java, String vmargs, Str
xml = xml.replace("@ARGS@", args);
xml = xml.replace("\n","\r\n");

for (Map.Entry<String, String> entry : extraMacroValues.entrySet()) {
// Resolve missing macros to retain compatibility with old API
Map <String, String> toResolve = new HashMap<>(extraMacroValues);
Collection<MacroValueProvider> defaultProviders = MacroValueProvider.allDefaultProviders();
for (String macroName : ADDITIONAL_REQUIRED_MACROS) {
if (!extraMacroValues.containsKey(macroName)) {
for (MacroValueProvider provider : defaultProviders) {
String defaultValue = provider.getDefaulValue(macroName);
if (defaultValue != null) {
toResolve.put(macroName, defaultValue);
break;
}
}
}
}

for (Map.Entry<String, String> entry : toResolve.entrySet()) {
xml = xml.replace("@" + entry.getKey() + "@", entry.getValue());
}

if (xml.contains("@")) {
Set<String> unresolvedMacros = new HashSet<>();
for (String macroName : ADDITIONAL_REQUIRED_MACROS) {
if (xml.contains("@" + macroName + "@")) {
unresolvedMacros.add(macroName);
}
}
// If there is any unknown macro, it will be caught by tests.
throw new IOException("Unresolved macros in the XML file: " + String.join(",", unresolvedMacros));
}

return xml;
}

@@ -221,34 +264,53 @@ public static String generateSlaveXml(String id, String java, String vmargs, Str

/**
* Macro provider implementation for the internal use.
* Currently the implementation supports only one macro for the provider.
*/
@Restricted(NoExternalUse.class)
/*package*/ static abstract class MacroValueProvider {

@Nonnull
public abstract Map<String, String> getMacroValues();

@Nonnull
public abstract Set<String> getMacroNames();

@CheckForNull
public abstract String getDefaulValue(@Nonnull String macroName);

static final Collection<MacroValueProvider> allDefaultProviders() {
return Arrays.<MacroValueProvider>asList(new AgentURLMacroProvider(null));
}
}

/*package*/ static class AgentURLMacroProvider extends MacroValueProvider {

@Nonnull
static final String MACRO_NAME = "AGENT_DOWNLOAD_URL";
static final String DEFAULT_DISABLED_VALUE = "<!-- <download from=\"TODO:jarFile\" to=\"%BASE%\\slave.jar\"/> -->";

private static final Set<String> MACRO_NAMES = new TreeSet<>(Arrays.asList(MACRO_NAME));

@CheckForNull
private final LaunchConfiguration launchConfiguration;

public AgentURLMacroProvider(@Nonnull LaunchConfiguration launchConfig) {
public AgentURLMacroProvider(@CheckForNull LaunchConfiguration launchConfig) {
this.launchConfiguration = launchConfig;
}

@Override
public Map<String, String> getMacroValues() {
Map<String, String> res = new TreeMap<>();

URL remotingURL = null;
try {
remotingURL = launchConfiguration.getLatestJarURL();
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, "Failed to retrieve the latest Remoting JAR URL. Auto-download will be disabled", ex);
if (launchConfiguration != null) {
try {
remotingURL = launchConfiguration.getLatestJarURL();
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, "Failed to retrieve the latest Remoting JAR URL. Auto-download will be disabled", ex);
}
}

res.put("AGENT_DOWNLOAD_URL", generateDownloadMacroValue(remotingURL));
res.put(MACRO_NAME, generateDownloadMacroValue(remotingURL));
return res;
}

@@ -261,9 +323,23 @@ public AgentURLMacroProvider(@Nonnull LaunchConfiguration launchConfig) {
macroValue = "<!-- " + macroValue + " -->";
}
} else {
macroValue = "<!-- <download from=\"TODO:jarFile\" to=\"%BASE%\\slave.jar\"/> -->";
macroValue = DEFAULT_DISABLED_VALUE;
}
return macroValue;
}

@Override
public Set<String> getMacroNames() {
return Collections.unmodifiableSet(MACRO_NAMES);
}

@Override
public String getDefaulValue(String macroName) {
if (MACRO_NAMES.contains(macroName)) {
// Fine since we keep one macro
return DEFAULT_DISABLED_VALUE;
}
return null;
}
}
}
@@ -29,12 +29,15 @@ of this software and associated documentation files (the "Software"), to deal
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.CheckForNull;
import org.apache.commons.io.IOUtils;
import static org.hamcrest.CoreMatchers.*;
import org.jenkinsci.modules.slave_installer.InstallationException;
import org.jenkinsci.modules.slave_installer.LaunchConfiguration;
import org.jenkinsci.modules.slave_installer.Prompter;
import org.jenkinsci.modules.windows_slave_installer.WindowsSlaveInstaller.AgentURLMacroProvider;
import org.junit.Assert;
import static org.junit.Assert.assertThat;
import org.junit.Before;
@@ -79,6 +82,34 @@ public String promptPassword(String question) throws InterruptedException {
};
}

@Test
@Issue("JENKINS-42745")
public void shouldGenerateValidConfigWithOldAPI() throws Exception {
@SuppressWarnings("deprecation")
String xml = WindowsSlaveInstaller.generateSlaveXml("serviceid", "myjava", "", "");
assertThat("There is unresolved macro", xml, not(containsString("@")));
}

@Test
@Issue("JENKINS-42745")
public void shouldThrowUnresolveMacro() throws Exception {
// Create a nested macro definition, which won't be resolved
Map<String,String> macroValues = new HashMap<>();
macroValues.put(AgentURLMacroProvider.MACRO_NAME, "Depends on @" + AgentURLMacroProvider.MACRO_NAME + "@");

// Try to resolve
try {
String xml = WindowsSlaveInstaller.generateSlaveXml("serviceid", "myjava", "", "", macroValues);
} catch (IOException ex) {
assertThat("Exception message does not mention unresolved macros",
ex.getMessage(), containsString("Unresolved macros in the XML file: "));
assertThat("Exception message does not reference the macro name",
ex.getMessage(), containsString(AgentURLMacroProvider.MACRO_NAME));
return;
}
Assert.fail("Expected the Unresolved macro exception");
}

@Test
@Issue("JENKINS-39237")
public void shouldGenerateConfigWithValidDownloadLink() throws InstallationException, IOException, InterruptedException {

0 comments on commit a3b83c8

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.