Skip to content

Commit 51919ab

Browse files
committed
#5666 - Set extra properties for the agent, from server
1 parent 8d0b695 commit 51919ab

File tree

16 files changed

+380
-72
lines changed

16 files changed

+380
-72
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2019 ThoughtWorks, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.thoughtworks.go.agent.common.util;
18+
19+
import org.apache.commons.lang3.StringUtils;
20+
import org.apache.http.Header;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
24+
import java.util.Arrays;
25+
import java.util.HashMap;
26+
import java.util.LinkedHashMap;
27+
import java.util.Map;
28+
import java.util.stream.Collectors;
29+
30+
public class HeaderUtil {
31+
private static Logger LOGGER = LoggerFactory.getLogger(HeaderUtil.class);
32+
33+
public static Map<String, String> parseExtraProperties(Header extraPropertiesHeader) {
34+
if (extraPropertiesHeader == null || StringUtils.isBlank(extraPropertiesHeader.getValue())) {
35+
return new HashMap<>();
36+
}
37+
38+
try {
39+
return Arrays.stream(extraPropertiesHeader.getValue().trim().split(" +"))
40+
.map(property -> property.split("="))
41+
.collect(Collectors.toMap(
42+
keyAndValue -> keyAndValue[0].replaceAll("%20", " "),
43+
keyAndValue -> keyAndValue[1].replaceAll("%20", " "),
44+
(value1, value2) -> value1,
45+
LinkedHashMap::new));
46+
} catch (Exception e) {
47+
LOGGER.warn("Failed to parse extra properties header value: {}", extraPropertiesHeader.getValue(), e);
48+
return new HashMap<>();
49+
}
50+
}
51+
52+
}

agent-common/src/main/java/com/thoughtworks/go/agent/launcher/ServerBinaryDownloader.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2018 ThoughtWorks, Inc.
2+
* Copyright 2019 ThoughtWorks, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,8 +19,12 @@
1919
import com.thoughtworks.go.agent.ServerUrlGenerator;
2020
import com.thoughtworks.go.agent.common.ssl.GoAgentServerHttpClientBuilder;
2121
import com.thoughtworks.go.agent.common.util.Downloader;
22+
import com.thoughtworks.go.agent.common.util.HeaderUtil;
2223
import com.thoughtworks.go.util.PerfTimer;
2324
import com.thoughtworks.go.util.SslVerificationMode;
25+
import com.thoughtworks.go.util.SystemEnvironment;
26+
import org.apache.commons.lang3.StringUtils;
27+
import org.apache.http.Header;
2428
import org.apache.http.HttpResponse;
2529
import org.apache.http.HttpStatus;
2630
import org.apache.http.client.ClientProtocolException;
@@ -34,6 +38,12 @@
3438
import org.slf4j.LoggerFactory;
3539

3640
import java.io.*;
41+
import java.util.Arrays;
42+
import java.util.HashMap;
43+
import java.util.Map;
44+
import java.util.stream.Collectors;
45+
46+
import static com.thoughtworks.go.util.SystemEnvironment.AGENT_EXTRA_PROPERTIES_HEADER;
3747

3848
public class ServerBinaryDownloader implements Downloader {
3949

@@ -47,6 +57,7 @@ public class ServerBinaryDownloader implements Downloader {
4757
private static final String SSL_PORT_HEADER = "Cruise-Server-Ssl-Port";
4858
private static final int HTTP_TIMEOUT_IN_MILLISECONDS = 5000;
4959
private GoAgentServerHttpClientBuilder httpClientBuilder;
60+
private Map<String, String> extraProperties;
5061

5162
public ServerBinaryDownloader(ServerUrlGenerator urlGenerator, File rootCertFile, SslVerificationMode sslVerificationMode) {
5263
this(new GoAgentServerHttpClientBuilder(rootCertFile, sslVerificationMode), urlGenerator);
@@ -65,6 +76,10 @@ public String getSslPort() {
6576
return sslPort;
6677
}
6778

79+
public Map<String, String> getExtraProperties() {
80+
return extraProperties;
81+
}
82+
6883
public boolean downloadIfNecessary(final DownloadableFile downloadableFile) {
6984
boolean updated = false;
7085
boolean downloaded = false;
@@ -98,6 +113,7 @@ void fetchUpdateCheckHeaders(DownloadableFile downloadableFile) throws Exception
98113
handleInvalidResponse(response, url);
99114
this.md5 = response.getFirstHeader(MD5_HEADER).getValue();
100115
this.sslPort = response.getFirstHeader(SSL_PORT_HEADER).getValue();
116+
this.extraProperties = HeaderUtil.parseExtraProperties(response.getFirstHeader(AGENT_EXTRA_PROPERTIES_HEADER));
101117
}
102118
}
103119

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2019 ThoughtWorks, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.thoughtworks.go.agent.common.util;
18+
19+
import org.apache.http.message.BasicHeader;
20+
import org.junit.Test;
21+
22+
import java.util.HashMap;
23+
import java.util.Map;
24+
25+
import static org.hamcrest.CoreMatchers.is;
26+
import static org.junit.Assert.assertThat;
27+
28+
public class HeaderUtilTest {
29+
@Test
30+
public void shouldGetExtraPropertiesFromHeader() {
31+
assertExtraProperties(null, new HashMap<>());
32+
assertExtraProperties("", new HashMap<>());
33+
34+
assertExtraProperties("Key1=Value1 key2=value2", new HashMap<String, String>() {{
35+
put("Key1", "Value1");
36+
put("key2", "value2");
37+
}});
38+
39+
assertExtraProperties(" Key1=Value1 key2=value2 ", new HashMap<String, String>() {{
40+
put("Key1", "Value1");
41+
put("key2", "value2");
42+
}});
43+
44+
assertExtraProperties("Key1=Value1 key2=value2 key2=value3", new HashMap<String, String>() {{
45+
put("Key1", "Value1");
46+
put("key2", "value2");
47+
}});
48+
49+
assertExtraProperties("Key1%20WithSpace=Value1%20WithSpace key2=value2", new HashMap<String, String>() {{
50+
put("Key1 WithSpace", "Value1 WithSpace");
51+
put("key2", "value2");
52+
}});
53+
}
54+
55+
@Test
56+
public void shouldNotFailIfExtraPropertiesAreNotFormattedProperly() {
57+
assertExtraProperties("abc", new HashMap<>());
58+
}
59+
60+
private void assertExtraProperties(String actualHeaderValue, Map<String, String> expectedProperties) {
61+
final Map<String, String> actualResult = HeaderUtil.parseExtraProperties(new BasicHeader("some-key", actualHeaderValue));
62+
assertThat(actualResult, is(expectedProperties));
63+
64+
}
65+
66+
}

agent-common/src/test/java/com/thoughtworks/go/agent/launcher/ServerBinaryDownloaderTest.java

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017 ThoughtWorks, Inc.
2+
* Copyright 2019 ThoughtWorks, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,6 +39,8 @@
3939
import java.net.UnknownHostException;
4040
import java.security.DigestInputStream;
4141
import java.security.MessageDigest;
42+
import java.util.HashMap;
43+
import java.util.Map;
4244

4345
import static org.hamcrest.core.Is.is;
4446
import static org.junit.Assert.assertThat;
@@ -55,7 +57,7 @@ public class ServerBinaryDownloaderTest {
5557
public ExpectedException exception = ExpectedException.none();
5658

5759
@After
58-
public void tearDown() throws Exception {
60+
public void tearDown() {
5961
FileUtils.deleteQuietly(new File(Downloader.AGENT_BINARY));
6062
FileUtils.deleteQuietly(DownloadableFile.AGENT.getLocalFile());
6163
}
@@ -76,15 +78,40 @@ public void shouldSetMd5AndSSLPortHeaders() throws Exception {
7678
}
7779

7880
@Test
79-
public void shouldDownloadAgentJarFile() throws Exception {
81+
public void shouldGetExtraPropertiesFromHeader() {
82+
assertExtraProperties("", new HashMap<>());
83+
84+
assertExtraProperties("Key1=Value1 key2=value2", new HashMap<String, String>() {{
85+
put("Key1", "Value1");
86+
put("key2", "value2");
87+
}});
88+
89+
assertExtraProperties("Key1=Value1 key2=value2 key2=value3", new HashMap<String, String>() {{
90+
put("Key1", "Value1");
91+
put("key2", "value2");
92+
}});
93+
94+
assertExtraProperties("Key1%20WithSpace=Value1%20WithSpace key2=value2", new HashMap<String, String>() {{
95+
put("Key1 WithSpace", "Value1 WithSpace");
96+
put("key2", "value2");
97+
}});
98+
}
99+
100+
@Test
101+
public void shouldNotFailIfExtraPropertiesAreNotFormattedProperly() {
102+
assertExtraProperties("abc", new HashMap<>());
103+
}
104+
105+
@Test
106+
public void shouldDownloadAgentJarFile() {
80107
ServerBinaryDownloader downloader = new ServerBinaryDownloader(ServerUrlGeneratorMother.generatorFor("localhost", server.getPort()), null, SslVerificationMode.NONE);
81108
assertThat(DownloadableFile.AGENT.doesNotExist(), is(true));
82109
downloader.downloadIfNecessary(DownloadableFile.AGENT);
83110
assertThat(DownloadableFile.AGENT.getLocalFile().exists(), is(true));
84111
}
85112

86113
@Test
87-
public void shouldReturnTrueIfTheFileIsDownloaded() throws Exception {
114+
public void shouldReturnTrueIfTheFileIsDownloaded() {
88115
ServerBinaryDownloader downloader = new ServerBinaryDownloader(ServerUrlGeneratorMother.generatorFor("localhost", server.getPort()), null, SslVerificationMode.NONE);
89116
assertThat(downloader.downloadIfNecessary(DownloadableFile.AGENT), is(true));
90117
}
@@ -154,4 +181,16 @@ public void shouldReturnFalseIfTheServerDoesNotRespondWithEntity() throws Except
154181
ServerBinaryDownloader downloader = new ServerBinaryDownloader(builder, ServerUrlGeneratorMother.generatorFor("localhost", server.getPort()));
155182
assertThat(downloader.download(DownloadableFile.AGENT), is(false));
156183
}
184+
185+
private void assertExtraProperties(String valueToSet, Map<String, String> expectedValue) {
186+
ServerBinaryDownloader downloader = new ServerBinaryDownloader(ServerUrlGeneratorMother.generatorFor("localhost", server.getPort()), null, SslVerificationMode.NONE);
187+
try {
188+
server.setExtraPropertiesHeaderValue(valueToSet);
189+
downloader.downloadIfNecessary(DownloadableFile.AGENT);
190+
191+
assertThat(downloader.getExtraProperties(), is(expectedValue));
192+
} finally {
193+
server.setExtraPropertiesHeaderValue(null);
194+
}
195+
}
157196
}

agent-process-launcher/src/main/java/com/thoughtworks/go/agent/AgentProcessParentImpl.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2018 ThoughtWorks, Inc.
2+
* Copyright 2019 ThoughtWorks, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@
3333
import java.util.ArrayList;
3434
import java.util.List;
3535
import java.util.Map;
36+
import java.util.stream.Stream;
3637

3738
import static org.apache.commons.lang3.StringUtils.isEmpty;
3839
import static org.apache.commons.lang3.StringUtils.join;
@@ -65,7 +66,8 @@ public int run(String launcherVersion, String launcherMd5, ServerUrlGenerator ur
6566
ServerBinaryDownloader tfsImplDownloader = new ServerBinaryDownloader(urlGenerator, rootCertFile, sslVerificationMode);
6667
tfsImplDownloader.downloadIfNecessary(DownloadableFile.TFS_IMPL);
6768

68-
command = agentInvocationCommand(agentDownloader.getMd5(), launcherMd5, pluginZipDownloader.getMd5(), tfsImplDownloader.getMd5(), env, context, agentDownloader.getSslPort());
69+
command = agentInvocationCommand(agentDownloader.getMd5(), launcherMd5, pluginZipDownloader.getMd5(), tfsImplDownloader.getMd5(),
70+
env, context, agentDownloader.getSslPort(), agentDownloader.getExtraProperties());
6971
LOG.info("Launching Agent with command: {}", join(command, " "));
7072

7173
Process agent = invoke(command);
@@ -117,8 +119,8 @@ private void removeShutdownHook(Shutdown shutdownHook) {
117119
}
118120

119121
private String[] agentInvocationCommand(String agentMD5, String launcherMd5, String agentPluginsZipMd5, String tfsImplMd5, Map<String, String> env, Map context,
120-
@Deprecated String sslPort // the port is kept for backward compatibility to ensure that old bootstrappers are able to launch new agents
121-
) {
122+
@Deprecated String sslPort, // the port is kept for backward compatibility to ensure that old bootstrappers are able to launch new agents
123+
Map<String, String> extraProperties) {
122124
AgentBootstrapperBackwardCompatibility backwardCompatibility = backwardCompatibility(context);
123125

124126
String startupArgsString = env.get(AGENT_STARTUP_ARGS);
@@ -133,6 +135,9 @@ private String[] agentInvocationCommand(String agentMD5, String launcherMd5, Str
133135
}
134136
}
135137
}
138+
139+
extraProperties.forEach((key, value) -> commandSnippets.add(property(key, value)));
140+
136141
commandSnippets.add(property(GoConstants.AGENT_PLUGINS_MD5, agentPluginsZipMd5));
137142
commandSnippets.add(property(GoConstants.AGENT_JAR_MD5, agentMD5));
138143
commandSnippets.add(property(GoConstants.GIVEN_AGENT_LAUNCHER_JAR_MD5, launcherMd5));

agent-process-launcher/src/test/java/com/thoughtworks/go/agent/AgentProcessParentImplTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,37 @@ public void shouldStartSubprocessWithCommandLine() throws InterruptedException,
106106
}));
107107
}
108108

109+
@Test
110+
public void shouldAddAnyExtraPropertiesFoundToTheAgentInvocation() throws InterruptedException, IOException {
111+
final List<String> cmd = new ArrayList<>();
112+
String expectedAgentMd5 = TEST_AGENT.getMd5();
113+
String expectedAgentPluginsMd5 = TEST_AGENT_PLUGINS.getMd5();
114+
String expectedTfsMd5 = TEST_TFS_IMPL.getMd5();
115+
116+
server.setExtraPropertiesHeaderValue("extra.property=value1%20with%20space extra%20property%20with%20space=value2%20with%20space");
117+
AgentProcessParentImpl bootstrapper = createBootstrapper(cmd);
118+
int returnCode = bootstrapper.run("launcher_version", "bar", getURLGenerator(), new HashMap<>(), context());
119+
120+
assertThat(returnCode, is(42));
121+
assertThat(cmd.toArray(new String[]{}), equalTo(new String[]{
122+
(getProperty("java.home") + getProperty("file.separator") + "bin" + getProperty("file.separator") + "java"),
123+
"-Dextra.property=value1 with space",
124+
"-Dextra property with space=value2 with space",
125+
"-Dagent.plugins.md5=" + expectedAgentPluginsMd5,
126+
"-Dagent.binary.md5=" + expectedAgentMd5,
127+
"-Dagent.launcher.md5=bar",
128+
"-Dagent.tfs.md5=" + expectedTfsMd5,
129+
"-jar",
130+
"agent.jar",
131+
"-serverUrl",
132+
"https://localhost:" + server.getSecurePort() + "/go/",
133+
"-sslVerificationMode",
134+
"NONE",
135+
"-rootCertFile",
136+
"/path/to/cert.pem"
137+
}));
138+
}
139+
109140
private Process mockProcess() throws InterruptedException {
110141
return mockProcess(new ByteArrayInputStream(new byte[0]), new ByteArrayInputStream(new byte[0]), new ByteArrayOutputStream());
111142
}

agent/src/main/java/com/thoughtworks/go/agent/AgentController.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017 ThoughtWorks, Inc.
2+
* Copyright 2019 ThoughtWorks, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -80,7 +80,7 @@ public AgentController(SslInfrastructureService sslInfrastructureService,
8080
public final void loop() {
8181
try {
8282
LOG.debug("[Agent Loop] Trying to retrieve work.");
83-
agentUpgradeService.checkForUpgrade();
83+
agentUpgradeService.checkForUpgradeAndExtraProperties();
8484
sslInfrastructureService.registerIfNecessary(getAgentAutoRegistrationProperties());
8585
work();
8686
LOG.debug("[Agent Loop] Successfully retrieved work.");

0 commit comments

Comments
 (0)