Config Repo Preflight API endpoint#5579
Conversation
b673854 to
d00907d
Compare
cebf61e to
022613d
Compare
022613d to
dd1822c
Compare
|
Hi @kierarad @marques-work , I've read the PR, I have only one suggestion. Don't you think API should accept
Also it would be nice to add a test which sort of showcases the error location in the returned message. |
|
I agree with all of that. I think we should definitely send the whole directory, since there can be dependencies between files. |
111d874 to
ee7ff88
Compare
|
@tomzo @arvindsv Multipart upload should work now; just need to update the plugin PRs with the contract change. Here's what the request looks like: |
|
I think this is OK now, but a few commits could be squashed and rebased before merge. |
|
@tomzo will rebase/squash frivolous commits :) |
arvindsv
left a comment
There was a problem hiding this comment.
I just took a quick look and had some comments. Don't consider this a review. :)
|
|
||
| public abstract class ApiController implements ControllerMethods, SparkController { | ||
| private static final Set<String> UPDATE_HTTP_METHODS = new HashSet<>(Arrays.asList("PUT", "POST", "PATCH")); | ||
| private static final String MULTIPART_REQ_ATTR = "org.eclipse.jetty.multipartConfig"; |
There was a problem hiding this comment.
Can we push this to somewhere closer to Jetty? My worry is, if a Jetty upgrade changes this property, we won't know to change it here.
There was a problem hiding this comment.
I will move it into GoServer, as I don't want to add any more module dependencies (mostly because I'm unsure of consequences). This might be close enough to the jetty9 module?
There was a problem hiding this comment.
Better yet, I found the constant in org.eclipse.jetty.server.Request.__MULTIPART_CONFIG_ELEMENT and extracted this into the server module as RequestUtils.configureMultipart().
| } | ||
| } | ||
|
|
||
| protected void setMultpipartUpload(Request req, Response res) { |
| } | ||
|
|
||
| protected void setMultpipartUpload(Request req, Response res) { | ||
| req.raw().setAttribute(MULTIPART_REQ_ATTR, new MultipartConfigElement(System.getProperty("java.io.tmpdir", "tmp"))); |
There was a problem hiding this comment.
Does this mean that, once the request is served, this directory will not be cleaned up?
There was a problem hiding this comment.
@arvindsv I had the same question in my head, but I empirically found (at least in my basic trials) that nothing was being written to java.io.tmpdir after a multipart request.
FWIW, there's a method on MultiPartInputStreamParser that can explicitly delete the parts from disk, but we don't have a handle on that object. There's also a flag for "delete on exit" but same lack of handle problem. So...shrug?
There was a problem hiding this comment.
Maybe it uses it only if the payload is bigger than its buffer?
There was a problem hiding this comment.
It’s certainly possible— I’ll try with a larger payload (100mb?). If I see a file not being cleaned, I’ll have an explicit cleanup at the end.
| import static org.apache.commons.io.IOUtils.toInputStream; | ||
|
|
||
| public class MagicalGoConfigXmlLoader { | ||
| private static final Logger LOGGER = LoggerFactory.getLogger(MagicalGoConfigXmlLoader.class); |
There was a problem hiding this comment.
Most (all?) of the changes in this file are moving stuff around, right?
There was a problem hiding this comment.
Most. Just these minor changes largely due to changing the constructor signature of certain exceptions:
diff --git a/config/config-server/src/main/java/com/thoughtworks/go/config/MagicalGoConfigXmlLoader.java b/config/config-server/src/main/java/com/thoughtworks/go/config/MagicalGoConfigXmlLoader.java
index cbdb1dc2b..d28cec426 100644
--- a/config/config-server/src/main/java/com/thoughtworks/go/config/MagicalGoConfigXmlLoader.java
+++ b/config/config-server/src/main/java/com/thoughtworks/go/config/MagicalGoConfigXmlLoader.java
@@ -111,11 +111,11 @@ public class MagicalGoConfigXmlLoader {
}
public CruiseConfig preprocessAndValidate(CruiseConfig config) throws Exception {
- LOGGER.debug("[Config Save] In preprocessAndValidate: Cloning.");
+ LOGGER.debug("[Config Validation] In preprocessAndValidate: Cloning.");
CruiseConfig cloned = CLONER.deepClone(config);
- LOGGER.debug("[Config Save] In preprocessAndValidate: Validating.");
+ LOGGER.debug("[Config Validation] In preprocessAndValidate: Validating.");
validateCruiseConfig(cloned);
- LOGGER.debug("[Config Save] In preprocessAndValidate: Done.");
+ LOGGER.debug("[Config Validation] In preprocessAndValidate: Done.");
config.encryptSecureProperties(cloned);
return cloned;
}
@@ -133,14 +133,14 @@ public class MagicalGoConfigXmlLoader {
}
}
- private CruiseConfig validateCruiseConfig(CruiseConfig config) throws Exception {
+ public CruiseConfig validateCruiseConfig(CruiseConfig config) throws Exception {
LOGGER.debug("[Config Save] In validateCruiseConfig: Starting.");
List<ConfigErrors> allErrors = validate(config);
if (!allErrors.isEmpty()) {
if (config.isLocal())
- throw new GoConfigInvalidException(config, allErrors.get(0).asString());
+ throw new GoConfigInvalidException(config, allErrors);
else
- throw new GoConfigInvalidMergeException("Merged validation failed", config, config.getMergedPartials(), allErrors);
+ throw new GoConfigInvalidMergeException(config, config.getMergedPartials(), allErrors);
}
LOGGER.debug("[Config Save] In validateCruiseConfig: Running validate.");
| @Override | ||
| public CruiseConfig update(CruiseConfig cruiseConfig) { | ||
| if (partial != null && fingerprint != null) { | ||
| cruiseConfig.getPartials().remove(resolver.findPartialByFingerprint(cruiseConfig, fingerprint)); |
There was a problem hiding this comment.
findPartialByFingerprint can be null. Is that going to be a problem? In some race condition maybe?
There was a problem hiding this comment.
Not sure - this was a refactor (move method, rename) so this was likely a problem before. I can add a null check just in case anyway.
| return cruiseConfig().getArtifactStores(); | ||
| } | ||
|
|
||
| public CruiseConfig validateCruiseConfig(CruiseConfig cruiseConfig) throws Exception { |
There was a problem hiding this comment.
Except for this method, just moves in this file?
`parse-content` will transform arbitrary string content into the CRPipeline serialized JSON equivalent.
… into a CruiseConfig To achieve this, we also extracted the inner instance class GoPartialUpdateCommand into a top-level class, PartialConfigUpdateCommand. This required abstracting some behavior behind a new interface, PartialConfigResolver and the implementation was moved to CachedGoPartials as it seemed more appropriate given the fact that all of the logic only concerned the partial caches.
…to perform validations This required some significant refactoring to GoConfigInvalidException and GoConfigInvalidMergeException so that we can access the errors as a list from the exceptions directly. This will help build the preflight result that will be returned by the API, as it is generally more useful to retain a list of errors rather than a concatenated error message. The `summary` string was also dropped from the exception constructors as the value wasn't being used(!) in code anyway. MagicalGoConfigXmlLoader now exposes `validateCruiseConfig()`, which is delegated to by GoConfigService.
…trollerV1 as a public API
- POST endpoint accepts arbitrary file content that will parse and validate syntax, structure,
and dependencies against a config-repo plugin
- Requires user to provide a config-repo plugin ID
- Optionally accepts a config-repo ID; preflighting changes to existing config-repos should
always provide this, or else validations will likely fail on duplicate pipeline names
…ept custom messages
…ltiple file uploads
7f14a48 to
2cff944
Compare
- Fix type in method name - Null-check partial config removal - Refactor multipart configuration to use Jetty constant
- Fix type in method name - Null-check partial config removal - Refactor multipart configuration to use Jetty constant
795ab2e to
ef4bd09
Compare
This adds an endpoint for parsing arbitrary pipeline/environment definition content with a specified config-repo plugin and validating structure, collisions, and dependencies -- basically the full set of validations on a
CruiseConfig.This preflight endpoint lives under the config-repo operations API, which has been repurposed as a public API.
For reviewers, there is a lot of refactoring and IDEA reformatting/optimization. As such, it might be easier to understand if one review the individual commits to see the intended change and progression of logic, rather than looking over the
Files changedtab.Notable changes
parse-contentmessage to the config repo contract. Will need to update the documentation.Part of #5489