-
Notifications
You must be signed in to change notification settings - Fork 812
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
Fix parsing of unclean map values in Config. #4032
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.tooling.config; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
import static org.assertj.core.api.Assertions.entry; | ||
|
||
import io.opentelemetry.instrumentation.api.config.Config; | ||
import io.opentelemetry.sdk.autoconfigure.ConfigProperties; | ||
import io.opentelemetry.sdk.autoconfigure.ConfigurationException; | ||
import java.time.Duration; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import org.junit.jupiter.api.Disabled; | ||
import org.junit.jupiter.api.Test; | ||
|
||
// Copied from | ||
// https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/ConfigPropertiesTest.java | ||
class ConfigPropertiesAdapterTest { | ||
|
||
@Test | ||
void allValid() { | ||
Map<String, String> properties = new HashMap<>(); | ||
properties.put("string", "str"); | ||
properties.put("int", "10"); | ||
properties.put("long", "20"); | ||
properties.put("double", "5.4"); | ||
properties.put("list", "cat,dog,bear"); | ||
properties.put("map", "cat=meow,dog=bark,bear=growl"); | ||
properties.put("duration", "1s"); | ||
|
||
ConfigProperties config = createConfig(properties); | ||
assertThat(config.getString("string")).isEqualTo("str"); | ||
assertThat(config.getInt("int")).isEqualTo(10); | ||
assertThat(config.getLong("long")).isEqualTo(20); | ||
assertThat(config.getDouble("double")).isEqualTo(5.4); | ||
assertThat(config.getCommaSeparatedValues("list")).containsExactly("cat", "dog", "bear"); | ||
assertThat(config.getCommaSeparatedMap("map")) | ||
.containsExactly(entry("cat", "meow"), entry("dog", "bark"), entry("bear", "growl")); | ||
assertThat(config.getDuration("duration")).isEqualTo(Duration.ofSeconds(1)); | ||
} | ||
|
||
@Test | ||
void allMissing() { | ||
ConfigProperties config = createConfig(Collections.emptyMap()); | ||
assertThat(config.getString("string")).isNull(); | ||
assertThat(config.getInt("int")).isNull(); | ||
assertThat(config.getLong("long")).isNull(); | ||
assertThat(config.getDouble("double")).isNull(); | ||
assertThat(config.getCommaSeparatedValues("list")).isEmpty(); | ||
assertThat(config.getCommaSeparatedMap("map")).isEmpty(); | ||
assertThat(config.getDuration("duration")).isNull(); | ||
} | ||
|
||
@Test | ||
void allEmpty() { | ||
Map<String, String> properties = new HashMap<>(); | ||
properties.put("string", ""); | ||
properties.put("int", ""); | ||
properties.put("long", ""); | ||
properties.put("double", ""); | ||
properties.put("list", ""); | ||
properties.put("map", ""); | ||
properties.put("duration", ""); | ||
|
||
ConfigProperties config = createConfig(properties); | ||
assertThat(config.getString("string")).isEmpty(); | ||
assertThat(config.getInt("int")).isNull(); | ||
assertThat(config.getLong("long")).isNull(); | ||
assertThat(config.getDouble("double")).isNull(); | ||
assertThat(config.getCommaSeparatedValues("list")).isEmpty(); | ||
assertThat(config.getCommaSeparatedMap("map")).isEmpty(); | ||
assertThat(config.getDuration("duration")).isNull(); | ||
} | ||
|
||
@Test | ||
@Disabled("Currently invalid values are not handled the same as the SDK") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mateuszrzeszutek I think we should make sure the behavior is the same as the SDK for these too There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that'd certainly make sense to do so. On the other hand, in the agent code I think we'd probably prefer to fall back on the default value instead of throwing an exception in the middle of an advice class. So, how about the following:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I think that's fine |
||
void invalidInt() { | ||
assertThatThrownBy(() -> createConfig(Collections.singletonMap("int", "bar")).getInt("int")) | ||
.isInstanceOf(ConfigurationException.class) | ||
.hasMessage("Invalid value for property int=bar. Must be a integer."); | ||
assertThatThrownBy( | ||
() -> createConfig(Collections.singletonMap("int", "999999999999999")).getInt("int")) | ||
.isInstanceOf(ConfigurationException.class) | ||
.hasMessage("Invalid value for property int=999999999999999. Must be a integer."); | ||
} | ||
|
||
@Test | ||
@Disabled("Currently invalid values are not handled the same as the SDK") | ||
void invalidLong() { | ||
assertThatThrownBy(() -> createConfig(Collections.singletonMap("long", "bar")).getLong("long")) | ||
.isInstanceOf(ConfigurationException.class) | ||
.hasMessage("Invalid value for property long=bar. Must be a long."); | ||
assertThatThrownBy( | ||
() -> | ||
createConfig(Collections.singletonMap("long", "99223372036854775807")) | ||
.getLong("long")) | ||
.isInstanceOf(ConfigurationException.class) | ||
.hasMessage("Invalid value for property long=99223372036854775807. Must be a long."); | ||
} | ||
|
||
@Test | ||
@Disabled("Currently invalid values are not handled the same as the SDK") | ||
void invalidDouble() { | ||
assertThatThrownBy( | ||
() -> createConfig(Collections.singletonMap("double", "bar")).getDouble("double")) | ||
.isInstanceOf(ConfigurationException.class) | ||
.hasMessage("Invalid value for property double=bar. Must be a double."); | ||
assertThatThrownBy( | ||
() -> createConfig(Collections.singletonMap("double", "1.0.1")).getDouble("double")) | ||
.isInstanceOf(ConfigurationException.class) | ||
.hasMessage("Invalid value for property double=1.0.1. Must be a double."); | ||
} | ||
|
||
@Test | ||
void uncleanList() { | ||
assertThat( | ||
createConfig(Collections.singletonMap("list", " a ,b,c , d,, ,")) | ||
.getCommaSeparatedValues("list")) | ||
.containsExactly("a", "b", "c", "d"); | ||
} | ||
|
||
@Test | ||
void uncleanMap() { | ||
assertThat( | ||
createConfig(Collections.singletonMap("map", " a=1 ,b=2,c = 3 , d= 4,, ,")) | ||
.getCommaSeparatedMap("map")) | ||
.containsExactly(entry("a", "1"), entry("b", "2"), entry("c", "3"), entry("d", "4")); | ||
} | ||
|
||
@Test | ||
@Disabled("Currently invalid values are not handled the same as the SDK") | ||
void invalidMap() { | ||
assertThatThrownBy( | ||
() -> | ||
createConfig(Collections.singletonMap("map", "a=1,b=")).getCommaSeparatedMap("map")) | ||
.isInstanceOf(ConfigurationException.class) | ||
.hasMessage("Invalid map property: map=a=1,b="); | ||
assertThatThrownBy( | ||
() -> | ||
createConfig(Collections.singletonMap("map", "a=1,b")).getCommaSeparatedMap("map")) | ||
.isInstanceOf(ConfigurationException.class) | ||
.hasMessage("Invalid map property: map=a=1,b"); | ||
assertThatThrownBy( | ||
() -> | ||
createConfig(Collections.singletonMap("map", "a=1,=b")).getCommaSeparatedMap("map")) | ||
.isInstanceOf(ConfigurationException.class) | ||
.hasMessage("Invalid map property: map=a=1,=b"); | ||
} | ||
|
||
@Test | ||
@Disabled("Currently invalid values are not handled the same as the SDK") | ||
void invalidDuration() { | ||
assertThatThrownBy( | ||
() -> | ||
createConfig(Collections.singletonMap("duration", "1a1ms")).getDuration("duration")) | ||
.isInstanceOf(ConfigurationException.class) | ||
.hasMessage("Invalid duration property duration=1a1ms. Expected number, found: 1a1"); | ||
assertThatThrownBy( | ||
() -> createConfig(Collections.singletonMap("duration", "9mm")).getDuration("duration")) | ||
.isInstanceOf(ConfigurationException.class) | ||
.hasMessage("Invalid duration property duration=9mm. Invalid duration string, found: mm"); | ||
} | ||
|
||
@Test | ||
void durationUnitParsing() { | ||
assertThat(createConfig(Collections.singletonMap("duration", "1")).getDuration("duration")) | ||
.isEqualTo(Duration.ofMillis(1)); | ||
assertThat(createConfig(Collections.singletonMap("duration", "2ms")).getDuration("duration")) | ||
.isEqualTo(Duration.ofMillis(2)); | ||
assertThat(createConfig(Collections.singletonMap("duration", "3s")).getDuration("duration")) | ||
.isEqualTo(Duration.ofSeconds(3)); | ||
assertThat(createConfig(Collections.singletonMap("duration", "4m")).getDuration("duration")) | ||
.isEqualTo(Duration.ofMinutes(4)); | ||
assertThat(createConfig(Collections.singletonMap("duration", "5h")).getDuration("duration")) | ||
.isEqualTo(Duration.ofHours(5)); | ||
assertThat(createConfig(Collections.singletonMap("duration", "6d")).getDuration("duration")) | ||
.isEqualTo(Duration.ofDays(6)); | ||
// Check Space handling | ||
assertThat(createConfig(Collections.singletonMap("duration", "7 ms")).getDuration("duration")) | ||
.isEqualTo(Duration.ofMillis(7)); | ||
assertThat(createConfig(Collections.singletonMap("duration", "8 ms")).getDuration("duration")) | ||
.isEqualTo(Duration.ofMillis(8)); | ||
} | ||
|
||
private static ConfigProperties createConfig(Map<String, String> properties) { | ||
return new ConfigPropertiesAdapter(Config.newBuilder().readProperties(properties).build()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we allow empty values? e.g.
key1=val1,key2=,key3=val3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do for
getString
(at least in SDK) though seem to be missing tests for it, and the map with empty values. I'd expect the two to be consistent. I think returning empty when it's empty is reasonable behavior, it's similar to how query params work