Skip to content

Commit f8ab50a

Browse files
committed
Added patch support for environments update API
1 parent 1027d49 commit f8ab50a

File tree

12 files changed

+541
-41
lines changed

12 files changed

+541
-41
lines changed

config/config-api/src/com/thoughtworks/go/config/BasicEnvironmentConfig.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ public void addPipeline(final CaseInsensitiveString pipelineName) {
140140
pipelines.add(new EnvironmentPipelineConfig(pipelineName));
141141
}
142142

143+
@Override
144+
public void removePipeline(final CaseInsensitiveString pipelineName) {
145+
pipelines.remove(new EnvironmentPipelineConfig(pipelineName));
146+
}
147+
143148
@Override
144149
public boolean contains(String pipelineName) {
145150
return pipelines.containsPipelineNamed(new CaseInsensitiveString(pipelineName));

config/config-api/src/com/thoughtworks/go/config/EnvironmentConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ public interface EnvironmentConfig extends ParamsAttributeAware, Validatable, En
5858

5959
void addPipeline(CaseInsensitiveString pipelineName);
6060

61+
void removePipeline(CaseInsensitiveString pipelineName);
62+
6163
boolean contains(String pipelineName);
6264

6365
void validateContainsOnlyPipelines(List<CaseInsensitiveString> pipelineNames);

config/config-api/src/com/thoughtworks/go/config/merge/MergeEnvironmentConfig.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,11 @@ public void addPipeline(CaseInsensitiveString pipelineName) {
192192
this.getFirstEditablePart().addPipeline(pipelineName);
193193
}
194194

195+
@Override
196+
public void removePipeline(CaseInsensitiveString pipelineName) {
197+
this.getFirstEditablePart().removePipeline(pipelineName);
198+
}
199+
195200
@Override
196201
public void removeAgent(String uuid) {
197202
for(EnvironmentConfig part : this)
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright 2016 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.config.update;
18+
19+
import com.thoughtworks.go.config.*;
20+
import com.thoughtworks.go.config.commands.EntityConfigUpdateCommand;
21+
import com.thoughtworks.go.i18n.Localizable;
22+
import com.thoughtworks.go.i18n.LocalizedMessage;
23+
import com.thoughtworks.go.server.domain.Username;
24+
import com.thoughtworks.go.server.service.GoConfigService;
25+
import com.thoughtworks.go.server.service.result.HttpLocalizedOperationResult;
26+
import com.thoughtworks.go.server.service.result.LocalizedOperationResult;
27+
import com.thoughtworks.go.serverhealth.HealthStateType;
28+
29+
import java.util.ArrayList;
30+
import java.util.List;
31+
32+
public class PatchEnvironmentCommand extends EnvironmentCommand implements EntityConfigUpdateCommand<EnvironmentConfig> {
33+
private final GoConfigService goConfigService;
34+
private final EnvironmentConfig environmentConfig;
35+
private final List<String> pipelinesToAdd;
36+
private final List<String> pipelinesToRemove;
37+
private final List<String> agentsToAdd;
38+
private final List<String> agentsToRemove;
39+
private final Username username;
40+
private final Localizable.CurryableLocalizable actionFailed;
41+
private final HttpLocalizedOperationResult result;
42+
43+
public PatchEnvironmentCommand(GoConfigService goConfigService, EnvironmentConfig environmentConfig, List<String> pipelinesToAdd, List<String> pipelinesToRemove, List<String> agentsToAdd, List<String> agentsToRemove, Username username, Localizable.CurryableLocalizable actionFailed, HttpLocalizedOperationResult result) {
44+
super(actionFailed, environmentConfig, result);
45+
46+
this.goConfigService = goConfigService;
47+
this.environmentConfig = environmentConfig;
48+
this.pipelinesToAdd = pipelinesToAdd;
49+
this.pipelinesToRemove = pipelinesToRemove;
50+
this.agentsToAdd = agentsToAdd;
51+
this.agentsToRemove = agentsToRemove;
52+
this.username = username;
53+
this.actionFailed = actionFailed;
54+
this.result = result;
55+
}
56+
57+
private boolean validateAgents(List<String> uuids, LocalizedOperationResult result, Agents agents) {
58+
List<String> unknownAgents = new ArrayList<>();
59+
60+
for (String uuid : uuids) {
61+
AgentConfig agent = agents.getAgentByUuid(uuid);
62+
if (agent.isNull()) {
63+
unknownAgents.add(uuid);
64+
}
65+
}
66+
67+
if (!unknownAgents.isEmpty()) {
68+
result.badRequest(LocalizedMessage.string("AGENTS_WITH_UUIDS_NOT_FOUND", unknownAgents));
69+
return false;
70+
}
71+
return true;
72+
}
73+
74+
private boolean validatePipelines(List<String> pipelines, HttpLocalizedOperationResult result, CruiseConfig config) {
75+
ArrayList<String> unknownPipelines = new ArrayList<>();
76+
77+
for (String pipeline : pipelines) {
78+
try{
79+
config.pipelineConfigByName(new CaseInsensitiveString(pipeline));
80+
} catch(PipelineNotFoundException e){
81+
unknownPipelines.add(pipeline);
82+
}
83+
}
84+
85+
if (!unknownPipelines.isEmpty()) {
86+
result.badRequest(LocalizedMessage.string("PIPELINES_WITH_NAMES_NOT_FOUND", unknownPipelines));
87+
return false;
88+
}
89+
return true;
90+
91+
}
92+
93+
94+
@Override
95+
public void update(CruiseConfig preprocessedConfig) throws Exception {
96+
EnvironmentsConfig environments = preprocessedConfig.getEnvironments();
97+
int index = environments.indexOf(environmentConfig);
98+
EnvironmentConfig preprocessedEnvironmentConfig = environments.get(index);
99+
100+
if(isValidConfig(preprocessedConfig)){
101+
for (String uuid : agentsToAdd) {
102+
preprocessedEnvironmentConfig.addAgent(uuid);
103+
}
104+
105+
for (String uuid : agentsToRemove) {
106+
preprocessedEnvironmentConfig.removeAgent(uuid);
107+
}
108+
109+
for (String pipelineName : pipelinesToAdd) {
110+
preprocessedEnvironmentConfig.addPipeline(new CaseInsensitiveString(pipelineName));
111+
}
112+
113+
for (String pipelineName : pipelinesToRemove) {
114+
preprocessedEnvironmentConfig.removePipeline(new CaseInsensitiveString(pipelineName));
115+
}
116+
}
117+
}
118+
119+
@Override
120+
public void clearErrors() {
121+
BasicCruiseConfig.clearErrors(environmentConfig);
122+
}
123+
124+
@Override
125+
public boolean canContinue(CruiseConfig cruiseConfig) {
126+
if (!goConfigService.isAdministrator(username.getUsername())) {
127+
Localizable noPermission = LocalizedMessage.string("NO_PERMISSION_TO_UPDATE_ENVIRONMENT", environmentConfig.name().toString(), username.getDisplayName());
128+
result.unauthorized(noPermission, HealthStateType.unauthorised());
129+
return false;
130+
}
131+
return true;
132+
}
133+
134+
public boolean isValidConfig(CruiseConfig preprocessedConfig) {
135+
boolean isValid = validateAgents(agentsToAdd, result, preprocessedConfig.agents());
136+
isValid = isValid && validateAgents(agentsToRemove, result, preprocessedConfig.agents());
137+
isValid = isValid && validatePipelines(pipelinesToAdd, result, preprocessedConfig);
138+
isValid = isValid && validatePipelines(pipelinesToRemove, result, preprocessedConfig);
139+
return isValid;
140+
}
141+
}

server/src/com/thoughtworks/go/server/service/EnvironmentConfigService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.thoughtworks.go.config.exceptions.NoSuchEnvironmentException;
2424
import com.thoughtworks.go.config.update.AddEnvironmentCommand;
2525
import com.thoughtworks.go.config.update.DeleteEnvironmentCommand;
26+
import com.thoughtworks.go.config.update.PatchEnvironmentCommand;
2627
import com.thoughtworks.go.config.update.UpdateEnvironmentCommand;
2728
import com.thoughtworks.go.domain.*;
2829
import com.thoughtworks.go.i18n.Localizable;
@@ -221,6 +222,16 @@ public void updateEnvironment(final EnvironmentConfig oldEnvironmentConfig, fina
221222
}
222223
}
223224

225+
public void patchEnvironment(final EnvironmentConfig environmentConfig, List<String> pipelinesToAdd, List<String> pipelinesToRemove, List<String> agentsToAdd, List<String> agentsToRemove, final Username username, final HttpLocalizedOperationResult result) {
226+
Localizable.CurryableLocalizable actionFailed = LocalizedMessage.string("ENV_UPDATE_FAILED", environmentConfig.name());
227+
228+
PatchEnvironmentCommand patchEnvironmentCommand = new PatchEnvironmentCommand(goConfigService, environmentConfig, pipelinesToAdd, pipelinesToRemove, agentsToAdd, agentsToRemove, username, actionFailed, result);
229+
update(patchEnvironmentCommand, environmentConfig, username, result, actionFailed);
230+
if (result.isSuccessful()) {
231+
result.setMessage(LocalizedMessage.string("UPDATE_ENVIRONMENT_SUCCESS", environmentConfig.name()));
232+
}
233+
}
234+
224235
public void deleteEnvironment(final EnvironmentConfig environmentConfig, final Username username, final HttpLocalizedOperationResult result) {
225236
String environmentName = environmentConfig.name().toString();
226237
Localizable.CurryableLocalizable actionFailed = LocalizedMessage.string("ENV_DELETE_FAILED", environmentName);

server/test/integration/com/thoughtworks/go/server/service/EnvironmentConfigServiceIntegrationTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.io.IOException;
3737
import java.util.ArrayList;
3838
import java.util.Arrays;
39+
import java.util.List;
3940
import java.util.Map;
4041

4142
import static com.thoughtworks.go.server.service.EnvironmentConfigServiceTest.env;
@@ -221,6 +222,41 @@ public void shouldReturnTheCorrectLocalizedMessageWhenUserDoesNotHavePermissionT
221222
assertThat(result.message(localizer), is("Failed to delete environment 'foo'. User 'evil_hacker' does not have permission to update environments"));
222223
}
223224

225+
@Test
226+
public void shouldPatchAnEnvironment() throws Exception{
227+
String environmentName = "env";
228+
229+
BasicEnvironmentConfig env = environmentConfig(environmentName);
230+
Username user = Username.ANONYMOUS;
231+
String uuid = "uuid-1";
232+
agentConfigService.addAgent(new AgentConfig(uuid, "host-1", "192.168.1.2"), user);
233+
goConfigService.addEnvironment(env);
234+
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
235+
236+
List<String> agentsToremove = new ArrayList<>();
237+
List<String> agentsToAdd = new ArrayList<>();
238+
agentsToAdd.add(uuid);
239+
List<String> pipelinesToAdd = new ArrayList<>();
240+
List<String> pipelinesToRemove = new ArrayList<>();
241+
242+
service.patchEnvironment(service.getEnvironmentConfig(environmentName), pipelinesToAdd, pipelinesToRemove, agentsToAdd, agentsToremove, user, result);
243+
EnvironmentConfig updatedEnv = service.named(env.name().toString());
244+
245+
assertThat(updatedEnv.name(), is(new CaseInsensitiveString(environmentName)));
246+
assertThat(updatedEnv.getAgents().getUuids(), is(Arrays.asList("uuid-1")));
247+
assertThat(result.message(localizer), containsString("Updated environment 'env'."));
248+
}
249+
250+
@Test
251+
public void shouldReturnTheCorrectLocalizedMessageWhenUserDoesNotHavePermissionToPatch() throws IOException, NoSuchEnvironmentException {
252+
configHelper.addEnvironments("foo");
253+
configHelper.turnOnSecurity();
254+
configHelper.addAdmins("super_hero");
255+
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();
256+
service.patchEnvironment(service.getEnvironmentConfig("foo"), new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(), new ArrayList<String>(),new Username(new CaseInsensitiveString("evil_hacker")), result);
257+
assertThat(result.message(localizer), is("Failed to update environment 'foo'. User 'evil_hacker' does not have permission to update environments"));
258+
}
259+
224260
@Test
225261
public void shouldReturnAClonedInstanceOfEnvironmentConfig() throws NoSuchEnvironmentException {
226262
HttpLocalizedOperationResult result = new HttpLocalizedOperationResult();

0 commit comments

Comments
 (0)