METRON-1336 Patching Can Result in Bad Configuration#851
METRON-1336 Patching Can Result in Bad Configuration#851nickwallen wants to merge 9 commits intoapache:masterfrom
Conversation
|
|
||
| private static String getConfigZKPath(ConfigurationType configType, Optional<String> configName) { | ||
| String pathSuffix = configName.isPresent() && configType != GLOBAL ? "/" + configName : ""; | ||
| String pathSuffix = configName.isPresent() && configType != GLOBAL ? "/" + configName.get() : ""; |
There was a problem hiding this comment.
how did this ever work before? I'm surprised we didn't get the wrong result because configName is optional.
There was a problem hiding this comment.
Yeah, this confused me too. There are a lot of paths through ConfigurationUtils. Some paths worked, others didn't.
There was a problem hiding this comment.
We need to refactor ConfigurationUtils so badly that it hurts.
There was a problem hiding this comment.
I think that's because we handle global config in a special way. This whole class needs refactored, and I'd have done more with the original patch PR had it not blown up scope.
There was a problem hiding this comment.
Yeah, this is not one of those things that can be done as a side-effect of another PR. I considered refactoring it as part of the zookeeper refactoring that I did earlier. It needs more direct attention and refactoring.
| writeConfigToZookeeper(type, configName, sensorIndexingConfigs.get(sensorType), client); | ||
|
|
||
| case PARSER: { | ||
| Map<String, byte[]> configs = readSensorConfigsFromFile(rootFilePath, PARSER, configName); |
There was a problem hiding this comment.
These cases look like they are cut and pasted which seems like code smell to me and might be a maintenance issue. Can we extract the common code for Parser, Enrichment, and Indexing into a separate function that is called here? Perhaps something like:
void writeSensorConfigs(ConfigurationType type, Optional<String> configName, BiFunction<String, byte[], Void> callback) {
Map<String, byte[]> configs = readSensorConfigsFromFile(rootFilePath, type, configName);
for(String sensorType : configs.keySet()) {
byte[] configData = configs.get(sensorType);
callback.apply(sensorType, configData);
}
}
which could be called from this case via writeSensorConfigs(PARSER, configName, (sensorType, configData) -> writeSensorParserConfigToZookeeper(sensorType, configData, client));
Take the above as a very rough suggestion and you can feel to abstract it however you wish.
There was a problem hiding this comment.
An other option here is to add a new method called writeSensorConfigToZookeeper(ConfigurationType type, String sensorType, byte[] configData, CuratorFramework client) that calls the appropriate writeSensorXConfigToZookeeper call.
There was a problem hiding this comment.
Oh, an even better option (IMO) would be to add a writeSensorConfigToZookeeper(String sensorType, byte[] configData, CuratorFramework client) method to ConfigurationType.
That would make this:
case PARSER:
case ENRICHMENT:
case INDEXING:
Map<String, byte[]> sensorIndexingConfigs = readSensorConfigsFromFile(rootFilePath, type,configName);
for (String sensorType : configs.keySet()) {
byte[] configData = configs.get(sensorType);
type.writeSensorConfigToZookeeper(sensorType, configData, client);
}
Anyway, I'll stop ;)
There was a problem hiding this comment.
Yes, I had the same thought and tried for a bit to refactor it. I landed on this because the various other ways to do this either (1) seemed more complex and less obvious as to what we are actually doing here or (2) lead down a path of heavy refactoring of ConfigurationUtils. Both of which i wanted to avoid
There was a problem hiding this comment.
Your first suggestion with the callback just seems less obvious and clear to me (IMHO).
I'll try and think through your 2nd and 3rd suggestions, but (at least right now) I'm not seeing something that doesn't add more complexity to ConfigurationUtils.
But clearly I could be totally wrong here. Let me think on it.
There was a problem hiding this comment.
@cestella I submitted a PR against your PR on this PR - cestella#8
There was a problem hiding this comment.
@mmiklavc I'm ok with that modification if we decide to go this route.
There was a problem hiding this comment.
I like the refactoring, guys. It's looking food. Can't believe we have a PR of a PR of this PR. One more level and I think we hit Inception's limbo.
I'd be open to including those changes here or even just creating a separate PR for the refactoring work as a follow-on to this. That refactoring adds a good bit of code in itself. Maybe we could even tackle some further ConfigurationUtils clean-up in that same PR.
But If we want @cestella and @mmiklavc 's refactoring to go in with this PR, then we need @cestella to do the first "kick" and merge @mmiklavc's PR.
There was a problem hiding this comment.
Haha, I know. I couldn't resist seeing if GitHub actually allowed this sort of thing, and well, it was actually pretty straightforward. I don't care if we pull it all together or submit them separately. It sounds like we're all in agreement that the refactoring is a good idea, so if it was split them Casey's interface refactoring would go in immediately following with the necessary votes. I'll leave it to you both to decide.
Refactor ConfigutionOperation implementations to their own classes
Proposed abstraction for writeSensorToZookeeper
ottobackwards
left a comment
There was a problem hiding this comment.
A couple of notes now that all the hard stuff is done.
| public static void uploadConfigsToZookeeper(String rootFilePath, CuratorFramework client, | ||
| ConfigurationType type, Optional<String> configName) throws Exception { | ||
| public static void uploadConfigsToZookeeper( | ||
| String rootFilePath, |
There was a problem hiding this comment.
When reading from file, the API should explicitly require would be more clear if it used file Paths as opposed to just strings.
It makes the api methods easier to understand when there are a bunch of similar methods.
There was a problem hiding this comment.
Sure, but this method signature already existed. Do I need to fix that on this PR?
There was a problem hiding this comment.
No. I guess I miss read the ambition of this pr, given the meta-pr and all ;)
There was a problem hiding this comment.
Yeah I get it. ;). It wasn't my preference to include all of that here, but I bend to make people happy.
There was a problem hiding this comment.
I am familiar with that ;)
| JSONUtils.INSTANCE.toJSONPretty(patchedConfig), client); | ||
| byte[] prettyPatchedConfig = JSONUtils.INSTANCE.toJSONPretty(patchedConfig); | ||
|
|
||
| // ensure the patch produces a valid result; otherwise exception thrown during deserialization |
There was a problem hiding this comment.
Can we reword this? It needs to be clear that some things cannot be deserialized if they have an invalid configuration.
There was a problem hiding this comment.
I'm not sure exactly what you're looking for. Can you suggest some specific text?
There was a problem hiding this comment.
Never mind Nick, I didn't read this correctly, and I have the "serialization mixed with logical validation" stuff upfront in my head because of my work over in #856.
Sorry
There was a problem hiding this comment.
Heh, well our meta-PR was to address a code change that resulted in duplication, not just a style preference. ;)
|
Bump. Anything else we need on this one? |
|
I'm comfortable with this. +1 by inspection |
|
+1 by inspection. Nice work Nick. |
|
Thanks guys. Nice work on the meta-PRs. I like how that turned out. |
The following problems are addressed in this PR.
A patch can be constructed that when applied creates an invalid configuration. The invalid configuration is not discovered until attempting to "pull" it back out of Zookeeper. In all cases, the result of applying a patch needs to be validated before pushing it to Zookeeper.
The Profiler configuration can only be pushed when pushing all configurations at once. Attempting to push just the Profiler configuration alone, fails silently.
I do not believe that validation was occurring in all cases; in particular when pushing a specific configuration type (like pushing "Squid", PARSING alone.) Validation was occurring in other cases, like when all configuration elements are pushed at once.
Added unit tests to validate that a 'bad' patch cannot be applied.
Added a good number of unit tests to ensure invalid configurations cannot be pushed under all circumstances (parser, enrichment, indexing, profiler, "push all").
Added a test to ensure that the Profiler configuration can be pushed independently.
Manual Testing
Launch Full Dev.
Environment definitions that will be used later.
Dump all existing configurations.
Dump just the Profiler configuration. There should be none.
Create a Profiler configuration on disk.
Push the Profiler configuration.
Dump just the Profiler configuration. It should be defined now.
Create a patch for the Profiler configuration.
Apply the patch.
Dump the Profiler configuration and ensure that the name of the Profile was changed.
Create a patch that would make the Profiler configuration invalid.
Apply the bad patch. The script should terminate with an error.
Pull Request Checklist