Skip to content

Commit a2c8829

Browse files
graylog-internal-actions-access[bot]patrickmannclaude
authored
Fix extractors being deleted when an input is started or stopped (7.1) (#26203)
* Fix extractors being deleted when an input is started or stopped (#26198) * Fix extractors being deleted when an input is started or stopped The PersistedImpl -> AutoValue migration of InputImpl (#24057) dropped the embedded extractors array from the entity model: the builder ignores unknown properties, so loading an input discards its extractors, and saving uses replaceOne, which then persists the loss by replacing the whole document. Starting or stopping an input persists the desired state on the input document since #25338, so every start/stop deleted all extractors of the input. Updating an input through the REST API had the same effect via the getFields() merge in InputsResource, which also omitted extractors. Fix this by modeling the embedded extractor documents on InputImpl (like the embedded static fields) so they survive full document round-trips, and by carrying them through getFields() and buildFromMap(). Also turn persistDesiredState() into a targeted update of the desired_state field so state changes no longer rewrite the whole document. Fixes #26009 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * CL --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit dbc747f) * Remove backported extractor regression tests from 7.1 branch --------- Co-authored-by: Patrick Mann <patrickmann@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 157ade0 commit a2c8829

4 files changed

Lines changed: 35 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
type = "fixed"
2+
message = "Fix extractors being deleted when an input is started, stopped, or updated."
3+
4+
issues = ["26009"]
5+
pulls = ["26198"]

graylog2-server/src/main/java/org/graylog2/inputs/InputImpl.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ public Map<String, String> getStaticFields() {
120120
return result;
121121
}
122122

123+
/**
124+
* The embedded extractor documents. They are modified through targeted update operations (see
125+
* {@code InputServiceImpl#addExtractor} etc.) and only modeled here so that they survive full document
126+
* replacements when saving an input.
127+
*/
128+
@Nullable
129+
@JsonProperty(EMBEDDED_EXTRACTORS)
130+
public abstract List<Map<String, Object>> getEmbeddedExtractors();
131+
123132
@NotNull
124133
@JsonProperty(FIELD_TYPE)
125134
public abstract String getType();
@@ -179,6 +188,9 @@ public static Builder create() {
179188
@JsonProperty(EMBEDDED_STATIC_FIELDS)
180189
public abstract Builder setEmbeddedStaticFields(List<Map<String, String>> staticFields);
181190

191+
@JsonProperty(EMBEDDED_EXTRACTORS)
192+
public abstract Builder setEmbeddedExtractors(List<Map<String, Object>> extractors);
193+
182194
@JsonProperty(FIELD_TYPE)
183195
public abstract Builder setType(String type);
184196

@@ -226,6 +238,11 @@ public Map<String, Object> getFields() {
226238
doc.put(EMBEDDED_STATIC_FIELDS, getEmbeddedStaticFields());
227239
}
228240

241+
final List<Map<String, Object>> extractors = getEmbeddedExtractors();
242+
if (extractors != null && !extractors.isEmpty()) {
243+
doc.put(EMBEDDED_EXTRACTORS, extractors);
244+
}
245+
229246
if (getContentPack() != null) {
230247
doc.put(FIELD_CONTENT_PACK, getContentPack());
231248
}

graylog2-server/src/main/java/org/graylog2/inputs/InputServiceImpl.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,11 @@ private InputImpl.Builder buildFromMap(Map<String, Object> fields) {
296296
builder.setEmbeddedStaticFields(staticFields);
297297
}
298298

299+
final List<Map<String, Object>> extractors = (List<Map<String, Object>>) fields.get(InputImpl.EMBEDDED_EXTRACTORS);
300+
if (extractors != null && !extractors.isEmpty()) {
301+
builder.setEmbeddedExtractors(extractors);
302+
}
303+
299304
if (!isGlobal) {
300305
builder.setNodeId((String) fields.get(MessageInput.FIELD_NODE_ID));
301306
}
@@ -749,8 +754,12 @@ private InputImpl withEncryptedFields(InputImpl input) {
749754

750755
@Override
751756
public void persistDesiredState(Input input, IOState.Type desiredState) throws ValidationException {
752-
final Input updatedInput = input.withDesiredState(desiredState);
753-
saveWithoutEvents(updatedInput);
757+
// Use a targeted update instead of saving the whole input to avoid overwriting concurrent changes
758+
// to other parts of the input document.
759+
collection.updateOne(
760+
MongoUtils.idEq(input.getId()),
761+
Updates.set(InputImpl.FIELD_DESIRED_STATE, desiredState.name())
762+
);
754763
}
755764

756765
@Override

graylog2-server/src/test/java/org/graylog2/inputs/InputServiceImplTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ public void persistedDocumentContainsOnlyExpectedFields(MongoCollections mongoCo
448448
.setEmbeddedStaticFields(List.of(
449449
Map.of(InputImpl.FIELD_STATIC_FIELD_KEY, "static_key",
450450
InputImpl.FIELD_STATIC_FIELD_VALUE, "static_value")))
451+
.setEmbeddedExtractors(List.of(createCopyInputExtractor().getPersistedFields()))
451452
.build();
452453

453454
final String id = inputService.save(input);
@@ -466,6 +467,7 @@ public void persistedDocumentContainsOnlyExpectedFields(MongoCollections mongoCo
466467
InputImpl.FIELD_GLOBAL,
467468
InputImpl.FIELD_CONFIGURATION,
468469
InputImpl.EMBEDDED_STATIC_FIELDS,
470+
InputImpl.EMBEDDED_EXTRACTORS,
469471
InputImpl.FIELD_DESIRED_STATE,
470472
InputImpl.FIELD_CONTENT_PACK,
471473
InputImpl.FIELD_NODE_ID

0 commit comments

Comments
 (0)