Skip to content

Commit 79c7168

Browse files
committed
Add an api for bulk updates of agents. (#2340)
1 parent 9a02711 commit 79c7168

File tree

15 files changed

+755
-37
lines changed

15 files changed

+755
-37
lines changed

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,14 @@
1616

1717
package com.thoughtworks.go.config;
1818

19-
import java.util.ArrayList;
20-
import java.util.Collection;
21-
import java.util.List;
22-
2319
import com.thoughtworks.go.domain.ConfigErrors;
2420
import com.thoughtworks.go.domain.IpAddress;
2521
import com.thoughtworks.go.remote.AgentIdentifier;
2622
import com.thoughtworks.go.util.StringUtil;
2723
import com.thoughtworks.go.util.SystemUtil;
2824

25+
import java.util.Collection;
26+
2927
import static java.lang.String.format;
3028

3129
/**
@@ -136,6 +134,10 @@ public void setDisabled(Boolean disabled) {
136134
isDisabled = disabled;
137135
}
138136

137+
public void enable() {
138+
disable(Boolean.FALSE);
139+
}
140+
139141
public void disable() {
140142
disable(Boolean.TRUE);
141143
}

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,18 +16,18 @@
1616

1717
package com.thoughtworks.go.config;
1818

19-
import java.util.ArrayList;
20-
import java.util.List;
21-
import java.util.Set;
22-
import java.util.TreeSet;
23-
2419
import com.thoughtworks.go.config.exceptions.NoSuchEnvironmentException;
2520
import com.thoughtworks.go.domain.BaseCollection;
2621
import com.thoughtworks.go.domain.ConfigErrors;
2722
import com.thoughtworks.go.domain.EnvironmentPipelineMatcher;
2823
import com.thoughtworks.go.domain.EnvironmentPipelineMatchers;
2924
import com.thoughtworks.go.util.comparator.AlphaAsciiComparator;
3025

26+
import java.util.ArrayList;
27+
import java.util.List;
28+
import java.util.Set;
29+
import java.util.TreeSet;
30+
3131
/**
3232
* @understands the current persistent information related to multiple logical groupings of machines
3333
*/
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*************************GO-LICENSE-START*********************************
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+
*************************GO-LICENSE-END***********************************/
16+
17+
package com.thoughtworks.go.config.exceptions;
18+
19+
import org.apache.commons.lang.StringUtils;
20+
21+
import java.util.List;
22+
23+
public class NoSuchAgentException extends Exception {
24+
public NoSuchAgentException(List<String> unknownAgents) {
25+
super(String.format("Agents [%s] could not be found", StringUtils.join(unknownAgents, ", ")));
26+
}
27+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
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.config.exceptions.NoSuchAgentException;
22+
import com.thoughtworks.go.config.exceptions.NoSuchEnvironmentException;
23+
import com.thoughtworks.go.i18n.LocalizedMessage;
24+
import com.thoughtworks.go.server.domain.Username;
25+
import com.thoughtworks.go.server.service.GoConfigService;
26+
import com.thoughtworks.go.server.service.result.LocalizedOperationResult;
27+
import com.thoughtworks.go.serverhealth.HealthStateType;
28+
import com.thoughtworks.go.util.TriState;
29+
import com.thoughtworks.go.validation.AgentConfigsUpdateValidator;
30+
31+
import java.util.ArrayList;
32+
import java.util.HashSet;
33+
import java.util.List;
34+
import java.util.Set;
35+
36+
public class AgentsEntityConfigUpdateCommand implements EntityConfigUpdateCommand<Agents> {
37+
private final Username username;
38+
private final LocalizedOperationResult result;
39+
private final List<String> uuids;
40+
private final List<String> environmentsToAdd;
41+
private final List<String> environmentsToRemove;
42+
private final TriState state;
43+
private final List<String> resourcesToAdd;
44+
private final List<String> resourcesToRemove;
45+
private GoConfigService goConfigService;
46+
public Agents agents;
47+
48+
public AgentsEntityConfigUpdateCommand(Username username, LocalizedOperationResult result, List<String> uuids, List<String> environmentsToAdd, List<String> environmentsToRemove, TriState state, List<String> resourcesToAdd, List<String> resourcesToRemove, GoConfigService goConfigService) {
49+
this.username = username;
50+
this.result = result;
51+
this.uuids = uuids;
52+
this.environmentsToAdd = environmentsToAdd;
53+
this.environmentsToRemove = environmentsToRemove;
54+
this.state = state;
55+
this.resourcesToAdd = resourcesToAdd;
56+
this.resourcesToRemove = resourcesToRemove;
57+
this.goConfigService = goConfigService;
58+
}
59+
60+
@Override
61+
public boolean canContinue(CruiseConfig cruiseConfig) {
62+
if (goConfigService.isAdministrator(username.getUsername())) {
63+
return true;
64+
}
65+
66+
result.unauthorized(LocalizedMessage.string("UNAUTHORIZED_TO_OPERATE_AGENTS"), HealthStateType.unauthorised());
67+
return false;
68+
}
69+
70+
private List<AgentConfig> getValidAgents(List<String> uuids, LocalizedOperationResult result, Agents agents) throws NoSuchAgentException {
71+
List<AgentConfig> goodAgents = new ArrayList<>();
72+
List<String> unknownAgents = new ArrayList<>();
73+
74+
for (String uuid : uuids) {
75+
AgentConfig agent = agents.getAgentByUuid(uuid);
76+
if (!agent.isNull()) {
77+
goodAgents.add(agent);
78+
} else {
79+
unknownAgents.add(uuid);
80+
}
81+
}
82+
if (!unknownAgents.isEmpty()) {
83+
result.badRequest(LocalizedMessage.string("AGENTS_WITH_UUIDS_NOT_FOUND", unknownAgents));
84+
throw new NoSuchAgentException(unknownAgents);
85+
}
86+
87+
return goodAgents;
88+
}
89+
90+
private void validateEnvironment(Set<CaseInsensitiveString> allEnvironmentNames, List<String> environmentsToOperate, LocalizedOperationResult result) throws NoSuchEnvironmentException {
91+
for (String environment : environmentsToOperate) {
92+
CaseInsensitiveString environmentName = new CaseInsensitiveString(environment);
93+
if (!allEnvironmentNames.contains(environmentName)) {
94+
result.badRequest(LocalizedMessage.string("ENV_NOT_FOUND", environmentName));
95+
throw new NoSuchEnvironmentException(environmentName);
96+
}
97+
}
98+
}
99+
100+
@Override
101+
public void update(CruiseConfig preprocessedConfig) throws Exception {
102+
List<AgentConfig> goodAgents = getValidAgents(uuids, result, preprocessedConfig.agents());
103+
104+
Set<CaseInsensitiveString> allEnvironmentNames = new HashSet<>(goConfigService.getEnvironments().names());
105+
106+
validateEnvironment(allEnvironmentNames, environmentsToAdd, result);
107+
validateEnvironment(allEnvironmentNames, environmentsToRemove, result);
108+
109+
for (AgentConfig agentConfig : goodAgents) {
110+
if (state.isFalse()) {
111+
agentConfig.disable();
112+
}
113+
114+
if (state.isTrue()) {
115+
agentConfig.enable();
116+
}
117+
118+
for (String r : resourcesToAdd) {
119+
agentConfig.addResource(new Resource(r));
120+
}
121+
122+
for (String r : resourcesToRemove) {
123+
agentConfig.removeResource(new Resource(r));
124+
}
125+
126+
for (String environment : environmentsToAdd) {
127+
EnvironmentConfig environmentConfig = preprocessedConfig.getEnvironments().find(new CaseInsensitiveString(environment));
128+
if (environmentConfig != null){
129+
environmentConfig.addAgentIfNew(agentConfig.getUuid());
130+
}
131+
}
132+
133+
for (String environment : environmentsToRemove) {
134+
EnvironmentConfig environmentConfig = preprocessedConfig.getEnvironments().find(new CaseInsensitiveString(environment));
135+
if (environmentConfig != null)
136+
environmentConfig.removeAgent(agentConfig.getUuid());
137+
}
138+
}
139+
}
140+
141+
@Override
142+
public boolean isValid(CruiseConfig preprocessedConfig) {
143+
agents = preprocessedConfig.agents();
144+
AgentConfigsUpdateValidator validator = new AgentConfigsUpdateValidator(uuids);
145+
return validator.isValid(preprocessedConfig);
146+
}
147+
148+
@Override
149+
public void clearErrors() {
150+
BasicCruiseConfig.clearErrors(agents);
151+
}
152+
153+
@Override
154+
public Agents getPreprocessedEntityConfig() {
155+
return agents;
156+
}
157+
}

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

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -18,31 +18,34 @@
1818

1919
import com.google.caja.util.Sets;
2020
import com.thoughtworks.go.config.*;
21+
import com.thoughtworks.go.config.commands.EntityConfigUpdateCommand;
2122
import com.thoughtworks.go.config.exceptions.GoConfigInvalidException;
23+
import com.thoughtworks.go.config.update.AgentsEntityConfigUpdateCommand;
2224
import com.thoughtworks.go.config.update.AgentsUpdateCommand;
2325
import com.thoughtworks.go.config.update.ModifyEnvironmentCommand;
2426
import com.thoughtworks.go.domain.AgentInstance;
2527
import com.thoughtworks.go.domain.ConfigErrors;
28+
import com.thoughtworks.go.i18n.LocalizedMessage;
2629
import com.thoughtworks.go.listener.AgentChangeListener;
2730
import com.thoughtworks.go.presentation.TriStateSelection;
2831
import com.thoughtworks.go.server.domain.AgentInstances;
2932
import com.thoughtworks.go.server.domain.Username;
3033
import com.thoughtworks.go.server.service.result.HttpOperationResult;
34+
import com.thoughtworks.go.server.service.result.LocalizedOperationResult;
3135
import com.thoughtworks.go.serverhealth.HealthStateScope;
3236
import com.thoughtworks.go.serverhealth.HealthStateType;
3337
import com.thoughtworks.go.util.TriState;
3438
import com.thoughtworks.go.validation.AgentConfigsUpdateValidator;
3539
import com.thoughtworks.go.validation.ConfigUpdateValidator;
3640
import com.thoughtworks.go.validation.DoNothingValidator;
41+
import org.apache.commons.lang.StringUtils;
3742
import org.slf4j.Logger;
3843
import org.slf4j.LoggerFactory;
3944
import org.springframework.beans.factory.annotation.Autowired;
4045
import org.springframework.stereotype.Service;
4146

42-
import java.util.ArrayList;
43-
import java.util.HashSet;
47+
import java.util.*;
4448
import java.util.List;
45-
import java.util.Set;
4649

4750
import static com.thoughtworks.go.util.ExceptionUtils.bomb;
4851
import static com.thoughtworks.go.util.ExceptionUtils.bombIfNull;
@@ -150,6 +153,7 @@ private void updateAgentWithoutValidations(UpdateConfigCommand command, Username
150153
updateAgents(command, new DoNothingValidator(), currentUser);
151154
}
152155

156+
153157
/**
154158
* @understands how to delete agent
155159
*/
@@ -228,6 +232,20 @@ public ConfigModifyingUser user() {
228232
}
229233
}
230234

235+
public void bulkUpdateAgentAttributes(final Username username, final LocalizedOperationResult result, final List<String> uuids, final List<String> resourcesToAdd, final List<String> resourcesToRemove, final List<String> environmentsToAdd, final List<String> environmentsToRemove, final TriState enable) {
236+
EntityConfigUpdateCommand<Agents> agentsEntityConfigUpdateCommand = new AgentsEntityConfigUpdateCommand(username, result, uuids, environmentsToAdd, environmentsToRemove, enable, resourcesToAdd, resourcesToRemove, goConfigService);
237+
238+
try {
239+
goConfigService.updateConfig(agentsEntityConfigUpdateCommand, username);
240+
if(result.isSuccessful()){
241+
result.setMessage(LocalizedMessage.string("BULK_AGENT_UPDATE_SUCESSFUL", StringUtils.join(uuids, ", ")));
242+
}
243+
} catch (Exception e) {
244+
if (e.getMessage() == null) {
245+
result.internalServerError(LocalizedMessage.string("INTERNAL_SERVER_ERROR"));
246+
}
247+
}
248+
}
231249

232250
public AgentConfig updateAgentAttributes(final String uuid, Username username, String hostname, String resources, String environments, TriState enable, AgentInstances agentInstances, HttpOperationResult result) {
233251
final GoConfigDao.CompositeConfigCommand command = new GoConfigDao.CompositeConfigCommand();

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -29,6 +29,7 @@
2929
import com.thoughtworks.go.server.domain.Username;
3030
import com.thoughtworks.go.server.persistence.AgentDao;
3131
import com.thoughtworks.go.server.service.result.HttpOperationResult;
32+
import com.thoughtworks.go.server.service.result.LocalizedOperationResult;
3233
import com.thoughtworks.go.server.service.result.OperationResult;
3334
import com.thoughtworks.go.server.ui.AgentViewModel;
3435
import com.thoughtworks.go.server.ui.AgentsViewModel;
@@ -179,6 +180,10 @@ public AgentInstance updateAgentAttributes(Username username, HttpOperationResul
179180
return null;
180181
}
181182

183+
public void bulkUpdateAgentAttributes(Username username, LocalizedOperationResult result, List<String> uuids, List<String> resourcesToAdd, List<String> resourcesToRemove, List<String> environmentsToAdd, List<String> environmentsToRemove, TriState enable) {
184+
agentConfigService.bulkUpdateAgentAttributes(username, result, uuids, resourcesToAdd, resourcesToRemove, environmentsToAdd, environmentsToRemove, enable);
185+
}
186+
182187
public void enableAgents(Username username, OperationResult operationResult, List<String> uuids) {
183188
if (!hasOperatePermission(username, operationResult)) {
184189
return;
@@ -227,12 +232,8 @@ public void deleteAgents(Username username, HttpOperationResult operationResult,
227232
return;
228233
}
229234
List<AgentInstance> agents = new ArrayList<>();
230-
for (String uuid : uuids) {
231-
AgentInstance agentInstance = findAgent(uuid);
232-
if (isUnknownAgent(agentInstance, operationResult)) {
233-
return;
234-
}
235-
agents.add(agentInstance);
235+
if (!populateAgentInstancesForUUIDs(operationResult, uuids, agents)){
236+
return;
236237
}
237238

238239
List<AgentInstance> failedToDeleteAgents = new ArrayList<>();

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,10 @@ public ArrayList<SCM> getSCMs() {
976976
return cruiseConfig().getSCMs();
977977
}
978978

979+
public boolean isAdministrator(CaseInsensitiveString username) {
980+
return isAdministrator(username.toString());
981+
}
982+
979983
public abstract class XmlPartialSaver<T> {
980984
protected final SAXReader reader;
981985
private final ConfigElementImplementationRegistry registry;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ public List<PipelineConfigs> viewableOrOperatableGroupsFor(Username username) {
151151
return list;
152152
}
153153

154-
public void createPipelineConfig(final Username currentUser, final PipelineConfig pipelineConfig, final LocalizedOperationResult result, final String groupName) {
154+
public void createPipelineConfig(final Username currentUser, final PipelineConfig pipelineConfig, final LocalizedOperationResult result, final String groupName) {
155155
validatePluggableTasks(pipelineConfig);
156156
CreatePipelineConfigCommand createPipelineConfigCommand = new CreatePipelineConfigCommand(goConfigService, pipelineConfig, currentUser, result, groupName);
157157
update(currentUser, pipelineConfig, result, createPipelineConfigCommand);

0 commit comments

Comments
 (0)