Skip to content

Commit 2303e53

Browse files
committed
Added AWS dependencies so IAM login works from config files and also added more preflight options to handle developers with limited permissions ( Drop all Collections, Create with Validation)
1 parent 33e0cb0 commit 2303e53

4 files changed

Lines changed: 132 additions & 36 deletions

File tree

memex/pom.xml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
3737
<cucumber.version>7.22.2</cucumber.version>
3838
<rest-assured.version>5.5.5</rest-assured.version>
39+
<amazonaws.version>2.29.43</amazonaws.version>
3940
</properties>
4041

4142
<dependencyManagement>
@@ -69,6 +70,32 @@
6970
<artifactId>spring-kafka</artifactId>
7071
</dependency>
7172

73+
<dependency>
74+
<groupId>com.amazonaws</groupId>
75+
<artifactId>aws-java-sdk-ssm</artifactId>
76+
<version>1.12.74</version>
77+
</dependency>
78+
<dependency>
79+
<groupId>software.amazon.awssdk</groupId>
80+
<artifactId>secretsmanager</artifactId>
81+
<version>${amazonaws.version}</version>
82+
<exclusions>
83+
<exclusion>
84+
<artifactId>httpclient</artifactId>
85+
<groupId>org.apache.httpcomponents</groupId>
86+
</exclusion>
87+
<exclusion>
88+
<artifactId>apache-client</artifactId>
89+
<groupId>software.amazon.awssdk</groupId>
90+
</exclusion>
91+
</exclusions>
92+
</dependency>
93+
<dependency>
94+
<groupId>software.amazon.awssdk</groupId>
95+
<artifactId>sdk-core</artifactId>
96+
<version>${amazonaws.version}</version>
97+
</dependency>
98+
7299
<dependency>
73100
<groupId>org.springframework.boot</groupId>
74101
<artifactId>spring-boot-starter-data-mongodb</artifactId>

memex/src/main/java/com/johnlpage/memex/service/MongoDbPreflightCheckService.java

Lines changed: 84 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
import com.johnlpage.memex.util.MongoSchemaGenerator;
55
import com.mongodb.client.MongoCollection;
66
import com.mongodb.client.MongoDatabase;
7+
import com.mongodb.client.model.CreateCollectionOptions;
8+
import com.mongodb.client.model.ValidationAction;
9+
import com.mongodb.client.model.ValidationLevel;
10+
import com.mongodb.client.model.ValidationOptions;
711
import java.util.*;
8-
9-
import org.apache.kafka.common.protocol.types.Field;
1012
import org.bson.Document;
11-
import org.bson.json.JsonWriterSettings;
1213
import org.slf4j.Logger;
1314
import org.slf4j.LoggerFactory;
1415
import org.springframework.beans.factory.annotation.Value;
@@ -23,7 +24,7 @@
2324
/**
2425
* This is an example of a class you can use to ensure the system is ready to start correctly. It
2526
* shows you how to check or indexes etc. And optionally create them - although spring has similar
26-
* lifecycle methods they aren't comprehensive enough. It uses ApplicationRunner to ensure this is
27+
* lifecycle methods, they aren't comprehensive enough. It uses ApplicationRunner to ensure this is
2728
* run on startup.
2829
*/
2930
@Service
@@ -60,15 +61,24 @@ public class MongoDbPreflightCheckService {
6061
@Value("${memex.preflight.createSearchIndexes:true}")
6162
private boolean createSearchIndexes;
6263

64+
@Value("${memex.preflight.enforceSchema:true}")
65+
private boolean enforceSchema;
66+
6367
@Value("${memex.mongodb.hasSearch:true}")
6468
private boolean hasSearch;
6569

70+
@Value("${memex.preflight.schemaOnCreate:true}")
71+
private boolean schemaOnCreate;
72+
73+
@Value("${memex.preflight.dropAllCollections:false}")
74+
private boolean dropAllCollections;
75+
6676
public MongoDbPreflightCheckService(ApplicationContext context, MongoTemplate mongoTemplate) {
6777
this.context = context;
6878
this.mongoTemplate = mongoTemplate;
6979
}
7080

71-
/** Ensure all Collections exist, create them or quit depending on flag. */
81+
/** Ensure all Collections exist, create them or quit depending on flags. */
7282
List<Document> ensureCollectionsExist(Document schemaAndIndexes) {
7383
MongoDatabase database = mongoTemplate.getDb();
7484

@@ -78,40 +88,76 @@ List<Document> ensureCollectionsExist(Document schemaAndIndexes) {
7888
for (Document requiredCollection : requiredCollections) {
7989
String collectionName = requiredCollection.getString("name");
8090
String className = requiredCollection.getString("serverSchemaEnforcement");
91+
Document validator = null;
92+
if (className != null) {
93+
try {
94+
Class<?> clazz = Class.forName(className);
95+
validator = MongoSchemaGenerator.generateSchema(clazz);
96+
LOG.info("Scheme for {} = '{}' ", collectionName, validator.toJson());
97+
98+
} catch (Exception e) {
99+
LOG.error("Could not process class {}: {}", className, e.getMessage());
100+
}
101+
}
102+
103+
// Rarely used but useful if you have no easy way to drop the DB
104+
if (dropAllCollections && existingCollections.contains(collectionName)) {
105+
LOG.warn(
106+
"WARNING: FORCE DROPPING COLLECTION {} !!! - disable in app config if you don't want to");
107+
mongoTemplate.dropCollection(collectionName);
108+
existingCollections.remove(collectionName);
109+
}
81110

82111
if (!existingCollections.contains(collectionName)) {
83112
if (createCollections) {
84-
LOG.warn("Collection '{}' does not exist, creating it.", collectionName);
85-
database.createCollection(collectionName);
113+
114+
if (schemaOnCreate) {
115+
/*
116+
This Code is here if you cannot apply validation using collMod you can do so on
117+
create - but to change it you would need to drop and recreate the collection, which
118+
may be OK in Development
119+
*/
120+
121+
ValidationOptions validationOptions =
122+
new ValidationOptions()
123+
.validator(validator)
124+
.validationLevel(ValidationLevel.MODERATE) // or STRICT, OFF
125+
.validationAction(ValidationAction.ERROR); // or WARN
126+
CreateCollectionOptions options =
127+
new CreateCollectionOptions().validationOptions(validationOptions);
128+
database.createCollection(collectionName, options);
129+
130+
LOG.warn(
131+
"Collection '{}' does not exist, creating it with schema validation.",
132+
collectionName);
133+
} else {
134+
LOG.warn("Collection '{}' does not exist, creating it.", collectionName);
135+
database.createCollection(collectionName);
136+
}
137+
86138
} else {
87139
LOG.error("Collection '{}' does not exist, cancelling startup", collectionName);
88140
int exitCode = SpringApplication.exit(context, () -> 0);
89141
System.exit(exitCode);
90142
}
91143
}
92144

93-
if (className != null) {
145+
if (validator != null && enforceSchema && !schemaOnCreate) {
94146
try {
95-
Class<?> clazz = Class.forName(className);
96-
Document schema = MongoSchemaGenerator.generateSchema(clazz);
97-
LOG.info("Schema: {}", schema.toJson());
98-
99147
// Apply validator via collMod
100148
Document collModCmd =
101149
new Document("collMod", collectionName)
102-
.append("validator", schema)
150+
.append("validator", validator)
103151
.append(
104152
"validationLevel", "moderate") // doesn't retroactively reject existing docs
105153
.append("validationAction", "error"); // reject bad inserts/updates
106154

107155
database.runCommand(collModCmd);
108-
LOG.info("Enforcing Schema based on {}", className);
109-
LOG.debug(
110-
"Collection '{}' schema enforced",
111-
schema.toJson(JsonWriterSettings.builder().indent(true).build()));
156+
LOG.info("Enforcing Schema Validation with collMod based on {}", className);
112157

113-
} catch (ClassNotFoundException e) {
114-
LOG.error("Could not load class {}: {}", className, e.getMessage());
158+
} catch (Exception e) {
159+
LOG.error(
160+
"Error enforcing schema validation for class {}: {}", className, e.getMessage());
115161
}
116162
}
117163
}
@@ -160,6 +206,7 @@ void ensureRequiredSearchIndexesExist(List<Document> requiredInfo) {
160206
if (!hasSearch) {
161207
return;
162208
}
209+
163210
for (Document requiredCollection : requiredInfo) {
164211
String collectionName = requiredCollection.getString("name");
165212

@@ -169,17 +216,24 @@ void ensureRequiredSearchIndexesExist(List<Document> requiredInfo) {
169216
if (requiredSearchIndexes != null) {
170217

171218
// Get a list of the indexes that are defined and their statuses
219+
AggregationResults<Document> results;
172220
AggregationOperation listIndexes = Aggregation.stage("{\"$listSearchIndexes\" : {}}");
173-
AggregationResults<Document> results =
174-
mongoTemplate.aggregate(
175-
Aggregation.newAggregation(listIndexes), collectionName, Document.class);
221+
try {
222+
results =
223+
mongoTemplate.aggregate(
224+
Aggregation.newAggregation(listIndexes), collectionName, Document.class);
225+
} catch (Exception e) {
226+
LOG.error(
227+
"ERROR: memex.mongodb.hasSearch=true but Search not available: {}", e.getMessage());
228+
return;
229+
}
176230
Map<String, Document> resultsearchIndexMap = new HashMap<>();
177231
for (Document existingSearchIndex : results) {
178232
String key = existingSearchIndex.getString("name");
179233
resultsearchIndexMap.put(key, existingSearchIndex);
180234
}
181235

182-
// Iterate over the indexes we requires
236+
// Iterate over the indexes we require
183237
for (Document requiredSearchIndex : requiredSearchIndexes) {
184238
String requiredName = requiredSearchIndex.getString("name");
185239
Document requiredDefinition = requiredSearchIndex.get("definition", Document.class);
@@ -230,10 +284,15 @@ public ApplicationRunner mongoPreflightCheck(MongoVersionBean mongoVersionBean)
230284
mongoVersionBean.getMinorversion());
231285
if (createRequiredIndexes) {
232286
LOG.warn(
233-
"!!! MEMEX IS CONFIGURED TO AUTOMATICALLY CREATE MISSING INDEXES - THIS IS NOT RECOMMENDED IN "
287+
"WARNING: MEMEX IS CONFIGURED TO AUTOMATICALLY CREATE MISSING INDEXES - THIS IS NOT RECOMMENDED IN "
234288
+ "PRODUCTION !");
235289
}
236-
290+
LOG.info("createRequiredIndexes: {}", createRequiredIndexes);
291+
LOG.info("createCollections: {}", createCollections);
292+
LOG.info("createSearchIndexes: {}", createSearchIndexes);
293+
LOG.info("enforceSchema: {}", enforceSchema);
294+
LOG.info("hasSearch: {}", hasSearch);
295+
LOG.info("schemaOnCreate: {}", schemaOnCreate);
237296
Document schemaAndIndexes = Document.parse(SCHEMA_AND_INDEXES);
238297
List<Document> requiredInfo = ensureCollectionsExist(schemaAndIndexes);
239298
ensureRequiredIndexesExist(requiredInfo);
Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,40 @@
11
spring.application.name=memex
2-
#spring.data.mongodb.uri=mongodb://localhost/?replicaSet=repset
3-
spring.data.mongodb.uri=mongodb://localhost:27018
2+
spring.data.mongodb.uri=mongodb://localhost/?replicaSet=rs0
3+
#spring.data.mongodb.uri=mongodb+srv://cluster0.qcpeq8.mongodb.net/?authSource=%24external&authMechanism=MONGODB-AWS
4+
#spring.data.mongodb.username=<Your Username>
5+
#spring.data.mongodb.password=<Your password>>
46
spring.data.mongodb.database=memex
7+
#
58
##Set this to false if you are using Enterprise Advanced without Search
6-
memex.mongodb.hasSearch=false
7-
server.shutdown=graceful
8-
spring.mvc.async.request-timeout=0
9-
spring.lifecycle.timeout-per-shutdown-phase=30s
9+
#
10+
memex.mongodb.hasSearch=true
11+
#There define how preflight should work
1012
memex.preflight.createRequiredIndexes=true
1113
memex.preflight.createCollections=true
1214
memex.preflight.createSearchIndexes=true
13-
# Connection timeout (60 seconds)
14-
server.tomcat.connection-timeout=0
15+
#Use these if you are lacking permissions to create/drop databases
16+
#Or lacking collMod to update a collection
17+
memex.preflight.schemaOnCreate=false
18+
memex.preflight.dropAllCollections=false
19+
#
1520
# Optional: increase max header size if needed
1621
#If not in URI or other Auth Method
17-
#spring.data.mongodb.username=admin
18-
#spring.data.mongodb.password=password
1922
logging.level.org.springframework.data.repository=INFO
2023
logging.level.org.springframework.data.mongodb.core=INFO
2124
# This one is handy to see what is being done against MDB
2225
logging.level.org.mongodb.driver=INFO
2326
#debug=true
27+
#
28+
# Use if testing the Kafka classes
2429
memex.kafkaexmple.enabled=false
2530
spring.kafka.bootstrap-servers=localhost:9092
2631
spring.kafka.consumer.group-id=my-group-id
2732
spring.kafka.consumer.auto-offset-reset=earliest
2833
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
2934
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
30-
35+
#
36+
server.shutdown=graceful
37+
spring.mvc.async.request-timeout=0
38+
spring.lifecycle.timeout-per-shutdown-phase=30s
39+
# Connection timeout (60 seconds)
40+
server.tomcat.connection-timeout=0

mxtest/target/mxtest-1.0.jar

-1.75 KB
Binary file not shown.

0 commit comments

Comments
 (0)