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

Add safemapstr.Put #6434

Merged
merged 3 commits into from
Mar 12, 2018
Merged

Add safemapstr.Put #6434

merged 3 commits into from
Mar 12, 2018

Conversation

exekias
Copy link
Contributor

@exekias exekias commented Feb 21, 2018

Add safemapstr.Put:

This method implements a way to put dotted keys into a MapStr while
ensuring they don't override each other when they are not really prepared to be stored in a tree. For example:

  a := MapStr{}
  a.Put("com.docker.swarm.task", "x")
  a.Put("com.docker.swarm.task.id", 1)
  a.Put("com.docker.swarm.task.name", "foobar")

Will result in {"com":{"docker":{"swarm":{"task":"x"}}}}

Put detects this scenario and renames the common base key, by appending
.value:

 a := MapStr{}
 safemapstr.Put(a, "com.docker.swarm.task", "x")
 safemapstr.Put(a, "com.docker.swarm.task.id", 1)
 safemapstr.Put(a, "com.docker.swarm.task.name", "foobar")

Will result in {"com":{"docker":{"swarm":{"task":{"id":1,"name":"foobar","value":"x"}}}}}

The plan is to use this method to convert all unstructured external data, like labels, annotations and so

Related to #6421

@exekias exekias added enhancement discuss Issue needs further discussion. libbeat labels Feb 21, 2018
@exekias
Copy link
Contributor Author

exekias commented Feb 21, 2018

@ruflin I came up with this while working on "dedoting", what do you think?

@exekias exekias added the review label Feb 21, 2018
@urso
Copy link

urso commented Feb 22, 2018

TBH I'm kind of -1 on this.

First of all, common.MapStr is already a 'monster'. Please let's not add more methods on top of it.

There is some magic going on, which might not be always clear when and how it is triggered.
Also wonder if/how fields.yml needs to be aware of this magic as well? The Put function allows you to use flat names, but it's already doing some magic in hiding the fact names are not flat, but we have some namespacing going on.

The docker swarm case of having these kind of labels is quite bad practice. When trying to add some hierarchy into labels using dots, the labels open up some kind of namespacing. Therefore a label string must not be a prefix of another label. This should be the case in general, not just in the context of beats.

Instead of adding the DeDot functionality to common.MapStr, how about having autodiscovery (or some other module) detect violations in label use and fix the label naming there. We could also introduce some label-rewriting rules (with default rules). Also consider opening an issue against docker-swarm to use sane labels (what's the difference between task and task.name?).

@exekias
Copy link
Contributor Author

exekias commented Feb 22, 2018

Thanks for the feedback, sounds good to me! I'll create a helper function to build the safe MapStr somewhere outside MapStr code 👍

@exekias exekias removed the discuss Issue needs further discussion. label Feb 22, 2018
@exekias exekias changed the title Add DeDotPut method to MapStr Add safemapstr.Put method Feb 25, 2018
@exekias
Copy link
Contributor Author

exekias commented Feb 25, 2018

I moved the code to a new safemapstr package, this should be ready for a second look 😃

@exekias exekias changed the title Add safemapstr.Put method Add safemapstr.Put Feb 25, 2018
This method implements a way to put dotted keys into a MapStr while
ensuring they don't override each other. For example:

```
  a := MapStr{}
  a.Put("com.docker.swarm.task", "x")
  a.Put("com.docker.swarm.task.id", 1)
  a.Put("com.docker.swarm.task.name", "foobar")
```

Will result in `{"com":{"docker":{"swarm":{"task":"x"}}}}`

DeDotPut detects this scenario and renames the common base key, by appending
`.value`:

```
 a := MapStr{}
 safemapstr.Put(a, "com.docker.swarm.task", "x")
 safemapstr.Put(a, "com.docker.swarm.task.id", 1)
 safemapstr.Put(a, "com.docker.swarm.task.name", "foobar")
```

Will result in `{"com":{"docker":{"swarm":{"task":{"id":1,"name":"foobar","value":"x"}}}}}`

The plan is to use this method to convert all unstructured external data, like labels, annotations and so

Related to elastic#6421
Copy link
Contributor

@ruflin ruflin left a comment

Choose a reason for hiding this comment

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

LGTM.

@urso Can you also have a look again?

//
// Put detects this scenario and renames the common base key, by appending
// `.value`
func Put(data common.MapStr, key string, value interface{}) error {
Copy link

Choose a reason for hiding this comment

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

how about introducing a 'defaultKey' to be used instead of hard-coded 'value' ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

do you mean moving it to a const or passing it as a parameter to Put?

Copy link

Choose a reason for hiding this comment

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

parameter. I wonder if in some cases we can declare/define a better name then 'value'.

// Put detects this scenario and renames the common base key, by appending
// `.value`
func Put(data common.MapStr, key string, value interface{}) error {
keyParts := strings.SplitN(key, ".", 2)
Copy link

Choose a reason for hiding this comment

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

I see it's a copy'n paste of MapStr.Put.

Using strings.Index, sub-slices and loop might make the routine even faster.

@exekias
Copy link
Contributor Author

exekias commented Mar 7, 2018

jenkins, retest it please

if len(keyParts) == 1 {
oldValue, exists := data[key]
if exists {
switch oldValue.(type) {
Copy link
Member

Choose a reason for hiding this comment

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

I think you should use tryToMapStr here to be consistent with the handling of common.MapStr and map[string]interface{}.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice catch, pushing a change to make use of it 😇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants