Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEAT: AWS secret extension #3340

Merged
merged 59 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
9b59ec1
ADD: initial implementation on injecting extension config
chenqi0805 May 31, 2023
43a52d7
ENH: context change
chenqi0805 Jun 2, 2023
fb9af26
TST: infra
chenqi0805 Jun 2, 2023
f906fce
Merge branch 'main' into enh/allow-config-in-extension
chenqi0805 Jun 5, 2023
a3b4c24
REF: parsing extension from pipeline config
chenqi0805 Jun 9, 2023
2049066
MAINT: remove test code
chenqi0805 Jun 9, 2023
250ae6f
MAINT: fix test cases
chenqi0805 Jun 9, 2023
b4f73d1
ADD: initial boilerplate
chenqi0805 Jun 14, 2023
a05352e
TST: test cases
chenqi0805 Jul 3, 2023
e143fbb
Merge branch 'main' into enh/allow-config-in-extension
chenqi0805 Aug 21, 2023
79f20cf
MAINT: handle null value on config in the ExtensionPlugin
chenqi0805 Aug 22, 2023
50f8e0d
FIX: null validation
chenqi0805 Aug 22, 2023
c56001b
Merge branch 'enh/allow-config-in-extension' into add/aws-secret-exte…
chenqi0805 Aug 22, 2023
12e9d32
ADD: config value translater
chenqi0805 Aug 23, 2023
0f6fbf2
MAINT: test coverage
chenqi0805 Aug 23, 2023
bd2c0a6
REF: pipeline_extensions in data-prepper-config
chenqi0805 Aug 24, 2023
1e2bdc9
Merge branch 'enh/allow-config-in-extension' into add/aws-secret-exte…
chenqi0805 Aug 25, 2023
d35cb90
MAINT: checkpoint
chenqi0805 Aug 28, 2023
5b98976
MAINT: checkin missing doc
chenqi0805 Aug 28, 2023
e1dec5b
MAINT: revert changes on refactoring pipeline_extensions
chenqi0805 Aug 28, 2023
51df8e3
MAINT: revert pipeline_extensions into data-prepper-config
chenqi0805 Aug 29, 2023
d545390
REF: pipeline_extensions -> extensions
chenqi0805 Aug 29, 2023
bab025d
MAINT: refactor ObjectMapper
chenqi0805 Aug 29, 2023
d7a057d
MAINT: unused imports
chenqi0805 Aug 29, 2023
1081eb3
Merge branch 'main' into enh/allow-config-in-extension
chenqi0805 Aug 30, 2023
09c2146
REF: merge dataflow model
chenqi0805 Aug 30, 2023
3a84ff3
MAINT: resolve merging conflict
chenqi0805 Aug 30, 2023
6527e07
MAINT: import fix
chenqi0805 Aug 30, 2023
b7c19f3
FIX: do not allow duplicate pipelines
chenqi0805 Aug 30, 2023
7b29f73
MAINT: revert
chenqi0805 Aug 30, 2023
0204d4d
ENH: resolve pipeline extension configurations
chenqi0805 Aug 31, 2023
0c2c6dc
Merge branch 'main' into enh/support-pipeline-extensions-in-pipeline-…
chenqi0805 Aug 31, 2023
c785db6
FIX: test cases
chenqi0805 Aug 31, 2023
9153a21
MAINT: initial change on merging pipelineExtensions
chenqi0805 Aug 31, 2023
88942aa
TST: test cases on parsing pipeline_extensions
chenqi0805 Sep 1, 2023
6955a3b
FIX: no pipeline-extensions
chenqi0805 Sep 1, 2023
1722ad9
MAINT: test coverage
chenqi0805 Sep 1, 2023
535befa
MAINT: merging dependent branch and refactor PluginConfigValueTranslator
chenqi0805 Sep 1, 2023
1f80214
MAINT: adapt dependency injection and variable expansion
chenqi0805 Sep 6, 2023
07a1f99
ADD: VariableExpander and coverage
chenqi0805 Sep 7, 2023
267cc4b
ADD: DataPrepperScalarTypeDeserializer and tests
chenqi0805 Sep 7, 2023
dfc7cc4
ENH: support secret string value
chenqi0805 Sep 13, 2023
81dcda8
MAINT: fix extensionIT
chenqi0805 Sep 13, 2023
9576a4e
MAINT: remove noop translator
chenqi0805 Sep 13, 2023
c574957
FIX: pluginSettings conversion
chenqi0805 Sep 14, 2023
4f932a3
MAINT: rootKey -> jsonPath
chenqi0805 Sep 14, 2023
01e2f94
MAINT: workaround for pluginsettings
chenqi0805 Sep 14, 2023
fbba530
MAINT: fix test extesion
chenqi0805 Sep 14, 2023
ed47582
MAINT: fix ExtensionLoaderTests
chenqi0805 Sep 14, 2023
472c502
MAINT: inject allowInPipelineConfigurations
chenqi0805 Sep 15, 2023
784a8e2
MAINT: merge main and resolve conflict
chenqi0805 Sep 15, 2023
b47911e
MAINT: fix coverage
chenqi0805 Sep 15, 2023
8b99927
MAINT: unnecessary dependency
chenqi0805 Sep 15, 2023
d065938
REF: SecretsSupplier package
chenqi0805 Sep 15, 2023
b642ee5
MAINT: rename name to secret_id
chenqi0805 Sep 15, 2023
af0664b
MAINT: ResourceNotFoundException -> RuntimeException
chenqi0805 Sep 15, 2023
7054ca1
MAINT: remove unused deserializer
chenqi0805 Sep 15, 2023
8f90fe0
MAINT: refactor return type
chenqi0805 Sep 15, 2023
73a265d
MAINT: ref and fix test coverage
chenqi0805 Sep 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,22 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Annotates a Data Prepper extension plugin which includes a configuration model class.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface DataPrepperExtensionPlugin {
/**
* @return extension plugin configuration class.
*/
Class<?> modelType();

String rootKey();
/**
* @return valid JSON path string starts with "/" pointing towards the configuration block.
*/
String rootKeyJsonPath();

boolean allowInPipelineConfigurations() default false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class PluginSetting implements PipelineDescription {
private static final String UNEXPECTED_ATTRIBUTE_TYPE_MSG = "Unexpected type [%s] for attribute [%s]";

private final String name;
private final Map<String, Object> settings;
private Map<String, Object> settings;
private int processWorkers;
private String pipelineName;

Expand All @@ -31,6 +31,10 @@ public Map<String, Object> getSettings() {
return settings;
}

public void setSettings(final Map<String, Object> settings) {
this.settings = settings;
}

/**
* Returns the number of process workers the pipeline is using; This is only required for special plugin use-cases
* where plugin implementation depends on the number of process workers. For example, Trace analytics service map
Expand Down Expand Up @@ -99,7 +103,7 @@ public Object getAttributeOrDefault(final String attribute, final Object default
* @return the value of the specified attribute, or {@code defaultValue} if this settings contains no value for
* the attribute. If the value is null, null will be returned.
*/
public Integer getIntegerOrDefault(final String attribute, final int defaultValue) {
public Integer getIntegerOrDefault(final String attribute, final Integer defaultValue) {
Object object = getAttributeOrDefault(attribute, defaultValue);
if (object == null) {
return null;
Expand Down Expand Up @@ -218,7 +222,7 @@ public <K, V> Map<K, List<V>> getTypedListMap(final String attribute, final Clas
* @return the value of the specified attribute, or {@code defaultValue} if this settings contains no value for
* the attribute
*/
public Boolean getBooleanOrDefault(final String attribute, final boolean defaultValue) {
public Boolean getBooleanOrDefault(final String attribute, final Boolean defaultValue) {
Object object = getAttributeOrDefault(attribute, defaultValue);
if (object == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.opensearch.dataprepper.model.plugin;

public interface PluginConfigValueTranslator {
String translate(final String value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return a generic type as it can vary.


String getPrefix();
}
Original file line number Diff line number Diff line change
Expand Up @@ -537,4 +537,13 @@ public void testGetLongOrDefault_UnsupportedType() {

assertThrows(IllegalArgumentException.class, () -> pluginSetting.getLongOrDefault(TEST_LONG_ATTRIBUTE, TEST_LONG_DEFAULT_VALUE));
}

@Test
public void testSetSettings() {
final PluginSetting pluginSetting = new PluginSetting(TEST_PLUGIN_NAME, null);

final Map<String, Object> settings = Map.of("test", 1);
pluginSetting.setSettings(settings);
assertThat(pluginSetting.getSettings(), equalTo(settings));
}
}
1 change: 1 addition & 0 deletions data-prepper-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies {
implementation project(':data-prepper-expression')
implementation project(':data-prepper-plugins:blocking-buffer')
implementation project(':data-prepper-plugins:common')
implementation project(':data-prepper-plugins:aws-plugin')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Data Prepper core should not depend on aws-plugin. The extensions framework is what will let this load dynamically.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the catch! It is unnecessary

implementation project(':data-prepper-logstash-configuration')
testImplementation project(':data-prepper-plugins:common').sourceSets.test.output
implementation 'com.fasterxml.jackson.core:jackson-databind'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,24 @@
public class DataPrepperExtensionPoints implements ExtensionPoints {
private static final ExtensionProvider.Context EMPTY_CONTEXT = new EmptyContext();
private final GenericApplicationContext sharedApplicationContext;
private final GenericApplicationContext coreApplicationContext;

@Inject
public DataPrepperExtensionPoints(
final PluginBeanFactoryProvider pluginBeanFactoryProvider) {
Objects.requireNonNull(pluginBeanFactoryProvider);
Objects.requireNonNull(pluginBeanFactoryProvider.getCoreApplicationContext());
Objects.requireNonNull(pluginBeanFactoryProvider.getSharedPluginApplicationContext());
this.sharedApplicationContext = pluginBeanFactoryProvider.getSharedPluginApplicationContext();
this.coreApplicationContext = pluginBeanFactoryProvider.getCoreApplicationContext();
}

@Override
public void addExtensionProvider(final ExtensionProvider extensionProvider) {
coreApplicationContext.registerBean(
extensionProvider.supportedClass(),
() -> extensionProvider.provideInstance(EMPTY_CONTEXT).orElse(null),
b -> b.setScope(BeanDefinition.SCOPE_PROTOTYPE));
sharedApplicationContext.registerBean(
extensionProvider.supportedClass(),
() -> extensionProvider.provideInstance(EMPTY_CONTEXT),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.opensearch.dataprepper.plugin;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

import java.io.IOException;

public class DataPrepperScalarTypeDeserializer<T> extends JsonDeserializer<T> {
private final VariableExpander variableExpander;
private final Class<T> scalarType;

public DataPrepperScalarTypeDeserializer(final VariableExpander variableExpander, final Class<T> scalarType) {
this.variableExpander = variableExpander;
this.scalarType = scalarType;
}

@Override
public T deserialize(final JsonParser jsonParser, final DeserializationContext ctxt) throws IOException {
return variableExpander.translate(jsonParser, this.scalarType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.opensearch.dataprepper.plugin;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
import org.opensearch.dataprepper.model.plugin.PluginConfigValueTranslator;

import java.io.IOException;
import java.lang.annotation.Annotation;

public class DataPrepperStringContextualDeserializer extends JsonDeserializer<String> implements ContextualDeserializer {

private final PluginConfigValueTranslator pluginConfigValueTranslator;

public DataPrepperStringContextualDeserializer(final PluginConfigValueTranslator pluginConfigValueTranslator) {
this.pluginConfigValueTranslator = pluginConfigValueTranslator;
}

@Override
public JsonDeserializer<String> createContextual(final DeserializationContext ctxt, final BeanProperty property) {
final Annotation annotation = property.getAnnotation(SupportSecretString.class);
if (annotation != null) {
return this;
} else {
return new StringDeserializer();
}
}

@Override
public String deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException {
return pluginConfigValueTranslator.translate(p.getText());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ private PluginArgumentsContext getConstructionContext(final Class<?> extensionPl
return new NoArgumentsArgumentsContext();
} else {
final Class<?> pluginConfigurationType = pluginAnnotation.modelType();
final String rootKey = pluginAnnotation.rootKey();
final String rootKey = pluginAnnotation.rootKeyJsonPath();
final Object configuration = extensionPluginConfigurationConverter.convert(
pluginAnnotation.allowInPipelineConfigurations(),
pluginConfigurationType, rootKey);
return new SingleConfigArgumentArgumentsContext(configuration);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.opensearch.dataprepper.plugin;

import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
Expand All @@ -8,12 +11,14 @@
import javax.inject.Inject;
import javax.inject.Named;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Named
public class ExtensionPluginConfigurationConverter {
static final TypeReference<Map<String, Object>> MAP_TYPE_REFERENCE = new TypeReference<>() {};
private final ExtensionPluginConfigurationResolver extensionPluginConfigurationResolver;
private final ObjectMapper objectMapper;
private final Validator validator;
Expand All @@ -22,19 +27,25 @@ public class ExtensionPluginConfigurationConverter {
public ExtensionPluginConfigurationConverter(
final ExtensionPluginConfigurationResolver extensionPluginConfigurationResolver,
final Validator validator,
@Named("pluginConfigObjectMapper")
@Named("extensionPluginConfigObjectMapper")
final ObjectMapper objectMapper) {
this.extensionPluginConfigurationResolver = extensionPluginConfigurationResolver;
this.objectMapper = objectMapper;
this.validator = validator;
}

public Object convert(final Class<?> extensionPluginConfigurationType, final String rootKey) {
public Object convert(final boolean configAllowedInPipelineConfigurations,
final Class<?> extensionPluginConfigurationType, final String rootKey) {
Objects.requireNonNull(extensionPluginConfigurationType);
Objects.requireNonNull(rootKey);

final Object configuration = convertSettings(extensionPluginConfigurationType,
extensionPluginConfigurationResolver.getExtensionMap().get(rootKey));
final Object configuration = configAllowedInPipelineConfigurations ?
convertSettings(extensionPluginConfigurationType,
getExtensionPluginConfigMap(
extensionPluginConfigurationResolver.getCombinedExtensionMap(), rootKey)) :
convertSettings(extensionPluginConfigurationType,
getExtensionPluginConfigMap(
extensionPluginConfigurationResolver.getDataPrepperConfigExtensionMap(), rootKey));

final Set<ConstraintViolation<Object>> constraintViolations = configuration == null ? Collections.emptySet() :
validator.validate(configuration);
Expand All @@ -54,4 +65,12 @@ public Object convert(final Class<?> extensionPluginConfigurationType, final Str
private Object convertSettings(final Class<?> extensionPluginConfigurationType, final Object extensionPlugin) {
return objectMapper.convertValue(extensionPlugin, extensionPluginConfigurationType);
}

private Map<String, Object> getExtensionPluginConfigMap(
final Map<String, Object> extensionMap, final String rootKey) {
final JsonNode jsonNode = objectMapper.valueToTree(extensionMap);
final JsonPointer jsonPointer = JsonPointer.compile(rootKey);
final JsonNode extensionPluginConfigNode = jsonNode.at(jsonPointer);
return objectMapper.convertValue(extensionPluginConfigNode, MAP_TYPE_REFERENCE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,26 @@

@Named
public class ExtensionPluginConfigurationResolver {
private final Map<String, Object> extensionMap;
private final Map<String, Object> combinedExtensionMap;

private final Map<String, Object> dataPrepperConfigExtensionMap;

@Inject
public ExtensionPluginConfigurationResolver(final DataPrepperConfiguration dataPrepperConfiguration,
final PipelinesDataFlowModel pipelinesDataFlowModel) {
extensionMap = dataPrepperConfiguration.getPipelineExtensions() == null?
this.dataPrepperConfigExtensionMap = dataPrepperConfiguration.getPipelineExtensions() == null?
new HashMap<>() : new HashMap<>(dataPrepperConfiguration.getPipelineExtensions().getExtensionMap());
combinedExtensionMap = new HashMap<>(dataPrepperConfigExtensionMap);
if (pipelinesDataFlowModel.getPipelineExtensions() != null) {
extensionMap.putAll(pipelinesDataFlowModel.getPipelineExtensions().getExtensionMap());
combinedExtensionMap.putAll(pipelinesDataFlowModel.getPipelineExtensions().getExtensionMap());
}
}

public Map<String, Object> getExtensionMap() {
return Collections.unmodifiableMap(extensionMap);
public Map<String, Object> getDataPrepperConfigExtensionMap() {
return Collections.unmodifiableMap(dataPrepperConfigExtensionMap);
}

public Map<String, Object> getCombinedExtensionMap() {
return Collections.unmodifiableMap(combinedExtensionMap);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,35 @@

import javax.inject.Named;
import java.time.Duration;
import java.util.Set;

/**
* Application context for internal plugin framework beans.
*/
@Named
public class ObjectMapperConfiguration {
@Bean(name = "pluginConfigObjectMapper")
ObjectMapper objectMapper() {
static final Set<Class> TRANSLATE_VALUE_SUPPORTED_JAVA_TYPES = Set.of(
String.class, Number.class, Long.class, Short.class, Integer.class, Double.class, Float.class,
Boolean.class, Duration.class, Enum.class, Character.class);

@Bean(name = "extensionPluginConfigObjectMapper")
ObjectMapper extensionPluginConfigObjectMapper() {
final SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(Duration.class, new DataPrepperDurationDeserializer());

return new ObjectMapper()
.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
.registerModule(simpleModule);
}

@Bean(name = "pluginConfigObjectMapper")
ObjectMapper pluginConfigObjectMapper(final VariableExpander variableExpander) {
final SimpleModule simpleModule = new SimpleModule();
TRANSLATE_VALUE_SUPPORTED_JAVA_TYPES.stream().forEach(clazz -> simpleModule.addDeserializer(
clazz, new DataPrepperScalarTypeDeserializer<>(variableExpander, clazz)));

return new ObjectMapper()
.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
.registerModule(simpleModule);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@
@Named
class PluginBeanFactoryProvider implements Provider<BeanFactory> {
private final GenericApplicationContext sharedPluginApplicationContext;
private final GenericApplicationContext coreApplicationContext;

@Inject
PluginBeanFactoryProvider(final ApplicationContext coreContext) {
final ApplicationContext publicContext = Objects.requireNonNull(coreContext.getParent());
PluginBeanFactoryProvider(final GenericApplicationContext coreApplicationContext) {
final ApplicationContext publicContext = Objects.requireNonNull(coreApplicationContext.getParent());
sharedPluginApplicationContext = new GenericApplicationContext(publicContext);
this.coreApplicationContext = coreApplicationContext;
}

/**
Expand All @@ -43,6 +45,10 @@ GenericApplicationContext getSharedPluginApplicationContext() {
return sharedPluginApplicationContext;
}

GenericApplicationContext getCoreApplicationContext() {
return coreApplicationContext;
}

/**
* @since 1.3
* Creates a new isolated application context that inherits from
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@

package org.opensearch.dataprepper.plugin;

import com.fasterxml.jackson.core.type.TypeReference;
import org.opensearch.dataprepper.model.annotations.DataPrepperPlugin;
import org.opensearch.dataprepper.model.configuration.PluginSetting;
import org.opensearch.dataprepper.model.plugin.InvalidPluginConfigurationException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import org.springframework.context.annotation.DependsOn;

import javax.inject.Named;
import java.util.Collections;
Expand All @@ -24,7 +26,9 @@
* and converting it to the plugin model type which should be denoted by {@link DataPrepperPlugin#pluginConfigurationType()}
*/
@Named
@DependsOn({"extensionsApplier"})
class PluginConfigurationConverter {
static final TypeReference<Map<String, Object>> MAP_TYPE_REFERENCE = new TypeReference<>() {};
private final ObjectMapper objectMapper;
private final Validator validator;

Expand All @@ -48,8 +52,12 @@ public Object convert(final Class<?> pluginConfigurationType, final PluginSettin
Objects.requireNonNull(pluginConfigurationType);
Objects.requireNonNull(pluginSetting);

if (pluginConfigurationType.equals(PluginSetting.class))
if (pluginConfigurationType.equals(PluginSetting.class)) {
final Map<String, Object> settings = pluginSetting.getSettings();
final Map<String, Object> convertedSettings = objectMapper.convertValue(settings, MAP_TYPE_REFERENCE);
pluginSetting.setSettings(convertedSettings);
return pluginSetting;
}

final Object configuration = convertSettings(pluginConfigurationType, pluginSetting);

Expand Down
Loading
Loading