-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a validate only flag process to facilitate descriptor testing in …
…feature branches and add ConfigurationKeyValidation and TopicNameRegexValidation validations (#274) * add a validate option for the CLI, this would allow only to run validations in the topology. This would be without access to the cluster, if validations are required with the connectivity they would need to use the dry run parameter * add validation for config keys in topics * Add a TopicNameRegexValidation to validate topic names * simplify the code by making the validation a topic one and add test for this module * make the config value a constant for the topic name regexp * docs update Co-authored-by: Leonardo Bonacci <aabcehmu@mailfence.com> remove usage of var
- Loading branch information
Showing
14 changed files
with
284 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
Validate your topologies | ||
******************************* | ||
|
||
A normal practise in many *gitops* deployments is to run a set of automated validations before allowing the changes in. | ||
JulieOps allows the users to run a variable set of validations before the project will apply the changes into each of the managed components. | ||
|
||
Validate a topology in a feature branch | ||
----------- | ||
|
||
As a user you can use the *--validate* CLI option to only validate the incoming topology. Note this would run a validation completely offline, | ||
without any knowledge of the current state in the cluster. | ||
|
||
To configure which validations you require for your topology the reader would need to do it in the configuration file, this can be done like this: | ||
|
||
.. code-block:: bash | ||
topology.validations.0=com.purbon.kafka.topology.validation.topic.ConfigurationKeyValidation | ||
topology.validations.1=com.purbon.kafka.topology.validation.topic.TopicNameRegexValidation | ||
topology.validations.topic.name.regexp="[a-z0-9]" | ||
In the previous example we have configured two validations. | ||
|
||
1.- ConfigurationKeyValidation will make sure all config keys are valid for Kafka. | ||
2.- Will validate, based on the configured regexp that all topic names follow the right pattern. | ||
|
||
All detected errors will be reported in a single outcome like this: | ||
|
||
.. code-block:: bash | ||
... | ||
Exception in thread "main" com.purbon.kafka.topology.exceptions.ValidationException: Topology name does not follow the camelCase format: context | ||
Topic context.company.env.source.projectA.foo has an invalid number of partitions: 1 | ||
Topic context.company.env.source.projectA.bar.avro has an invalid number of partitions: 1 | ||
Topic context.company.env.source.projectB.bar.avro has an invalid number of partitions: 1 | ||
Topic context.company.env.source.projectC.topicE has an invalid number of partitions: 1 | ||
Topic context.company.env.source.projectC.topicF has an invalid number of partitions: 1 | ||
at com.purbon.kafka.topology.JulieOps.build(JulieOps.java:125) | ||
at com.purbon.kafka.topology.JulieOps.build(JulieOps.java:75) | ||
at com.purbon.kafka.topology.CommandLineInterface.processTopology(CommandLineInterface.java:206) | ||
at com.purbon.kafka.topology.CommandLineInterface.run(CommandLineInterface.java:156) | ||
at com.purbon.kafka.topology.CommandLineInterface.main(CommandLineInterface.java:146) | ||
Add your own validations | ||
----------- | ||
|
||
JulieOps provides you with a set of integrated validations, however you as user can provide your own. To do so you will need to: | ||
|
||
* Code your validation following the required interfaces as defined in the JulieOps project. See core validations to see the current pattern. | ||
* Build a jar with your validations. | ||
* Run JulieOps with a configured CLASSPATH where the JVM can find access to your validations jar in order to dynamically load them. | ||
Remember when running JulieOps you can use the _JULIE_OPS_OPTIONS_ env variable to pass custom system configurations such as CLASSPATH or related to security. | ||
|
||
**NOTE**: The UberJar is for now only available from the release page, in future releases we will facilitate a smaller plugin jar. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
src/main/java/com/purbon/kafka/topology/validation/topic/ConfigurationKeyValidation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.purbon.kafka.topology.validation.topic; | ||
|
||
import com.purbon.kafka.topology.exceptions.ValidationException; | ||
import com.purbon.kafka.topology.model.Impl.TopicImpl; | ||
import com.purbon.kafka.topology.model.Topic; | ||
import com.purbon.kafka.topology.validation.TopicValidation; | ||
import java.lang.reflect.Field; | ||
import java.util.Arrays; | ||
import java.util.Map; | ||
import org.apache.kafka.common.config.TopicConfig; | ||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
|
||
/** | ||
* This validation checks that all topic configs are valid ones according to the TopicConfig class. | ||
*/ | ||
public class ConfigurationKeyValidation implements TopicValidation { | ||
|
||
private static final Logger LOGGER = LogManager.getLogger(ConfigurationKeyValidation.class); | ||
|
||
@Override | ||
public void valid(Topic topic) throws ValidationException { | ||
Field[] fields = TopicConfig.class.getDeclaredFields(); | ||
TopicConfig config = new TopicConfig(); | ||
Map<String, String> topicConfig = getTopicConfig(topic); | ||
for (Map.Entry<String, String> entry : topicConfig.entrySet()) { | ||
boolean match = | ||
Arrays.stream(fields) | ||
.anyMatch( | ||
field -> { | ||
try { | ||
return ((String) field.get(config)).contains(entry.getKey()); | ||
} catch (IllegalAccessException e) { | ||
LOGGER.error(e); | ||
return false; | ||
} | ||
}); | ||
if (!match) { | ||
String msg = | ||
String.format("Topic %s has an invalid configuration value: %s", topic, entry.getKey()); | ||
throw new ValidationException(msg); | ||
} | ||
} | ||
} | ||
|
||
private Map<String, String> getTopicConfig(Topic topic) { | ||
Topic clonedTopic = ((TopicImpl) topic).clone(); | ||
return clonedTopic.getRawConfig(); | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
src/main/java/com/purbon/kafka/topology/validation/topic/TopicNameRegexValidation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package com.purbon.kafka.topology.validation.topic; | ||
|
||
import static com.purbon.kafka.topology.Constants.TOPOLOGY_VALIDATIONS_TOPIC_NAME_REGEXP; | ||
|
||
import com.purbon.kafka.topology.exceptions.ConfigurationException; | ||
import com.purbon.kafka.topology.exceptions.ValidationException; | ||
import com.purbon.kafka.topology.model.Topic; | ||
import com.purbon.kafka.topology.validation.TopicValidation; | ||
import com.typesafe.config.Config; | ||
import com.typesafe.config.ConfigException; | ||
import com.typesafe.config.ConfigFactory; | ||
import java.util.regex.Pattern; | ||
import java.util.regex.PatternSyntaxException; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
|
||
public class TopicNameRegexValidation implements TopicValidation { | ||
|
||
private static final Logger LOGGER = LogManager.getLogger(TopicNameRegexValidation.class); | ||
|
||
private String topicNamePattern; | ||
|
||
public TopicNameRegexValidation() throws ConfigurationException { | ||
this(getTopicNamePatternFromConfig()); | ||
} | ||
|
||
public TopicNameRegexValidation(String pattern) throws ConfigurationException { | ||
validateRegexpPattern(pattern); | ||
|
||
this.topicNamePattern = pattern; | ||
} | ||
|
||
@Override | ||
public void valid(Topic topic) throws ValidationException { | ||
LOGGER.trace(String.format("Applying Topic Name Regex Validation [%s]", topicNamePattern)); | ||
|
||
if (!topic.getName().matches(topicNamePattern)) { | ||
String msg = | ||
String.format("Topic name '%s' does not follow regex: %s", topic, topicNamePattern); | ||
throw new ValidationException(msg); | ||
} | ||
} | ||
|
||
private static String getTopicNamePatternFromConfig() throws ConfigurationException { | ||
Config config = ConfigFactory.load(); | ||
try { | ||
return config.getString(TOPOLOGY_VALIDATIONS_TOPIC_NAME_REGEXP); | ||
} catch (ConfigException e) { | ||
String msg = | ||
String.format( | ||
"TopicNameRegexValidation requires you to define your regex in config '%s'", | ||
TOPOLOGY_VALIDATIONS_TOPIC_NAME_REGEXP); | ||
throw new ConfigurationException(msg); | ||
} | ||
} | ||
|
||
private void validateRegexpPattern(String pattern) throws ConfigurationException { | ||
if (StringUtils.isBlank(pattern)) { | ||
throw new ConfigurationException( | ||
"TopicNameRegexValidation is configured without specifying a topic name pattern. Use config 'topology.validations.regexp'"); | ||
} | ||
|
||
try { | ||
Pattern.compile(pattern); | ||
} catch (PatternSyntaxException exception) { | ||
throw new ConfigurationException( | ||
String.format("TopicNameRegexValidation configured with unvalid regex '%s'", pattern)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
src/test/java/com/purbon/kafka/topology/validation/topic/ConfigurationKeyValidationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package com.purbon.kafka.topology.validation.topic; | ||
|
||
import com.purbon.kafka.topology.exceptions.ValidationException; | ||
import com.purbon.kafka.topology.model.Impl.TopicImpl; | ||
import com.purbon.kafka.topology.model.Topic; | ||
import java.util.HashMap; | ||
import org.apache.kafka.common.config.TopicConfig; | ||
import org.junit.Test; | ||
|
||
public class ConfigurationKeyValidationTest { | ||
|
||
@Test(expected = ValidationException.class) | ||
public void testKoConfigValues() throws ValidationException { | ||
HashMap<String, String> config = new HashMap<String, String>(); | ||
config.put("foo", "2"); | ||
config.put(TopicConfig.COMPRESSION_TYPE_CONFIG, "gzip"); | ||
Topic topic = new TopicImpl("topic", config); | ||
|
||
ConfigurationKeyValidation validation = new ConfigurationKeyValidation(); | ||
validation.valid(topic); | ||
} | ||
|
||
@Test | ||
public void testOkConfigValues() throws ValidationException { | ||
HashMap<String, String> config = new HashMap<String, String>(); | ||
config.put(TopicConfig.COMPRESSION_TYPE_CONFIG, "gzip"); | ||
Topic topic = new TopicImpl("topic", config); | ||
|
||
ConfigurationKeyValidation validation = new ConfigurationKeyValidation(); | ||
validation.valid(topic); | ||
} | ||
|
||
@Test | ||
public void testPartitionsAndReplicationConfigValues() throws ValidationException { | ||
HashMap<String, String> config = new HashMap<String, String>(); | ||
config.put("replication.factor", "3"); | ||
Topic topic = new TopicImpl("topic", config); | ||
|
||
ConfigurationKeyValidation validation = new ConfigurationKeyValidation(); | ||
validation.valid(topic); | ||
} | ||
} |
31 changes: 31 additions & 0 deletions
31
src/test/java/com/purbon/kafka/topology/validation/topic/TopicNameRegexValidationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.purbon.kafka.topology.validation.topic; | ||
|
||
import com.purbon.kafka.topology.exceptions.ConfigurationException; | ||
import com.purbon.kafka.topology.exceptions.ValidationException; | ||
import com.purbon.kafka.topology.model.Impl.TopicImpl; | ||
import com.purbon.kafka.topology.model.Topic; | ||
import org.junit.Test; | ||
|
||
public class TopicNameRegexValidationTest { | ||
|
||
@Test(expected = ValidationException.class) | ||
public void testKoConfigValues() throws ValidationException, ConfigurationException { | ||
Topic topic = new TopicImpl("topic"); | ||
TopicNameRegexValidation validation = new TopicNameRegexValidation("[1-9]"); | ||
validation.valid(topic); | ||
} | ||
|
||
@Test | ||
public void testOkConfigValues() throws ValidationException, ConfigurationException { | ||
Topic topic = new TopicImpl("topic"); | ||
TopicNameRegexValidation validation = new TopicNameRegexValidation("[a-z]*"); | ||
validation.valid(topic); | ||
} | ||
|
||
@Test(expected = ConfigurationException.class) | ||
public void testEmptyParam() throws ValidationException, ConfigurationException { | ||
Topic topic = new TopicImpl("topic"); | ||
TopicNameRegexValidation validation = new TopicNameRegexValidation(""); | ||
validation.valid(topic); | ||
} | ||
} |