Ensures that there are no issues with context setup when picking custom propagation type (#1989)

fixes gh-1987
This commit is contained in:
Marcin Grzejszczak
2021-07-02 17:14:05 +02:00
committed by GitHub
parent e9d55a3af0
commit 8b61684168
5 changed files with 113 additions and 127 deletions

View File

@@ -105,7 +105,11 @@ class BraveBaggageConfiguration {
// See #1643
@Bean
@ConditionalOnMissingBean
PropagationFactorySupplier defaultPropagationFactorySupplier() {
PropagationFactorySupplier defaultPropagationFactorySupplier(SleuthPropagationProperties properties) {
if (properties.getType().contains(PropagationType.CUSTOM)) {
throw new IllegalStateException(
"Please register a bean with the following signature [extends Propagation.Factory implements Propagation<String>] to override the default Sleuth behaviour or [implements PropagationFactorySupplier] to reuse it.");
}
return () -> B3Propagation.newFactoryBuilder().injectFormat(B3Propagation.Format.SINGLE_NO_PARENT).build();
}
@@ -131,7 +135,6 @@ class BraveBaggageConfiguration {
@Qualifier(PROPAGATION_KEYS) List<String> propagationKeys, SleuthBaggageProperties sleuthBaggageProperties,
SleuthPropagationProperties sleuthPropagationProperties, PropagationFactorySupplier supplier,
@Nullable List<BaggagePropagationCustomizer> baggagePropagationCustomizers) {
Set<String> localFields = redirectOldPropertyToNew(LOCAL_KEYS, localKeys, "spring.sleuth.baggage.local-fields",
sleuthBaggageProperties.getLocalFields());
for (String fieldName : localFields) {

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.autoconfig.brave.baggage;
import java.util.Collections;
import java.util.List;
import brave.internal.propagation.StringPropagationAdapter;
import brave.propagation.Propagation;
import brave.propagation.TraceContext;
import brave.propagation.TraceContextOrSamplingFlags;
import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.gateway.config.GatewayAutoConfiguration;
import org.springframework.cloud.gateway.config.GatewayClassPathWarningAutoConfiguration;
import org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class CustomPropagationFactoryTests {
@Test
void should_fail_to_start_the_context_when_propagation_type_custom_and_no_custom_propagation_provided() {
new ApplicationContextRunner().withUserConfiguration(Config.class)
.withPropertyValues("spring.sleuth.propagation.type=custom")
.run(context -> BDDAssertions.then(context).hasFailed());
}
@Test
void should_start_the_context_when_propagation_type_custom_and_no_custom_propagation_provided() {
new ApplicationContextRunner().withUserConfiguration(CustomConfig.class)
.withPropertyValues("spring.sleuth.propagation.type=custom").run(context -> BDDAssertions.then(context)
.hasNotFailed().getBean(CustomConfig.CustomPropagation.class));
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
MongoAutoConfiguration.class, QuartzAutoConfiguration.class })
static class Config {
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration(exclude = { GatewayClassPathWarningAutoConfiguration.class, GatewayAutoConfiguration.class,
GatewayMetricsAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
MongoAutoConfiguration.class, QuartzAutoConfiguration.class })
static class CustomConfig {
@Bean
CustomPropagation customPropagation() {
return new CustomPropagation();
}
static class CustomPropagation extends Propagation.Factory implements Propagation<String> {
@Override
public List<String> keys() {
return Collections.emptyList();
}
@Override
public <R> TraceContext.Injector<R> injector(Setter<R, String> setter) {
return (traceContext, request) -> {
};
}
@Override
public <R> TraceContext.Extractor<R> extractor(Getter<R, String> getter) {
return request -> TraceContextOrSamplingFlags.EMPTY;
}
@Override
public <K> Propagation<K> create(KeyFactory<K> keyFactory) {
return StringPropagationAdapter.create(this, keyFactory);
}
}
}
}

View File

@@ -84,7 +84,7 @@ class CompositePropagationFactory extends Propagation.Factory implements Propaga
W3CPropagation w3CPropagation = new W3CPropagation(braveBaggageManager, localFields);
this.mapping.put(PropagationType.W3C, new AbstractMap.SimpleEntry<>(w3CPropagation, w3CPropagation.get()));
LazyPropagationFactory lazyPropagationFactory = new LazyPropagationFactory(
beanFactory.getBeanProvider(Factory.class));
beanFactory.getBeanProvider(PropagationFactorySupplier.class));
this.mapping.put(PropagationType.CUSTOM,
new AbstractMap.SimpleEntry<>(lazyPropagationFactory, lazyPropagationFactory.get()));
}
@@ -161,17 +161,17 @@ class CompositePropagationFactory extends Propagation.Factory implements Propaga
@SuppressWarnings("unchecked")
private static final class LazyPropagationFactory extends Propagation.Factory {
private final ObjectProvider<Propagation.Factory> delegate;
private final ObjectProvider<PropagationFactorySupplier> delegate;
private volatile Propagation.Factory propagationFactory;
private LazyPropagationFactory(ObjectProvider<Propagation.Factory> delegate) {
private LazyPropagationFactory(ObjectProvider<PropagationFactorySupplier> delegate) {
this.delegate = delegate;
}
private Propagation.Factory propagationFactory() {
if (this.propagationFactory == null) {
this.propagationFactory = this.delegate.getIfAvailable(() -> NoOpPropagation.INSTANCE);
this.propagationFactory = this.delegate.getIfAvailable(() -> () -> NoOpPropagation.INSTANCE).get();
}
return this.propagationFactory;
}

View File

@@ -40,7 +40,8 @@ public enum PropagationType {
W3C,
/**
* Custom propagation type.
* Custom propagation type. If picked, requires bean registration overriding the
* default propagation mechanisms.
*/
CUSTOM

View File

@@ -1,120 +0,0 @@
/*
* Copyright 2013-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.brave.bridge;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import brave.internal.codec.HexCodec;
import brave.internal.propagation.StringPropagationAdapter;
import brave.propagation.Propagation;
import brave.propagation.TraceContext;
import brave.propagation.TraceContextOrSamplingFlags;
import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.loadbalancer.support.SimpleObjectProvider;
import org.springframework.cloud.sleuth.brave.propagation.PropagationType;
import org.springframework.util.StringUtils;
class CompositePropagationFactorySupplierTests {
@Test
void should_pick_custom_registered_propagation_when_custom_mode_picked() {
BeanFactory beanFactory = Mockito.mock(BeanFactory.class);
Mockito.when(beanFactory.getBeanProvider(BraveBaggageManager.class))
.thenReturn(new SimpleObjectProvider(new BraveBaggageManager()));
Mockito.when(beanFactory.getBeanProvider(Propagation.Factory.class))
.thenReturn(new SimpleObjectProvider(new CustomTracePropagation()));
Mockito.when(beanFactory.getBeanProvider(Propagation.class))
.thenReturn(new SimpleObjectProvider(new CustomTracePropagation()));
CompositePropagationFactorySupplier supplier = new CompositePropagationFactorySupplier(beanFactory,
Collections.emptyList(), Collections.singletonList(PropagationType.CUSTOM));
BDDAssertions.then(supplier.get().get().keys()).containsExactly(CustomTraceExtractor.CUSTOM_TRACE_HEADER);
}
}
class CustomTracePropagation extends Propagation.Factory implements Propagation<String> {
public static final List<String> KEYS = Collections.singletonList(CustomTraceExtractor.CUSTOM_TRACE_HEADER);
@Override
public List<String> keys() {
return KEYS;
}
@Override
public <R> TraceContext.Injector<R> injector(Setter<R, String> setter) {
return (traceContext, request) -> {
String trace = traceContext.traceIdString() + ":" + traceContext.spanIdString();
setter.put(request, CustomTraceExtractor.CUSTOM_TRACE_HEADER, trace);
};
}
@Override
public <R> TraceContext.Extractor<R> extractor(Getter<R, String> getter) {
Objects.requireNonNull(getter);
return new CustomTraceExtractor<>(getter);
}
@Override
public <K> Propagation<K> create(KeyFactory<K> keyFactory) {
return StringPropagationAdapter.create(this, keyFactory);
}
}
class CustomTraceExtractor<R> implements TraceContext.Extractor<R> {
static final String CUSTOM_TRACE_HEADER = "x-custom-trace";
final Propagation.Getter<R, String> getter;
CustomTraceExtractor(Propagation.Getter<R, String> getter) {
this.getter = getter;
}
@Override
@SuppressWarnings("ReturnCount")
public TraceContextOrSamplingFlags extract(R request) {
String traceString = getter.get(request, CUSTOM_TRACE_HEADER);
if (!StringUtils.hasText(traceString)) {
return TraceContextOrSamplingFlags.EMPTY;
}
String[] trace = traceString.split(":");
if (trace.length != 2) {
return TraceContextOrSamplingFlags.EMPTY;
}
try {
TraceContext traceContext = TraceContext.newBuilder().traceId(HexCodec.lowerHexToUnsignedLong(trace[0]))
.spanId(HexCodec.lowerHexToUnsignedLong(trace[1])).build();
return TraceContextOrSamplingFlags.create(traceContext);
}
catch (NumberFormatException ex) {
return TraceContextOrSamplingFlags.EMPTY;
}
}
}