mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-01 19:15:52 +01:00
Set span kinds for internal requests
This commit is contained in:
@@ -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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
33
scm-webapp/src/main/java/sonia/scm/plugin/Tracing.java
Normal file
33
scm-webapp/src/main/java/sonia/scm/plugin/Tracing.java
Normal 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() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user