Set span kinds for internal requests

This commit is contained in:
Sebastian Sdorra
2020-11-04 09:37:24 +01:00
parent b91d9e6b60
commit 5694a89589
8 changed files with 77 additions and 14 deletions

View File

@@ -422,5 +422,5 @@ public abstract class BaseHttpRequest<T extends BaseHttpRequest>
private String url; private String url;
/** kind of span for trace api */ /** kind of span for trace api */
private String spanKind = "http-request"; private String spanKind = "HTTP Request";
} }

View File

@@ -24,6 +24,7 @@
package sonia.scm.admin; package sonia.scm.admin;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -46,6 +47,9 @@ public class ReleaseFeedParser {
public static final int DEFAULT_TIMEOUT_IN_MILLIS = 1000; public static final int DEFAULT_TIMEOUT_IN_MILLIS = 1000;
private static final Logger LOG = LoggerFactory.getLogger(ReleaseFeedParser.class); private static final Logger LOG = LoggerFactory.getLogger(ReleaseFeedParser.class);
@VisibleForTesting
static final String SPAN_KIND = "Release Feed";
private final AdvancedHttpClient client; private final AdvancedHttpClient client;
private final ExecutorService executorService; private final ExecutorService executorService;
@@ -103,7 +107,10 @@ public class ReleaseFeedParser {
if (Strings.isNullOrEmpty(url)) { if (Strings.isNullOrEmpty(url)) {
return Optional.empty(); return Optional.empty();
} }
ReleaseFeedDto releaseFeed = client.get(url).request().contentFromXml(ReleaseFeedDto.class); ReleaseFeedDto releaseFeed = client.get(url)
.spanKind(SPAN_KIND)
.request()
.contentFromXml(ReleaseFeedDto.class);
return filterForLatestRelease(releaseFeed); return filterForLatestRelease(releaseFeed);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Could not parse release feed from {}", url, e); LOG.error("Could not parse release feed from {}", url, e);

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
package sonia.scm.plugin; package sonia.scm.plugin;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
@@ -34,6 +34,8 @@ import javax.inject.Inject;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
import static sonia.scm.plugin.Tracing.SPAN_KIND;
class PluginCenterLoader { class PluginCenterLoader {
private static final Logger LOG = LoggerFactory.getLogger(PluginCenterLoader.class); private static final Logger LOG = LoggerFactory.getLogger(PluginCenterLoader.class);
@@ -57,7 +59,8 @@ class PluginCenterLoader {
Set<AvailablePlugin> load(String url) { Set<AvailablePlugin> load(String url) {
try { try {
LOG.info("fetch plugins from {}", url); LOG.info("fetch plugins from {}", url);
PluginCenterDto pluginCenterDto = client.get(url).request().contentFromJson(PluginCenterDto.class); PluginCenterDto pluginCenterDto = client.get(url).spanKind(SPAN_KIND).request()
.contentFromJson(PluginCenterDto.class);
return mapper.map(pluginCenterDto); return mapper.map(pluginCenterDto);
} catch (Exception ex) { } catch (Exception ex) {
LOG.error("failed to load plugins from plugin center, returning empty list", ex); LOG.error("failed to load plugins from plugin center, returning empty list", ex);

View File

@@ -38,6 +38,8 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Optional; import java.util.Optional;
import static sonia.scm.plugin.Tracing.SPAN_KIND;
@SuppressWarnings("UnstableApiUsage") @SuppressWarnings("UnstableApiUsage")
// guava hash is marked as unstable // guava hash is marked as unstable
class PluginInstaller { class PluginInstaller {
@@ -126,7 +128,7 @@ class PluginInstaller {
} }
private InputStream download(AvailablePlugin plugin) throws IOException { private InputStream download(AvailablePlugin plugin) throws IOException {
return client.get(plugin.getDescriptor().getUrl()).request().contentAsStream(); return client.get(plugin.getDescriptor().getUrl()).spanKind(SPAN_KIND).request().contentAsStream();
} }
private Path createFile(AvailablePlugin plugin) throws IOException { private Path createFile(AvailablePlugin plugin) throws IOException {

View File

@@ -0,0 +1,33 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.plugin;
final class Tracing {
public static final String SPAN_KIND = "Plugin Center";
private Tracing() {
}
}

View File

@@ -32,6 +32,7 @@ import org.mockito.Answers;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.net.ahc.AdvancedHttpClient; import sonia.scm.net.ahc.AdvancedHttpClient;
import sonia.scm.net.ahc.AdvancedHttpResponse;
import java.io.IOException; import java.io.IOException;
import java.util.Date; import java.util.Date;
@@ -44,6 +45,7 @@ import java.util.concurrent.Semaphore;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static sonia.scm.admin.ReleaseFeedParser.SPAN_KIND;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class ReleaseFeedParserTest { class ReleaseFeedParserTest {
@@ -62,7 +64,7 @@ class ReleaseFeedParserTest {
void shouldFindLatestRelease() throws IOException { void shouldFindLatestRelease() throws IOException {
String url = "https://www.scm-manager.org/download/rss.xml"; String url = "https://www.scm-manager.org/download/rss.xml";
when(client.get(url).request().contentFromXml(ReleaseFeedDto.class)).thenReturn(createReleaseFeedDto()); when(request(url).contentFromXml(ReleaseFeedDto.class)).thenReturn(createReleaseFeedDto());
Optional<UpdateInfo> update = releaseFeedParser.findLatestRelease(url); Optional<UpdateInfo> update = releaseFeedParser.findLatestRelease(url);
@@ -71,13 +73,17 @@ class ReleaseFeedParserTest {
assertThat(update.get().getLink()).isEqualTo("download-3"); assertThat(update.get().getLink()).isEqualTo("download-3");
} }
private AdvancedHttpResponse request(String url) throws IOException {
return client.get(url).spanKind(SPAN_KIND).request();
}
@Test @Test
void shouldHandleTimeout() throws IOException { void shouldHandleTimeout() throws IOException {
String url = "https://www.scm-manager.org/download/rss.xml"; String url = "https://www.scm-manager.org/download/rss.xml";
Semaphore waitWithResultUntilTimeout = new Semaphore(0); Semaphore waitWithResultUntilTimeout = new Semaphore(0);
when(client.get(url).request().contentFromXml(ReleaseFeedDto.class)).thenAnswer(invocation -> { when(request(url).contentFromXml(ReleaseFeedDto.class)).thenAnswer(invocation -> {
waitWithResultUntilTimeout.acquire(); waitWithResultUntilTimeout.acquire();
return createReleaseFeedDto(); return createReleaseFeedDto();
}); });
@@ -95,7 +101,7 @@ class ReleaseFeedParserTest {
Semaphore waitWithResultUntilBothTriggered = new Semaphore(0); Semaphore waitWithResultUntilBothTriggered = new Semaphore(0);
when(client.get(url).request().contentFromXml(ReleaseFeedDto.class)).thenAnswer(invocation -> { when(request(url).contentFromXml(ReleaseFeedDto.class)).thenAnswer(invocation -> {
waitWithResultUntilBothTriggered.acquire(); waitWithResultUntilBothTriggered.acquire();
return createReleaseFeedDto(); return createReleaseFeedDto();
}); });

View File

@@ -32,6 +32,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.event.ScmEventBus; import sonia.scm.event.ScmEventBus;
import sonia.scm.net.ahc.AdvancedHttpClient; import sonia.scm.net.ahc.AdvancedHttpClient;
import sonia.scm.net.ahc.AdvancedHttpResponse;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
@@ -41,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static sonia.scm.plugin.Tracing.SPAN_KIND;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class PluginCenterLoaderTest { class PluginCenterLoaderTest {
@@ -63,16 +65,20 @@ class PluginCenterLoaderTest {
void shouldFetch() throws IOException { void shouldFetch() throws IOException {
Set<AvailablePlugin> plugins = Collections.emptySet(); Set<AvailablePlugin> plugins = Collections.emptySet();
PluginCenterDto dto = new PluginCenterDto(); PluginCenterDto dto = new PluginCenterDto();
when(client.get(PLUGIN_URL).request().contentFromJson(PluginCenterDto.class)).thenReturn(dto); when(request().contentFromJson(PluginCenterDto.class)).thenReturn(dto);
when(mapper.map(dto)).thenReturn(plugins); when(mapper.map(dto)).thenReturn(plugins);
Set<AvailablePlugin> fetched = loader.load(PLUGIN_URL); Set<AvailablePlugin> fetched = loader.load(PLUGIN_URL);
assertThat(fetched).isSameAs(plugins); assertThat(fetched).isSameAs(plugins);
} }
private AdvancedHttpResponse request() throws IOException {
return client.get(PLUGIN_URL).spanKind(SPAN_KIND).request();
}
@Test @Test
void shouldReturnEmptySetIfPluginCenterNotBeReached() throws IOException { void shouldReturnEmptySetIfPluginCenterNotBeReached() throws IOException {
when(client.get(PLUGIN_URL).request()).thenThrow(new IOException("failed to fetch")); when(request()).thenThrow(new IOException("failed to fetch"));
Set<AvailablePlugin> fetch = loader.load(PLUGIN_URL); Set<AvailablePlugin> fetch = loader.load(PLUGIN_URL);
assertThat(fetch).isEmpty(); assertThat(fetch).isEmpty();
@@ -80,7 +86,7 @@ class PluginCenterLoaderTest {
@Test @Test
void shouldFirePluginCenterErrorEvent() throws IOException { void shouldFirePluginCenterErrorEvent() throws IOException {
when(client.get(PLUGIN_URL).request()).thenThrow(new IOException("failed to fetch")); when(request()).thenThrow(new IOException("failed to fetch"));
loader.load(PLUGIN_URL); loader.load(PLUGIN_URL);

View File

@@ -34,6 +34,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import sonia.scm.SCMContextProvider; import sonia.scm.SCMContextProvider;
import sonia.scm.net.ahc.AdvancedHttpClient; import sonia.scm.net.ahc.AdvancedHttpClient;
import sonia.scm.net.ahc.AdvancedHttpResponse;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
@@ -50,6 +51,7 @@ import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static sonia.scm.plugin.Tracing.SPAN_KIND;
@ExtendWith({MockitoExtension.class}) @ExtendWith({MockitoExtension.class})
class PluginInstallerTest { class PluginInstallerTest {
@@ -101,10 +103,14 @@ class PluginInstallerTest {
} }
private void mockContent(String content) throws IOException { private void mockContent(String content) throws IOException {
when(client.get("https://download.hitchhiker.com").request().contentAsStream()) when(request("https://download.hitchhiker.com").contentAsStream())
.thenReturn(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))); .thenReturn(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
} }
private AdvancedHttpResponse request(String url) throws IOException {
return client.get(url).spanKind(SPAN_KIND).request();
}
private AvailablePlugin createGitPlugin() { private AvailablePlugin createGitPlugin() {
return createPlugin( return createPlugin(
"scm-git-plugin", "scm-git-plugin",
@@ -115,7 +121,7 @@ class PluginInstallerTest {
@Test @Test
void shouldThrowPluginDownloadException() throws IOException { void shouldThrowPluginDownloadException() throws IOException {
when(client.get("https://download.hitchhiker.com").request()).thenThrow(new IOException("failed to download")); when(request("https://download.hitchhiker.com")).thenThrow(new IOException("failed to download"));
PluginInstallationContext context = PluginInstallationContext.empty(); PluginInstallationContext context = PluginInstallationContext.empty();
AvailablePlugin gitPlugin = createGitPlugin(); AvailablePlugin gitPlugin = createGitPlugin();
@@ -136,7 +142,7 @@ class PluginInstallerTest {
void shouldThrowPluginDownloadExceptionAndCleanup() throws IOException { void shouldThrowPluginDownloadExceptionAndCleanup() throws IOException {
InputStream stream = mock(InputStream.class); InputStream stream = mock(InputStream.class);
when(stream.read(any(), anyInt(), anyInt())).thenThrow(new IOException("failed to read")); when(stream.read(any(), anyInt(), anyInt())).thenThrow(new IOException("failed to read"));
when(client.get("https://download.hitchhiker.com").request().contentAsStream()).thenReturn(stream); when(request("https://download.hitchhiker.com").contentAsStream()).thenReturn(stream);
PluginInstallationContext context = PluginInstallationContext.empty(); PluginInstallationContext context = PluginInstallationContext.empty();
AvailablePlugin gitPlugin = createGitPlugin(); AvailablePlugin gitPlugin = createGitPlugin();