Skip to content

Commit

Permalink
Merge branch 'release/0.0.7'
Browse files Browse the repository at this point in the history
  • Loading branch information
Jenkins committed Dec 22, 2021
2 parents 58f4dd0 + 6e0da63 commit d5083b3
Show file tree
Hide file tree
Showing 11 changed files with 215 additions and 51 deletions.
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@
Export your favorite GitHub repositories to Prometheus

* Use it _as a service_: See https://gh.skuzzle.de for instructions
* Deploy it _on-premise_: `docker pull ghcr.io/skuzzle/gh-prom-exporter/gh-prom-exporter:0.0.6`
* Deploy it _on-premise_: `docker pull ghcr.io/skuzzle/gh-prom-exporter/gh-prom-exporter:0.0.7`

## On-Premise deployment with docker
This application can easily be run as a docker container in whatever environment you like:

```
docker run -p 8080:8080 \
-e WEB_ALLOWANONYMOUSSCRAPE=true \
ghcr.io/skuzzle/gh-prom-exporter/gh-prom-exporter:0.0.6
ghcr.io/skuzzle/gh-prom-exporter/gh-prom-exporter:0.0.7
```

With _anonymous scraping_ allowed, you can now easily view the scrape results directly in the browser by navigating to
`https://your.docker.host:8080/YOUR-GITHUB-USERNAME/YOUR-REPOSITORY`.
`http://localhost:8080/YOUR-GITHUB-USERNAME/YOUR-REPOSITORY`.

The scraped repository can just as easy be added as static scrape target to your prometheus' scrape configs:
The scraped repository can just as easily be added as static scrape target to your prometheus' scrape configs. You can
also scrape multiple repositories of the same owner at once:

```
scrape_configs:
Expand All @@ -37,10 +38,10 @@ scrape_configs:
In case you want to enforce authenticated scrapes only, use this configuration instead:
```
docker run -p 8080:8080 \
ghcr.io/skuzzle/gh-prom-exporter/gh-prom-exporter:0.0.6
ghcr.io/skuzzle/gh-prom-exporter/gh-prom-exporter:0.0.7
```

And
Scraping now requires a GitHub access token, otherwise the service will respond with 401/Unauthorized.

```
scrape_configs:
Expand Down
4 changes: 2 additions & 2 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

[![Coverage Status](https://coveralls.io/repos/github/skuzzle/gh-prom-exporter/badge.svg?branch=master)](https://coveralls.io/github/skuzzle/gh-prom-exporter?branch=master) [![Twitter Follow](https://img.shields.io/twitter/follow/skuzzleOSS.svg?style=social)](https://twitter.com/skuzzleOSS)

* Display application version
* Support open metrics format (`Accept: application/openmetrics-text`)

```
docker pull ghcr.io/skuzzle/gh-prom-exporter/gh-prom-exporter:0.0.6
docker pull ghcr.io/skuzzle/gh-prom-exporter/gh-prom-exporter:0.0.7
```
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<groupId>de.skuzzle.ghpromexporter</groupId>
<artifactId>gh-prom-exporter</artifactId>
<version>0.0.6</version>
<version>0.0.7</version>

<name>gh-prom-exporter</name>
<description>Export GitHub repository metrics in prometheus format</description>
Expand Down
7 changes: 4 additions & 3 deletions readme/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ docker run -p 8080:8080 \
```

With _anonymous scraping_ allowed, you can now easily view the scrape results directly in the browser by navigating to
`https://your.docker.host:8080/YOUR-GITHUB-USERNAME/YOUR-REPOSITORY`.
`http://localhost:8080/YOUR-GITHUB-USERNAME/YOUR-REPOSITORY`.

The scraped repository can just as easy be added as static scrape target to your prometheus' scrape configs:
The scraped repository can just as easily be added as static scrape target to your prometheus' scrape configs. You can
also scrape multiple repositories of the same owner at once:

```
scrape_configs:
Expand All @@ -40,7 +41,7 @@ docker run -p 8080:8080 \
${docker.image.name}:${project.version}
```

And
Scraping now requires a GitHub access token, otherwise the service will respond with 401/Unauthorized.

```
scrape_configs:
Expand Down
2 changes: 1 addition & 1 deletion readme/RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![Coverage Status](https://coveralls.io/repos/github/${github.user}/${github.name}/badge.svg?branch=${github.main-branch})](https://coveralls.io/github/${github.user}/${github.name}?branch=${github.main-branch}) [![Twitter Follow](https://img.shields.io/twitter/follow/skuzzleOSS.svg?style=social)](https://twitter.com/skuzzleOSS)

* Display application version
* Support open metrics format (`Accept: application/openmetrics-text`)

```
docker pull ${docker.image.name}:${project.version}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,9 @@ Flux<ScrapeRepositoryRequest> requests() {
return Flux.fromStream(repositories.stream()
.map(repository -> ScrapeRepositoryRequest.of(owner, repository)));
}

@Override
public String toString() {
return "owner=%s, repositories=%s".formatted(owner, repositories);
}
}
11 changes: 10 additions & 1 deletion src/main/java/de/skuzzle/ghpromexporter/web/PromController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.net.InetAddress;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
Expand All @@ -25,6 +27,8 @@ record PromController(
AbuseLimiter abuseLimiter,
WebProperties properties) {

private static final Logger log = LoggerFactory.getLogger(PromController.class);

@GetMapping(path = "{owner}/{repositories}")
public Mono<ResponseEntity<String>> createStats(
@PathVariable String owner,
Expand All @@ -39,9 +43,10 @@ public Mono<ResponseEntity<String>> createStats(
}

final InetAddress origin = request.getRemoteAddress().getAddress();
final MediaType contentType = MediaType.TEXT_PLAIN;// determineContentType(request);
final MediaType contentType = determineContentType(request);

final MultipleRepositories multipleRepositories = MultipleRepositories.parse(owner, repositories);
log.info("Request from '{}' to scrape '{}'", gitHubAuthentication, multipleRepositories);

return abuseLimiter.blockAbusers(origin)
.flatMap(__ -> freshResponse(gitHubAuthentication, multipleRepositories, contentType))
Expand All @@ -53,6 +58,10 @@ public Mono<ResponseEntity<String>> createStats(
.body("Your IP '%s' has exceeded the abuse limit\n".formatted(origin))));
}

private MediaType determineContentType(ServerHttpRequest request) {
return serializer.determineMediaType(request.getHeaders().getAccept());
}

private Mono<ResponseEntity<String>> freshResponse(GitHubAuthentication authentication,
MultipleRepositories repositories, MediaType contentType) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,91 @@

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import io.prometheus.client.Collector;
import io.prometheus.client.Collector.MetricFamilySamples;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exporter.common.TextFormat;

@Component
class RegistrySerializer {

private static final MediaType OPEN_METRICS = MediaType
private static final Logger log = LoggerFactory.getLogger(RegistrySerializer.class);

static final MediaType OPEN_METRICS = MediaType
.parseMediaType("application/openmetrics-text; version=1.0.0; charset=utf-8");

static final MediaType FORMAT_004 = MediaType.parseMediaType("text/plain; version=0.0.4; charset=utf-8");

private final List<Serializer> serializers = List.of(new V004(), new OpenMetrics());

public MediaType determineMediaType(Collection<MediaType> acceptibleMediaTypes) {
final MediaType result = serializers.stream()
.filter(serializer -> acceptibleMediaTypes.stream()
.anyMatch(acceptible -> serializer.supportedMediaType().isCompatibleWith(acceptible)))
.findFirst()
.map(Serializer::supportedMediaType)
.orElse(FORMAT_004);

log.debug("Chose '{}' from acceptible meda types {}", result, acceptibleMediaTypes);
return result;
}

public String serializeRegistry(CollectorRegistry registry, MediaType mediaType) {
try (final var stringWriter = new StringWriter()) {
if (mediaType.isCompatibleWith(OPEN_METRICS)) {
TextFormat.writeOpenMetrics100(stringWriter, registry.metricFamilySamples());
} else {
TextFormat.write004(stringWriter, registry.metricFamilySamples());
}
serializers.stream()
.filter(serializer -> serializer.supportedMediaType().equals(mediaType))
.findFirst()
.orElseThrow()
.write(stringWriter, registry.metricFamilySamples());

return stringWriter.toString();
} catch (final IOException e) {
throw new IllegalStateException("Error while serializing registry", e);
}
}

private interface Serializer {

MediaType supportedMediaType();

void write(Writer writer, Enumeration<Collector.MetricFamilySamples> mfs) throws IOException;
}

private static class OpenMetrics implements Serializer {

@Override
public MediaType supportedMediaType() {
return OPEN_METRICS;
}

@Override
public void write(Writer writer, Enumeration<MetricFamilySamples> mfs) throws IOException {
TextFormat.writeOpenMetrics100(writer, mfs);
}

}

private static final class V004 implements Serializer {

@Override
public MediaType supportedMediaType() {
return FORMAT_004;
}

@Override
public void write(Writer writer, Enumeration<MetricFamilySamples> mfs) throws IOException {
TextFormat.write004(writer, mfs);
}

}
}
Loading

0 comments on commit d5083b3

Please sign in to comment.