feat: support encoding and decoding TSF headers without TSF Consul (#1816)
* feat: support encoding and decoding TSF headers without TSF Consul Allow TSF-Tags transfer on Polaris via spring.cloud.tencent.metadata.tsf-header-compatible so putTag still works when Consul is off. Co-authored-by: Cursor <cursoragent@cursor.com> * chore: fill changelog PR number 1816. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor: centralize TSF header compatible check in TsfContextUtils Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: evelynwei <evelynwei@tencent.com> Co-authored-by: Cursor <cursoragent@cursor.com>pull/1817/head
parent
6712d772ef
commit
bbd0aa74da
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||
*
|
||||
* Copyright (C) 2021 Tencent. All rights reserved.
|
||||
*
|
||||
* Licensed under the BSD 3-Clause License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
* 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 com.tencent.cloud.metadata.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import com.tencent.cloud.common.constant.MetadataConstant;
|
||||
import com.tencent.cloud.common.metadata.MetadataContext;
|
||||
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||
import com.tencent.cloud.common.util.UrlUtils;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
/**
|
||||
* Test TSF header decode when Consul is disabled and tsf-header-compatible is true.
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@SpringBootTest(webEnvironment = RANDOM_PORT,
|
||||
classes = DecodeTransferTsfHeaderCompatibleTest.TestApplication.class,
|
||||
properties = {"spring.config.location = classpath:application-test.yml",
|
||||
"spring.main.web-application-type = servlet",
|
||||
"spring.cloud.gateway.enabled = false",
|
||||
"spring.cloud.tencent.metadata.tsf-header-compatible = true"})
|
||||
public class DecodeTransferTsfHeaderCompatibleTest {
|
||||
|
||||
@Autowired
|
||||
private DecodeTransferMetadataServletFilter metadataServletFilter;
|
||||
|
||||
@Test
|
||||
public void testDecodeTsfTagsWithoutConsul() throws ServletException, IOException {
|
||||
AtomicReference<String> featValue = new AtomicReference<>();
|
||||
FilterChain filterChain = (servletRequest, servletResponse) ->
|
||||
featValue.set(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "feat"));
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.addHeader(MetadataConstant.HeaderName.TSF_TAGS,
|
||||
UrlUtils.encode("[{\"k\":\"feat\",\"v\":\"test\",\"f\":[\"0\"]}]"));
|
||||
metadataServletFilter.doFilter(request, new MockHttpServletResponse(), filterChain);
|
||||
|
||||
assertThat(featValue.get()).isEqualTo("test");
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
protected static class TestApplication {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||
*
|
||||
* Copyright (C) 2021 Tencent. All rights reserved.
|
||||
*
|
||||
* Licensed under the BSD 3-Clause License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
* 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 com.tencent.cloud.metadata.core;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.tencent.cloud.common.constant.MetadataConstant;
|
||||
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
|
||||
import com.tencent.cloud.common.tsf.TsfContextUtils;
|
||||
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext;
|
||||
import feign.Request;
|
||||
import feign.RequestTemplate;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test TSF header encode when Consul is disabled.
|
||||
*/
|
||||
public class EncodeTransferTsfHeaderCompatibleTest {
|
||||
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
public void reset() throws Exception {
|
||||
MetadataContextHolder.remove();
|
||||
Field isTsfConsulEnabledFirst = TsfContextUtils.class.getDeclaredField("isTsfConsulEnabledFirstConfiguration");
|
||||
isTsfConsulEnabledFirst.setAccessible(true);
|
||||
((AtomicBoolean) isTsfConsulEnabledFirst.get(null)).set(true);
|
||||
|
||||
Field tsfConsulEnabledField = TsfContextUtils.class.getDeclaredField("tsfConsulEnabled");
|
||||
tsfConsulEnabledField.setAccessible(true);
|
||||
tsfConsulEnabledField.set(null, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeTsfHeaderWhenCompatibleWithoutConsul() throws Throwable {
|
||||
MetadataContextHolder.get().setTransitiveMetadata(Collections.singletonMap("feat", "test"));
|
||||
MetadataLocalProperties properties = new MetadataLocalProperties();
|
||||
properties.setTsfHeaderCompatible(true);
|
||||
EncodeTransferMedataFeignEnhancedPlugin plugin =
|
||||
new EncodeTransferMedataFeignEnhancedPlugin(new ArrayList<>(), properties);
|
||||
|
||||
Request request = createRequest();
|
||||
EnhancedPluginContext context = new EnhancedPluginContext();
|
||||
context.setOriginRequest(request);
|
||||
plugin.run(context);
|
||||
|
||||
assertThat(request.headers()).containsKey(MetadataConstant.HeaderName.TSF_TAGS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipTsfHeaderWhenIncompatibleWithoutConsul() throws Throwable {
|
||||
MetadataContextHolder.get().setTransitiveMetadata(Collections.singletonMap("feat", "test"));
|
||||
MetadataLocalProperties properties = new MetadataLocalProperties();
|
||||
properties.setTsfHeaderCompatible(false);
|
||||
EncodeTransferMedataFeignEnhancedPlugin plugin =
|
||||
new EncodeTransferMedataFeignEnhancedPlugin(new ArrayList<>(), properties);
|
||||
|
||||
Request request = createRequest();
|
||||
EnhancedPluginContext context = new EnhancedPluginContext();
|
||||
context.setOriginRequest(request);
|
||||
plugin.run(context);
|
||||
|
||||
assertThat(request.headers()).doesNotContainKey(MetadataConstant.HeaderName.TSF_TAGS);
|
||||
assertThat(request.headers()).containsKey(MetadataConstant.HeaderName.CUSTOM_METADATA);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeTsfHeaderWhenConsulEnabled() throws Throwable {
|
||||
Field tsfConsulEnabledField = TsfContextUtils.class.getDeclaredField("tsfConsulEnabled");
|
||||
tsfConsulEnabledField.setAccessible(true);
|
||||
tsfConsulEnabledField.set(null, true);
|
||||
|
||||
MetadataContextHolder.get().setTransitiveMetadata(Collections.singletonMap("feat", "test"));
|
||||
EncodeTransferMedataFeignEnhancedPlugin plugin =
|
||||
new EncodeTransferMedataFeignEnhancedPlugin(new ArrayList<>(), new MetadataLocalProperties());
|
||||
|
||||
Request request = createRequest();
|
||||
EnhancedPluginContext context = new EnhancedPluginContext();
|
||||
context.setOriginRequest(request);
|
||||
plugin.run(context);
|
||||
|
||||
assertThat(request.headers()).containsKey(MetadataConstant.HeaderName.TSF_TAGS);
|
||||
}
|
||||
|
||||
private Request createRequest() {
|
||||
Map<String, Collection<String>> headers = Collections.emptyMap();
|
||||
return Request.create(Request.HttpMethod.GET, "http://localhost/test",
|
||||
headers, new byte[0], StandardCharsets.UTF_8, new RequestTemplate());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||
*
|
||||
* Copyright (C) 2021 Tencent. All rights reserved.
|
||||
*
|
||||
* Licensed under the BSD 3-Clause License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
* 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 com.tencent.cloud.common.metadata.config;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test binding of tsf-header-compatible.
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = MetadataLocalPropertiesTsfHeaderCompatibleTest.TestApplication.class,
|
||||
properties = {"spring.config.location = classpath:application-test.yml",
|
||||
"spring.cloud.tencent.metadata.tsf-header-compatible=true"})
|
||||
public class MetadataLocalPropertiesTsfHeaderCompatibleTest {
|
||||
|
||||
@Autowired
|
||||
private MetadataLocalProperties metadataLocalProperties;
|
||||
|
||||
@Test
|
||||
public void testTsfHeaderCompatibleBinding() {
|
||||
assertThat(metadataLocalProperties.isTsfHeaderCompatible()).isTrue();
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
protected static class TestApplication {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||
*
|
||||
* Copyright (C) 2021 Tencent. All rights reserved.
|
||||
*
|
||||
* Licensed under the BSD 3-Clause License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
* 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 com.tencent.cloud.common.tsf;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test for {@link TsfContextUtils}.
|
||||
*/
|
||||
public class TsfContextUtilsTest {
|
||||
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
public void reset() throws Exception {
|
||||
Field isTsfConsulEnabledFirst = TsfContextUtils.class.getDeclaredField("isTsfConsulEnabledFirstConfiguration");
|
||||
isTsfConsulEnabledFirst.setAccessible(true);
|
||||
((AtomicBoolean) isTsfConsulEnabledFirst.get(null)).set(true);
|
||||
|
||||
Field tsfConsulEnabledField = TsfContextUtils.class.getDeclaredField("tsfConsulEnabled");
|
||||
tsfConsulEnabledField.setAccessible(true);
|
||||
tsfConsulEnabledField.set(null, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsTsfHeaderCompatibleWhenConfigured() {
|
||||
assertThat(TsfContextUtils.isTsfHeaderCompatible(true)).isTrue();
|
||||
assertThat(TsfContextUtils.isTsfConsulEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsTsfHeaderCompatibleWhenConsulEnabled() throws Exception {
|
||||
Field tsfConsulEnabledField = TsfContextUtils.class.getDeclaredField("tsfConsulEnabled");
|
||||
tsfConsulEnabledField.setAccessible(true);
|
||||
tsfConsulEnabledField.set(null, true);
|
||||
|
||||
assertThat(TsfContextUtils.isTsfHeaderCompatible(false)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsTsfHeaderCompatibleWhenDisabled() {
|
||||
assertThat(TsfContextUtils.isTsfHeaderCompatible(false)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsTsfHeaderCompatibleFromProperties() {
|
||||
assertThat(TsfContextUtils.isTsfHeaderCompatible((MetadataLocalProperties) null))
|
||||
.isFalse();
|
||||
|
||||
MetadataLocalProperties properties = new MetadataLocalProperties();
|
||||
assertThat(TsfContextUtils.isTsfHeaderCompatible(properties)).isFalse();
|
||||
|
||||
properties.setTsfHeaderCompatible(true);
|
||||
assertThat(TsfContextUtils.isTsfHeaderCompatible(properties)).isTrue();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||
*
|
||||
* Copyright (C) 2021 Tencent. All rights reserved.
|
||||
*
|
||||
* Licensed under the BSD 3-Clause License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
* 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 com.tencent.cloud.tsf.demo.consumer.controller;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.tencent.cloud.common.metadata.MetadataContext;
|
||||
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||
import com.tencent.cloud.tsf.demo.consumer.proxy.ProviderDemoService;
|
||||
import com.tencent.polaris.api.utils.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.tsf.core.TsfContext;
|
||||
import org.springframework.tsf.core.entity.Tag;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Put TSF tags then dump local context or call provider /metadata.
|
||||
*/
|
||||
@RestController
|
||||
public class MetadataInspectController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MetadataInspectController.class);
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private ProviderDemoService providerDemoService;
|
||||
|
||||
@GetMapping("/local-metadata")
|
||||
public Map<String, Object> localMetadata(
|
||||
@RequestParam(required = false) String hopKey,
|
||||
@RequestParam(required = false) String hopValue,
|
||||
@RequestParam(required = false) String passKey,
|
||||
@RequestParam(required = false) String passValue) {
|
||||
LOG.info("GET /local-metadata hop={}:{} pass={}:{}", hopKey, hopValue, passKey, passValue);
|
||||
putTags(hopKey, hopValue, passKey, passValue);
|
||||
Map<String, Object> body = dumpLocal();
|
||||
LOG.info("GET /local-metadata dump after putTag (no outbound): {}", body);
|
||||
return body;
|
||||
}
|
||||
|
||||
@GetMapping("/echo-metadata-rest")
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Object> echoMetadataRest(
|
||||
@RequestParam(required = false) String hopKey,
|
||||
@RequestParam(required = false) String hopValue,
|
||||
@RequestParam(required = false) String passKey,
|
||||
@RequestParam(required = false) String passValue) {
|
||||
LOG.info("GET /echo-metadata-rest hop={}:{} pass={}:{}, RestTemplate -> provider-demo /metadata",
|
||||
hopKey, hopValue, passKey, passValue);
|
||||
putTags(hopKey, hopValue, passKey, passValue);
|
||||
Map<String, Object> body = restTemplate.getForObject("http://provider-demo/metadata", Map.class);
|
||||
LOG.info("GET /echo-metadata-rest provider /metadata response: {}", body);
|
||||
return body;
|
||||
}
|
||||
|
||||
@GetMapping("/echo-metadata-feign")
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Object> echoMetadataFeign(
|
||||
@RequestParam(required = false) String hopKey,
|
||||
@RequestParam(required = false) String hopValue,
|
||||
@RequestParam(required = false) String passKey,
|
||||
@RequestParam(required = false) String passValue) {
|
||||
LOG.info("GET /echo-metadata-feign hop={}:{} pass={}:{}, "
|
||||
+ "Feign ProviderDemoService#metadata() -> provider-demo /metadata",
|
||||
hopKey, hopValue, passKey, passValue);
|
||||
putTags(hopKey, hopValue, passKey, passValue);
|
||||
Map<String, Object> body = providerDemoService.metadata();
|
||||
LOG.info("GET /echo-metadata-feign provider /metadata response: {}", body);
|
||||
return body;
|
||||
}
|
||||
|
||||
private void putTags(String hopKey, String hopValue, String passKey, String passValue) {
|
||||
if (StringUtils.isNotBlank(hopKey)) {
|
||||
TsfContext.putTag(hopKey, hopValue);
|
||||
LOG.info("TsfContext.putTag one-hop (no flag) {}:{}", hopKey, hopValue);
|
||||
}
|
||||
if (StringUtils.isNotBlank(passKey)) {
|
||||
TsfContext.putTags(Collections.singletonMap(passKey, passValue), Tag.ControlFlag.TRANSITIVE);
|
||||
LOG.info("TsfContext.putTags TRANSITIVE {}:{}", passKey, passValue);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> dumpLocal() {
|
||||
MetadataContext ctx = MetadataContextHolder.get();
|
||||
Map<String, Object> body = new LinkedHashMap<>();
|
||||
body.put("calleeTransitive", ctx.getTransitiveMetadata());
|
||||
body.put("calleeDisposable", ctx.getDisposableMetadata());
|
||||
body.put("callerDisposable", ctx.getFragmentContext(MetadataContext.FRAGMENT_UPSTREAM_DISPOSABLE));
|
||||
return body;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making spring-cloud-tencent available.
|
||||
*
|
||||
* Copyright (C) 2021 Tencent. All rights reserved.
|
||||
*
|
||||
* Licensed under the BSD 3-Clause License (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
* 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 com.tencent.cloud.tsf.demo.provider;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.tencent.cloud.common.constant.MetadataConstant;
|
||||
import com.tencent.cloud.common.metadata.MetadataContext;
|
||||
import com.tencent.cloud.common.metadata.MetadataContextHolder;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* Dump incoming TSF/SCT headers and MetadataContext buckets for tag verification.
|
||||
*/
|
||||
@RestController
|
||||
public class MetadataInspectController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MetadataInspectController.class);
|
||||
|
||||
@GetMapping("/metadata")
|
||||
public Map<String, Object> metadata(HttpServletRequest request) {
|
||||
MetadataContext ctx = MetadataContextHolder.get();
|
||||
Map<String, Object> body = new LinkedHashMap<>();
|
||||
Map<String, String> headers = new LinkedHashMap<>();
|
||||
headers.put(MetadataConstant.HeaderName.TSF_TAGS,
|
||||
request.getHeader(MetadataConstant.HeaderName.TSF_TAGS));
|
||||
headers.put(MetadataConstant.HeaderName.TSF_SYSTEM_TAG,
|
||||
request.getHeader(MetadataConstant.HeaderName.TSF_SYSTEM_TAG));
|
||||
headers.put(MetadataConstant.HeaderName.TSF_METADATA,
|
||||
request.getHeader(MetadataConstant.HeaderName.TSF_METADATA));
|
||||
headers.put(MetadataConstant.HeaderName.CUSTOM_METADATA,
|
||||
request.getHeader(MetadataConstant.HeaderName.CUSTOM_METADATA));
|
||||
headers.put(MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA,
|
||||
request.getHeader(MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA));
|
||||
body.put("headers", headers);
|
||||
body.put("calleeTransitive", ctx.getTransitiveMetadata());
|
||||
body.put("calleeDisposable", ctx.getDisposableMetadata());
|
||||
body.put("callerDisposable", ctx.getFragmentContext(MetadataContext.FRAGMENT_UPSTREAM_DISPOSABLE));
|
||||
LOG.info("GET /metadata incoming headers TSF-Tags={} TSF-System-Tags={} TSF-Metadata={} "
|
||||
+ "SCT-CUSTOM-METADATA={} SCT-CUSTOM-DISPOSABLE-METADATA={}",
|
||||
headers.get(MetadataConstant.HeaderName.TSF_TAGS),
|
||||
headers.get(MetadataConstant.HeaderName.TSF_SYSTEM_TAG),
|
||||
headers.get(MetadataConstant.HeaderName.TSF_METADATA),
|
||||
headers.get(MetadataConstant.HeaderName.CUSTOM_METADATA),
|
||||
headers.get(MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA));
|
||||
LOG.info("GET /metadata MetadataContext calleeTransitive={} calleeDisposable={} callerDisposable={}",
|
||||
body.get("calleeTransitive"), body.get("calleeDisposable"), body.get("callerDisposable"));
|
||||
return body;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue