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

Sidecar collector backend configuration name cannot contain "." #4885

Closed
bernd opened this issue Jul 6, 2018 · 0 comments · Fixed by #4886
Closed

Sidecar collector backend configuration name cannot contain "." #4885

bernd opened this issue Jul 6, 2018 · 0 comments · Fixed by #4886
Assignees
Labels
blocker If not finished by release date, the release will be postponed. bug sidecar triaged
Milestone

Comments

@bernd
Copy link
Member

bernd commented Jul 6, 2018

Expected Behavior

Sidecar collector backend name accepts arbitrary values.

Current Behavior

I was using packetbeat-6.3 as a name for a newly configured sidecar collector backend. Creating the backend config worked fine as well as assigning the backend config to a sidecar.

After assigning the configuration to a sidecar, I saw the following error in the server log:

java.lang.IllegalArgumentException: Invalid BSON field name packetbeat-6.3
graylog_1        | 2018-07-06 10:09:30,478 ERROR: org.graylog2.shared.rest.exceptionmappers.AnyExceptionClassMapper - Unhandled exception in REST resource
graylog_1        | java.lang.IllegalArgumentException: Invalid BSON field name packetbeat-6.3
graylog_1        | 	at org.bson.AbstractBsonWriter.writeName(AbstractBsonWriter.java:532) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.connection.LevelCountingBsonWriter.writeName(LevelCountingBsonWriter.java:203) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.DBObjectCodec.encodeMap(DBObjectCodec.java:221) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:198) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.DBObjectCodec.encodeMap(DBObjectCodec.java:222) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:198) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.DBObjectCodec.encodeMap(DBObjectCodec.java:222) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:198) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.DBObjectCodec.encode(DBObjectCodec.java:130) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.DBObjectCodec.encode(DBObjectCodec.java:61) ~[graylog.jar:?]
graylog_1        | 	at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63) ~[graylog.jar:?]
graylog_1        | 	at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29) ~[graylog.jar:?]
graylog_1        | 	at org.bson.codecs.EncoderContext.encodeWithChildContext(EncoderContext.java:91) ~[graylog.jar:?]
graylog_1        | 	at org.bson.codecs.BsonDocumentCodec.writeValue(BsonDocumentCodec.java:136) ~[graylog.jar:?]
graylog_1        | 	at org.bson.codecs.BsonDocumentCodec.encode(BsonDocumentCodec.java:115) ~[graylog.jar:?]
graylog_1        | 	at org.bson.codecs.BsonDocumentCodec.encode(BsonDocumentCodec.java:41) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.connection.RequestMessage.addDocument(RequestMessage.java:247) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.connection.RequestMessage.addDocument(RequestMessage.java:197) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.connection.CommandMessage.encodeMessageBodyWithMetadata(CommandMessage.java:126) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.connection.RequestMessage.encode(RequestMessage.java:147) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.connection.InternalStreamConnection.sendAndReceive(InternalStreamConnection.java:245) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.connection.UsageTrackingInternalConnection.sendAndReceive(UsageTrackingInternalConnection.java:98) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.connection.DefaultConnectionPool$PooledConnection.sendAndReceive(DefaultConnectionPool.java:441) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.connection.CommandProtocolImpl.execute(CommandProtocolImpl.java:80) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:189) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:264) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.connection.DefaultServerConnection.command(DefaultServerConnection.java:126) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.connection.DefaultServerConnection.command(DefaultServerConnection.java:118) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.operation.CommandOperationHelper$3.call(CommandOperationHelper.java:436) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.operation.OperationHelper.withReleasableConnection(OperationHelper.java:433) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.operation.CommandOperationHelper.executeRetryableCommand(CommandOperationHelper.java:429) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.operation.BaseFindAndModifyOperation.execute(BaseFindAndModifyOperation.java:33) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.operation.FindAndReplaceOperation.execute(FindAndReplaceOperation.java:56) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.Mongo$3.execute(Mongo.java:837) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.Mongo$3.execute(Mongo.java:818) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.DBCollection.findAndModify(DBCollection.java:1934) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.DBCollection.findAndModify(DBCollection.java:1797) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.DBCollection.findAndModify(DBCollection.java:1766) ~[graylog.jar:?]
graylog_1        | 	at com.mongodb.DBCollection.findAndModify(DBCollection.java:1713) ~[graylog.jar:?]
graylog_1        | 	at org.mongojack.JacksonDBCollection.findAndModify(JacksonDBCollection.java:866) ~[graylog.jar:?]
graylog_1        | 	at org.graylog.plugins.sidecar.services.SidecarService.save(SidecarService.java:78) ~[graylog.jar:?]
graylog_1        | 	at org.graylog.plugins.sidecar.rest.resources.SidecarResource.register(SidecarResource.java:209) ~[graylog.jar:?]
graylog_1        | 	at sun.reflect.GeneratedMethodAccessor100.invoke(Unknown Source) ~[?:?]
graylog_1        | 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_171]
graylog_1        | 	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_171]
graylog_1        | 	at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81) ~[graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144) ~[graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161) ~[graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:160) ~[graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99) ~[graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389) ~[graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347) ~[graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102) ~[graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326) [graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271) [graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267) [graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.internal.Errors.process(Errors.java:315) [graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.internal.Errors.process(Errors.java:297) [graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.internal.Errors.process(Errors.java:267) [graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317) [graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305) [graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154) [graylog.jar:?]
graylog_1        | 	at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.service(GrizzlyHttpContainer.java:384) [graylog.jar:?]
graylog_1        | 	at org.glassfish.grizzly.http.server.HttpHandler$1.run(HttpHandler.java:224) [graylog.jar:?]
graylog_1        | 	at com.codahale.metrics.InstrumentedExecutorService$InstrumentedRunnable.run(InstrumentedExecutorService.java:181) [graylog.jar:?]
graylog_1        | 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_171]
graylog_1        | 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_171]
graylog_1        | 	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_171]

The problem is that we are using a map for the collector backend status where the key is the backend name. Since MongoDB doesn't allow . characters in key names, we are getting this error.

Example:

In this case we only have a filebeat backend in the node_details.status.collectors structure.

{
	"_id" : "...",
	"node_id" : "...",
	"node_name" : "...",
	"node_details" : {
		"operating_system" : "Linux",
		"ip" : "...",
		"metrics" : {},
		"status" : {
			"status" : 0,
			"message" : "1 collectors running",
			"collectors" : {
				"filebeat" : {
					"status" : 0,
					"message" : ""
				}
			}
		}
	},
	"assignments" : [],
	"sidecar_version" : "1.0.0",
	"last_seen" : "..."
}

Possible Solution

1. Use a set of collector objects

We could convert the node_details.status.collectors map to a set of objects and add the name as a field in the collector status object. That way we don't run into problems with invalid characters in MongoDB field names.

{
	"node_details" : {
		"status" : {
			"collectors" : [
                            {
                                "name": "filebeat",
                                "status": 0,
                                "message": "..."
                             }
			]
		}
	}
}

2. Index collector status objects by collector ID

We could also use the collector backend config ID as a key name in the map. That would also avoid problems with MongoDB field names.

{
	"node_details" : {
		"status" : {
			"collectors" : {
				"<unique-collector-id>" : {
                                        "name": "filebeat",
					"status" : 0,
					"message" : ""
				}
			}
		}
	}
}

3. Don't allow certain characters in collector backend names

We could also restrict certain characters in the backend names. That means we would need to validate the user input and show an error in the UI.

That said, I would not do this because it would be inconsistent with the other "name" values in the sidecar configuration. Also, implementation details of the database shouldn't be a constraint for naming configurations in my opinion.

Steps to Reproduce (for bugs)

  1. Create new log collector backend with name "foo.bar"
  2. Create new collector configuration and use the new "foo.bar" backend
  3. Assign configuration to a sidecar
  4. Check Graylog server logs for errors

Context

I am in favor of the first possible solution (set of collector objects) because that also makes it easier for MongoDB queries. Using dynamic fields in MongoDB, and thus a dynamic schema, can make it hard to run some queries. For example "return all status objects where status != 0" is hard to express as a MongoDB query with a dynamic document schema.

Your Environment

  • Graylog Version: 3.0.0-alpha.2-SNAPSHOT (rev 7b37177)
@bernd bernd added bug blocker If not finished by release date, the release will be postponed. labels Jul 6, 2018
@bernd bernd added this to the 3.0.0 milestone Jul 6, 2018
@bernd bernd self-assigned this Jul 6, 2018
@bernd bernd added the sidecar label Jul 6, 2018
bernd added a commit that referenced this issue Jul 6, 2018
This fixes a problem when using a "." character in a collector backend
name. It also makes it easier to run MongoDB queries against collector
status objects.

Fixes #4885
bernd added a commit to Graylog2/collector-sidecar that referenced this issue Jul 6, 2018
The collector backend status data structure changed from a map to a set
of objects.

Refs Graylog2/graylog2-server#4885
Refs Graylog2/graylog2-server#4886
@joschi joschi added the triaged label Jul 9, 2018
mariussturm pushed a commit that referenced this issue Jul 9, 2018
This fixes a problem when using a "." character in a collector backend
name. It also makes it easier to run MongoDB queries against collector
status objects.

Fixes #4885
mariussturm pushed a commit to Graylog2/collector-sidecar that referenced this issue Jul 9, 2018
The collector backend status data structure changed from a map to a set
of objects.

Refs Graylog2/graylog2-server#4885
Refs Graylog2/graylog2-server#4886
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocker If not finished by release date, the release will be postponed. bug sidecar triaged
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants