Compare commits

..

1 Commits

Author SHA1 Message Date
Antoine f5265becec
add r2dbc spring data to support reactive datasource 2020-08-16 01:09:36 +02:00
60 changed files with 48 additions and 1785 deletions

3
.gitignore vendored
View File

@ -1,6 +1,3 @@
.idea/
*.iml
target/
testGit/
application-local.properties

View File

@ -1,29 +0,0 @@
FROM oracle/graalvm-ce:20.2.0-java11 as builder
ADD .mvn/ /build/.mvn
ADD mvnw /build
WORKDIR /build
# Install GraalVM Native Image
RUN gu install native-image; \
./mvnw --version \
native-image --version
COPY pom.xml /build
RUN ./mvnw clean dependency:go-offline
ADD src/ /build/src/
ADD Makefile /build
# download dependencies and compile into layer
RUN ./mvnw -Pgraal,-no-graal compile
# generate graalvm image into another layer
RUN ./mvnw -Pgraal,-no-graal package
#FROM debian:buster
FROM scratch
COPY --from=builder /build/target/wiki-project /wikiproject
#COPY --from=builder /build/target/wiki-project-1.0-SNAPSHOT.jar /wiki-project-1.0-SNAPSHOT.jar
COPY --from=builder /tmp /tmp
ENTRYPOINT ["/wikiproject"]

View File

@ -1,14 +0,0 @@
FROM oracle/graalvm-ce:20.1.0-java11 as builder
WORKDIR /build
VOLUME /build
# Install GraalVM Native Image
RUN gu install native-image; \
native-image --version
ENV RUN_PORT 8080
RUN mkdir -p /build/src/main/resources/META-INF
CMD java -agentlib:native-image-agent=config-output-dir=/build/src/main/resources/META-INF/native-image \
-jar /build/target/wikiProject-1.0-SNAPSHOT.jar --server.port=$ENV_PORT

View File

@ -1,10 +1,4 @@
.PHONY: build run debug docker-build docker-run docker-graal-introspect docker-graal-build docker-graal-run
NATIVE_VERSION=1.0.0
start-runner:
docker run -d --name gitlab-runner --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock \
-v gitlab-runner-config:/etc/gitlab-runner gitlab/gitlab-runner:latest
.PHONY: build
build:
./mvnw compile
@ -21,23 +15,3 @@ docker-build:
docker-run:
docker run --rm -d -p 8080:8080 docker.registry:5000/wikiproject:latest
# this java agent for instrospect java reflexion call is not mandatory, instead spring boot maven plugin do all stuff for us
docker-graal-introspect: RUN_PORT=8080
docker-graal-introspect:
docker build -f ./Dockerfile-agent -t docker.registry:5000/wikiproject-native-introspect .
docker run -p 8080:$(RUN_PORT) -e RUN_PORT=$(RUN_PORT) --rm -v $$(pwd):/build docker.registry:5000/wikiproject-native-introspect
docker-graal-build: NATIVE_VERSION=latest
docker-graal-build:
docker build -t docker.registry:5000/wikiproject-native:$(NATIVE_VERSION) .
docker-graal-release:
docker tag docker.registry:5000/wikiproject-native:latest docker.registry:5000/wikiproject-native:$(NATIVE_VERSION)
docker-graal-push:
docker push docker.registry:5000/wikiproject-native:$(NATIVE_VERSION)
docker-graal-run: RUN_PORT=8080
docker-graal-run:
echo "$(firstword $(MAKECMDGOALS))"
docker run --rm -p 8080:$(RUN_PORT) docker.registry:5000/wikiproject-native --server.port=$(RUN_PORT)

View File

@ -1,40 +0,0 @@
# gitlab runner
## start gitlab runner
```shell script
docker run -d --name gitlab-runner --restart always -v /var/run/docker.sock:/var/run/docker.sock -v gitlab-runner-config:/etc/gitlab-runner gitlab/gitlab-runner:latest
```
## register new runner
```shell script
gitlab-runner register -non-interactive --description "manualy registered gitlab runner" --url "http://172.17.0.1:8080/" --registration-token "3b79eb1f-32f3-4db2-ad1b-6702e476d839" --tag-list "docker,manual" --executor shell
```
with curl
```shell script
curl --request POST -H 'Content-Type: application/json' "http://localhost:8080/api/v4/runners" -d '{"description":"manualy registered gitlab runner","tag_list":"docker,manual","run_untagged":false,"locked":true,"active":true,"info":{"name":"gitlab-runner","version":"13.3.1","revision":"738bbe5a","platform":"linux","architecture":"amd64","features":{"variables":false,"image":false,"services":false,"artifacts":false,"cache":false,"shared":false,"upload_multiple_artifacts":false,"upload_raw_artifacts":false,"session":false,"terminal":false,"refspecs":false,"masking":false,"proxy":false,"raw_variables":false,"artifacts_exclude":false,"multi_build_steps":false}},"token":"registration_token"}'
```
## unregister runner
```shell script
gitlab-runner unregister --url "http://172.17.0.1:8080/" --token "3b79eb1f-32f3-4db2-ad1b-6702e476d839"
```
## list runners
```shell script
gitlab-runner list
```
local listing :
cf => /etc/gitlab-runner/config.toml
## verify which runner is always alive
```shell script
gitlab-runner verify --delete
```

View File

@ -1,89 +0,0 @@
# issues
## with undertow
### during native-image
```
Fatal error:com.oracle.graal.pointsto.util.AnalysisError$ParsingError: Error encountered while parsing io.undertow.servlet.spec.ServletPrintWriterDelegate.newInstance(io.undertow.servlet.spec.ServletPrintWriter)
Parsing context:
parsing io.undertow.servlet.spec.HttpServletResponseImpl.resetBuffer(HttpServletResponseImpl.java:556)
parsing javax.servlet.ServletResponseWrapper.resetBuffer(ServletResponseWrapper.java:190)
parsing io.undertow.servlet.spec.RequestDispatcherImpl.forwardImpl(RequestDispatcherImpl.java:169)
parsing io.undertow.servlet.spec.RequestDispatcherImpl.forwardImplSetup(RequestDispatcherImpl.java:149)
parsing io.undertow.servlet.spec.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:111)
parsing org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:171)
parsing org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:316)
parsing org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1373)
parsing com.oracle.svm.reflect.DispatcherServlet_render_e72506965c502aea2056a5f4489afdcb7851c1bf_1141.invoke(Unknown Source)
parsing java.lang.reflect.Method.invoke(Method.java:566)
parsing org.springframework.core.convert.support.ObjectToObjectConverter.convert(ObjectToObjectConverter.java:102)
parsing org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41)
parsing org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:191)
parsing org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:174)
parsing org.springframework.core.env.AbstractPropertyResolver.convertValueIfNecessary(AbstractPropertyResolver.java:265)
parsing org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:91)
parsing org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:62)
parsing org.springframework.core.env.AbstractPropertyResolver.getProperty(AbstractPropertyResolver.java:169)
parsing org.springframework.core.env.AbstractEnvironment.getProperty(AbstractEnvironment.java:540)
parsing org.springframework.boot.SpringApplicationBannerPrinter.createStringFromBanner(SpringApplicationBannerPrinter.java:116)
parsing org.springframework.boot.SpringApplicationBannerPrinter.print(SpringApplicationBannerPrinter.java:61)
parsing org.springframework.boot.SpringApplication.printBanner(SpringApplication.java:556)
parsing org.springframework.boot.SpringApplication.run(SpringApplication.java:310)
parsing org.springframework.boot.SpringApplication.run(SpringApplication.java:1237)
parsing org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
parsing tk.antoine_roux.wiki.MainLauncher.main(MainLauncher.java:25)
parsing com.oracle.svm.core.JavaMainWrapper.runCore(JavaMainWrapper.java:149)
parsing com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:184)
parsing com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)
at com.oracle.graal.pointsto.util.AnalysisError.parsingError(AnalysisError.java:138)
at com.oracle.graal.pointsto.flow.MethodTypeFlow.doParse(MethodTypeFlow.java:340)
at com.oracle.graal.pointsto.flow.MethodTypeFlow.ensureParsed(MethodTypeFlow.java:311)
at com.oracle.graal.pointsto.flow.MethodTypeFlow.addContext(MethodTypeFlow.java:112)
at com.oracle.graal.pointsto.flow.StaticInvokeTypeFlow.update(InvokeTypeFlow.java:437)
at com.oracle.graal.pointsto.BigBang$2.run(BigBang.java:530)
at com.oracle.graal.pointsto.util.CompletionExecutor.lambda$execute$0(CompletionExecutor.java:173)
at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
```
## with spring-boot-devtools
```
docker run --rm docker.registry:5000/wikiproject-native
Exception in thread "main" java.lang.IllegalArgumentException: Cannot instantiate interface org.springframework.context.ApplicationListener : org.springframework.boot.devtools.logger.DevToolsLogFactory.Listener
at org.springframework.boot.SpringApplication.createSpringFactoriesInstances(SpringApplication.java:445)
at org.springframework.boot.SpringApplication.getSpringFactoriesInstances(SpringApplication.java:427)
at org.springframework.boot.SpringApplication.getSpringFactoriesInstances(SpringApplication.java:420)
at org.springframework.boot.SpringApplication.<init>(SpringApplication.java:273)
at org.springframework.boot.SpringApplication.<init>(SpringApplication.java:253)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
at tk.antoine_roux.wiki.MainLauncher.main(MainLauncher.java:25)
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.devtools.logger.DevToolsLogFactory.Listener
at com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:60)
at java.lang.Class.forName(DynamicHub.java:1207)
at org.springframework.util.ClassUtils.forName(ClassUtils.java:284)
at org.springframework.boot.SpringApplication.createSpringFactoriesInstances(SpringApplication.java:438)
... 7 more
make: *** [Makefile:22: docker-graal-run] Error 1
```
## during compile
```
Warning: class initialization of class org.springframework.boot.validation.MessageInterpolatorFactory failed with exception java.lang.NoClassDefFoundError: javax/validation/ValidationException. This class will be initialized at run time because option --allow-incomplete-classpath is used for image building. Use the option --initialize-at-run-time=org.springframework.boot.validation.MessageInterpolatorFactory to explicitly request delayed initialization of this class.
Number of types dynamically registered for reflective access: #2024
```
## error class not found
```
WARNING: Could not resolve org.springframework.boot.devtools.logger.DevToolsLogFactory.Listener for reflection configuration.
WARNING: Could not resolve javax.validation.ValidationException for reflection configuration.
```

View File

@ -1,16 +0,0 @@
[PATCH /api/v4/jobs/0/trace HTTP/1.1
Host: 172.17.0.1:8080
User-Agent: gitlab-runner 13.3.1 (13-3-stable; go1.13.8; linux/amd64)
Content-Length: 363
Content-Range: 0-362
Content-Type: text/plain
Job-Token:
Accept-Encoding: gzip]
Running with gitlab-runner 13.3.1 (738bbe5a)
on manualy registered gitlab runner f336d593
Preparing the "shell" executor
Using Shell executor...
Preparing environment
Running on 94afd2dbc667...
ERROR: Job failed: panic: runtime error: slice bounds out of range [:8] with length 0

View File

@ -1,37 +0,0 @@
[POST /api/v4/jobs/request HTTP/1.1
Host: 172.17.0.1:8080
User-Agent: gitlab-runner 13.3.1 (13-3-stable; go1.13.8; linux/amd64)
Content-Length: 510
Accept: application/json
Content-Type: application/json
Accept-Encoding: gzip]
{
"info":{
"name":"gitlab-runner",
"version":"13.3.1",
"revision":"738bbe5a",
"platform":"linux",
"architecture":"amd64",
"executor":"shell",
"shell":"bash",
"features":{
"variables":true,
"image":false,
"services":false,
"artifacts":true,
"cache":true,
"shared":true,
"upload_multiple_artifacts":true,
"upload_raw_artifacts":true,
"session":true,
"terminal":true,
"refspecs":true,
"masking":true,
"proxy":false,
"raw_variables":true,
"artifacts_exclude":true,
"multi_build_steps":true
}
},
"token":"76a79b73-b211-48c6-a3da-6b99fb8b0612"
}

View File

@ -1,40 +0,0 @@
[POST /api/v4/runners HTTP/1.1
Host: 172.17.0.1:8080
User-Agent: gitlab-runner 13.3.1 (13-3-stable; go1.13.8; linux/amd64)
Content-Length: 510
Accept: application/json
Content-Type: application/json
Accept-Encoding: gzip]
{
"description": "manualy registered gitlab runner",
"tag_list": "docker,manual",
"run_untagged": false,
"locked": true,
"active": true,
"info": {
"name": "gitlab-runner",
"version": "13.3.1",
"revision": "738bbe5a",
"platform": "linux",
"architecture": "amd64",
"features": {
"variables": false,
"image": false,
"services": false,
"artifacts": false,
"cache": false,
"shared": false,
"upload_multiple_artifacts": false,
"upload_raw_artifacts": false,
"session": false,
"terminal": false,
"refspecs": false,
"masking": false,
"proxy": false,
"raw_variables": false,
"artifacts_exclude": false,
"multi_build_steps": false
}
},
"token": "registration_token"
}

View File

@ -1,8 +0,0 @@
[POST /api/v4/runners/verify HTTP/1.1
Host: 172.17.0.1:8080
User-Agent: gitlab-runner 13.3.1 (13-3-stable; go1.13.8; linux/amd64)
Content-Length: 48
Content-Type: application/json
Accept-Encoding: gzip]
{"token":"19b280e8-9c56-4566-8234-7a172d717be3"}

View File

@ -1,115 +0,0 @@
{
"secret": "toto",
"ref": "refs/heads/master",
"before": "1831a5d892851ec601c3f1b6c26245f18a9bcb58",
"after": "526300af0655f2eeb43c6dc553be4b64d96701c7",
"compare_url": "https://antoine-roux.ml/projects/antoine/testGit/compare/1831a5d892851ec601c3f1b6c26245f18a9bcb58...526300af0655f2eeb43c6dc553be4b64d96701c7",
"commits": [
{
"id": "526300af0655f2eeb43c6dc553be4b64d96701c7",
"message": "test trigger webhook\n",
"url": "https://antoine-roux.ml/projects/antoine/testGit/commit/526300af0655f2eeb43c6dc553be4b64d96701c7",
"author": {
"name": "Antoine",
"email": "antoinroux@hotmail.fr",
"username": "antoine"
},
"committer": {
"name": "Antoine",
"email": "antoinroux@hotmail.fr",
"username": "antoine"
},
"verification": null,
"timestamp": "2020-09-16T23:23:16+02:00",
"added": [
"toto"
],
"removed": [],
"modified": []
}
],
"head_commit": null,
"repository": {
"id": 124,
"owner": {
"id": 1,
"login": "antoine",
"full_name": "",
"email": "antoinroux@hotmail.fr",
"avatar_url": "https://antoine-roux.ml/projects/user/avatar/antoine/-1",
"language": "fr-FR",
"is_admin": true,
"last_login": "2020-09-15T21:45:21Z",
"created": "2018-08-26T06:16:47Z",
"username": "antoine"
},
"name": "testGit",
"full_name": "antoine/testGit",
"description": "repository for test some git feature",
"empty": false,
"private": true,
"fork": false,
"template": false,
"parent": null,
"mirror": false,
"size": 24,
"html_url": "https://antoine-roux.ml/projects/antoine/testGit",
"ssh_url": "gitolite@antoine-roux.ml:antoine/testGit.git",
"clone_url": "https://antoine-roux.ml/projects/antoine/testGit.git",
"original_url": "",
"website": "",
"stars_count": 0,
"forks_count": 0,
"watchers_count": 1,
"open_issues_count": 0,
"open_pr_counter": 0,
"release_counter": 0,
"default_branch": "master",
"archived": false,
"created_at": "2018-11-05T22:35:38Z",
"updated_at": "2020-09-16T21:23:48Z",
"permissions": {
"admin": true,
"push": true,
"pull": true
},
"has_issues": true,
"internal_tracker": {
"enable_time_tracker": true,
"allow_only_contributors_to_track_time": true,
"enable_issue_dependencies": true
},
"has_wiki": true,
"has_pull_requests": true,
"ignore_whitespace_conflicts": false,
"allow_merge_commits": true,
"allow_rebase": true,
"allow_rebase_explicit": false,
"allow_squash_merge": false,
"avatar_url": ""
},
"pusher": {
"id": 1,
"login": "antoine",
"full_name": "",
"email": "antoinroux@hotmail.fr",
"avatar_url": "https://antoine-roux.ml/projects/user/avatar/antoine/-1",
"language": "fr-FR",
"is_admin": true,
"last_login": "2020-09-15T21:45:21Z",
"created": "2018-08-26T06:16:47Z",
"username": "antoine"
},
"sender": {
"id": 1,
"login": "antoine",
"full_name": "",
"email": "antoinroux@hotmail.fr",
"avatar_url": "https://antoine-roux.ml/projects/user/avatar/antoine/-1",
"language": "fr-FR",
"is_admin": true,
"last_login": "2020-09-15T21:45:21Z",
"created": "2018-08-26T06:16:47Z",
"username": "antoine"
}
}

140
pom.xml
View File

@ -8,33 +8,25 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0-M2</version>
<!-- <version>2.3.2.RELEASE</version>-->
<relativePath/> <!-- lookup parent from repository -->
<version>2.3.2.RELEASE</version>
</parent>
<groupId>tk.antoine-roux</groupId>
<artifactId>wiki-project</artifactId>
<groupId>fr.antoine-roux</groupId>
<artifactId>wikiProject</artifactId>
<version>${revision}</version>
<name>wiki-project</name>
<name>wikiProject</name>
<description>project use wikipedia streaming api https://stream.wikimedia.org/?doc#/Streams/get_v2_stream_test
</description>
<properties>
<revision>1.0-SNAPSHOT</revision>
<java.version>11</java.version>
<java.version>14</java.version>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<jib-maven-plugin.version>2.5.2</jib-maven-plugin.version>
<spring-boot.run.jvmArguments/>
<application.port>8080</application.port>
<resource.delimiter>^</resource.delimiter>
<enable-preview.jvmFlag/>
<!-- <enable-preview.jvmFlag>&#45;&#45;enable-preview</enable-preview.jvmFlag>-->
<start-class>tk.antoine_roux.wiki.MainLauncher</start-class>
<spring-graalvm-native.version>0.8.0</spring-graalvm-native.version>
<native-image-maven-plugin.version>20.2.0</native-image-maven-plugin.version>
<jackson-datatype.version>2.11.2</jackson-datatype.version>
<org.eclipse.jgit.version>5.9.0.202009080501-r</org.eclipse.jgit.version>
<enable-preview.jvmFlag>--enable-preview</enable-preview.jvmFlag>
</properties>
<dependencies>
@ -46,33 +38,30 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- <exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>-->
</dependency>
<!-- <dependency>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>-->
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>${org.eclipse.jgit.version}</version>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
</dependency>
</dependencies>
@ -127,91 +116,4 @@
</plugins>
</build>
<profiles>
<profile>
<id>no-graal</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
</dependencies>
</profile>
<profile>
<id>graal</id>
<dependencies>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-graalvm-native</artifactId>
<version>${spring-graalvm-native.version}</version>
</dependency>
<!-- generate target/classes/META_INF/spring.components with list of application's spring component class -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<version>${native-image-maven-plugin.version}</version>
<configuration>
<!-- doc here : https://repo.spring.io/milestone/org/springframework/experimental/spring-graalvm-native-docs/0.7.1/spring-graalvm-native-docs-0.7.1.zip!/reference/index.html#options -->
<buildArgs>
-H:+PrintAnalysisCallTree
-H:+TraceClassInitialization
<!-- -H:+ReportExceptionStackTraces-->
<!-- reduce build time and memory -->
<!-- -H:+RemoveSaturatedTypeFlows-->
--allow-incomplete-classpath
--report-unsupported-elements-at-runtime
--no-fallback
--no-server
--verbose
<!-- &#45;&#45;initialize-at-build-time=org.springframework.boot.validation.MessageInterpolatorFactory-->
-J-Xmx3G --static
</buildArgs>
<imageName>${project.artifactId}</imageName>
</configuration>
<executions>
<execution>
<goals>
<goal>native-image</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<repositories>
<!-- spring repository for milestone artifact -->
<repository>
<id>spring-milestone</id>
<name>Spring milestone</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestone</id>
<name>Spring milestone</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
</project>

View File

@ -1,5 +0,0 @@
# Gitlab runner gateway
This aims of this repository is to catch gitea webhook, parse .gitlab-ci.yml file and run some job with gitlab-runner from docker container. Some documentation about [gitlab-runner](./misc/doc-gitlab-runner.md)
This API could be compile with graalVM to native image see [Makefile](./Makefile)

View File

@ -1,141 +0,0 @@
package tk.antoine_roux.wiki;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import tk.antoine_roux.wiki.annotation.ApiPrefix;
import tk.antoine_roux.wiki.annotation.ApiVersion;
import tk.antoine_roux.wiki.configuration.Exception.DeleteRunnerException;
import tk.antoine_roux.wiki.model.internal.GitlabCI;
import tk.antoine_roux.wiki.model.internal.Runner;
import tk.antoine_roux.wiki.model.request.AddRunner;
import tk.antoine_roux.wiki.model.request.HookEvent;
import tk.antoine_roux.wiki.model.request.JobRequest;
import tk.antoine_roux.wiki.model.request.TokenRunner;
import tk.antoine_roux.wiki.model.response.JobResponse;
import tk.antoine_roux.wiki.model.response.RegisterRunnerResponse;
import tk.antoine_roux.wiki.service.GitService;
import tk.antoine_roux.wiki.service.JobManager;
import tk.antoine_roux.wiki.service.RunnerRegistrar;
import tk.antoine_roux.wiki.utilitary.Boolean;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import static tk.antoine_roux.wiki.utilitary.Constant.*;
@RestController
@ApiPrefix(API_PREFIX)
public class ControllerHandlers {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final RunnerRegistrar runnerRegistrar;
private final JobManager jobManager;
private final GitService gitService;
@Autowired
public ControllerHandlers(RunnerRegistrar runnerRegistrar, JobManager jobManager, GitService gitService) {
this.runnerRegistrar = runnerRegistrar;
this.jobManager = jobManager;
this.gitService = gitService;
}
/**
* hello test endpoint
*
* @return
*/
@ResponseBody
@GetMapping("/info")
public ResponseEntity<TreeMap<String, String>> info() {
return ResponseEntity.ok(
// sort attribute by key name
new TreeMap<>(
Map.of(
"api-version", VERSION_PREFIX + API_VERSION,
"api-name", API_NAME
)
)
);
}
/**
* add register new runners
*
* @param body
* @return
*/
@ResponseBody
@ApiVersion({API_VERSION})
@PostMapping("/runners")
public ResponseEntity<RegisterRunnerResponse> addRunner(@RequestBody AddRunner body) {
logger.debug("Receive register runner request " + body);
Runner createdRunner = this.runnerRegistrar.addRunner(body);
RegisterRunnerResponse response = createdRunner.toRegisterResponse();
logger.debug("Response register runner " + response);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}
@ApiVersion(API_VERSION)
@DeleteMapping("/runners")
public ResponseEntity<Void> deleteRunner(@RequestBody TokenRunner body) throws Exception {
logger.debug("Receive delete runner request " + body);
Boolean.trueOrElseThrow(this.runnerRegistrar.removeRunnerByRegistrationToken(body), DeleteRunnerException::new);
logger.debug("Successfully delete runner ");
return ResponseEntity.noContent().build();
}
@ApiVersion(API_VERSION)
@PostMapping("/runners/verify")
public ResponseEntity<Void> runnerExist(@RequestBody TokenRunner body) {
boolean isValid = this.runnerRegistrar.checkRunner(body);
if (isValid) {
return ResponseEntity.ok().build();
} else {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
}
@ApiVersion(API_VERSION)
@PostMapping("/jobs/request")
public ResponseEntity<JobResponse> jobRequest(@RequestBody JobRequest jobRequest) {
Optional<JobResponse> currentJob = this.jobManager.popJob(jobRequest);
return currentJob
.map(job -> ResponseEntity.status(HttpStatus.CREATED).body(job))
.orElseGet(() -> ResponseEntity.noContent().build());
}
@ApiVersion(API_VERSION)
@PatchMapping("jobs/0/trace")
public ResponseEntity<Void> receiveTrace(@RequestBody String traceContent) {
logger.info(traceContent);
return ResponseEntity.status(HttpStatus.CREATED).build();
}
@ApiVersion(API_VERSION)
@PostMapping("/webhook")
public ResponseEntity<Void> webhook(@RequestBody HookEvent webHookData) throws IOException, GitAPIException {
ResponseEntity.BodyBuilder responseEntity;
Optional<GitlabCI> gitlabCI = this.gitService.getYMLGitlabCI(webHookData);
if (gitlabCI.isPresent()) {
JobResponse job = gitlabCI.get().buildJobResponse();
this.jobManager.stackJob(job);
responseEntity = ResponseEntity.ok();
} else {
responseEntity = ResponseEntity.unprocessableEntity();
}
return responseEntity.build();
}
}

View File

@ -1,21 +1,23 @@
package tk.antoine_roux.wiki;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.RouterFunctions;
import org.springframework.web.servlet.function.ServerResponse;
import tk.antoine_roux.wiki.configuration.GitConfiguration;
/**
* Main class
*/
// force spring application to not use glibc or any non jdk code which is bad for graalvm
@SpringBootApplication(proxyBeanMethods = false)
@EnableConfigurationProperties(GitConfiguration.GitlabCIContextProperties.class)
public class MainLauncher {
@Value("${wikiproject.basePath}")
private String basePath;
/**
* Entrypoint for application
*/
@ -26,11 +28,11 @@ public class MainLauncher {
/**
* Routing declaration
*/
// @Bean
@Bean
public RouterFunction<ServerResponse> routes() {
return RouterFunctions.route()
// .POST("/runners", accept(APPLICATION_FORM_URLENCODED), ControllerHandlers::addRunner)
// .GET("/hello", ControllerHandlers::hello)
.build();
.GET(this.basePath + "/hello", serverRequest ->
ServerResponse.ok().body("Hello world !")
).build();
}
}

View File

@ -1,12 +0,0 @@
package tk.antoine_roux.wiki.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiPrefix {
String value() default "";
}

View File

@ -1,12 +0,0 @@
package tk.antoine_roux.wiki.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiVersion {
String[] value() default {""};
}

View File

@ -1,91 +0,0 @@
package tk.antoine_roux.wiki.configuration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.servlet.mvc.condition.*;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import tk.antoine_roux.wiki.annotation.ApiPrefix;
import tk.antoine_roux.wiki.annotation.ApiVersion;
import java.lang.reflect.Method;
/**
* create custom {@link org.springframework.web.bind.annotation.RequestMapping} Handler to add versioning into uri
*/
public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
private final String versionPrefix;
/**
* create @{@link org.springframework.web.bind.annotation.RequestMapping} handler classe
* this class add some prefix to uri
*
* @param versionPrefix
*/
public ApiVersionRequestMappingHandlerMapping(String versionPrefix) {
this.versionPrefix = versionPrefix;
}
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = super.getMappingForMethod(method, handlerType);
if (info == null) {
return null;
}
ApiVersion methodAnnotation = AnnotationUtils.findAnnotation(method, ApiVersion.class);
if (methodAnnotation != null) {
RequestCondition<?> methodCondition = this.getCustomMethodCondition(method);
// Concatenate our ApiVersion with the usual request mapping
info = this.createApiVersionInfo(methodAnnotation, methodCondition).combine(info);
} else {
ApiVersion typeAnnotation = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
if (typeAnnotation != null) {
RequestCondition<?> typeCondition = this.getCustomTypeCondition(handlerType);
// Concatenate our ApiVersion with the usual request mapping
info = this.createApiVersionInfo(typeAnnotation, typeCondition).combine(info);
}
}
ApiPrefix annotationApiPrefix = AnnotationUtils.findAnnotation(handlerType, ApiPrefix.class);
if (annotationApiPrefix != null) {
RequestMappingInfo requestMappingInfo = new RequestMappingInfo(
new PatternsRequestCondition(
new String[]{annotationApiPrefix.value()},
this.getUrlPathHelper(), this.getPathMatcher(),
false
),
new RequestMethodsRequestCondition(),
new ParamsRequestCondition(),
new HeadersRequestCondition(),
new ConsumesRequestCondition(),
new ProducesRequestCondition(),
null);
info = requestMappingInfo.combine(info);
}
return info;
}
private RequestMappingInfo createApiVersionInfo(ApiVersion annotation, RequestCondition<?> customCondition) {
String[] values = annotation.value();
String[] patterns = new String[values.length];
for (int i = 0; i < values.length; i++) {
// Build the URL prefix
patterns[i] = this.versionPrefix + values[i];
}
return new RequestMappingInfo(
new PatternsRequestCondition(
patterns, this.getUrlPathHelper(), this.getPathMatcher(),
false
// , this.useTrailingSlashMatch(), this.getFileExtensions()
),
new RequestMethodsRequestCondition(),
new ParamsRequestCondition(),
new HeadersRequestCondition(),
new ConsumesRequestCondition(),
new ProducesRequestCondition(),
customCondition);
}
}

View File

@ -1,39 +0,0 @@
package tk.antoine_roux.wiki.configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import tk.antoine_roux.wiki.configuration.Exception.DeleteRunnerException;
import tk.antoine_roux.wiki.configuration.Exception.NoIdException;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Custom http handler for dealing with {@link Exception}
*/
@ControllerAdvice
public class ControllerExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler({NoIdException.class})
public ResponseEntity<Object> handleInternalException(NoIdException ex, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("message", ex.getMessage());
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler({DeleteRunnerException.class})
public ResponseEntity<Object> handleNotFoundException(NoIdException ex, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("message", ex.getMessage());
return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
}
}

View File

@ -0,0 +1,15 @@
package tk.antoine_roux.wiki.configuration;
import io.r2dbc.spi.ConnectionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.r2dbc.core.DatabaseClient;
@Configuration
public class DatabaseConfiguration {
@Bean
public DatabaseClient getDatabase(ConnectionFactory connectionFactory) {
return DatabaseClient.create(connectionFactory);
}
}

View File

@ -1,9 +0,0 @@
package tk.antoine_roux.wiki.configuration.Exception;
public class DeleteRunnerException extends RuntimeException {
private static final long serialVersionUID = 559919382888691526L;
public DeleteRunnerException() {
super("Fail to delete runner");
}
}

View File

@ -1,9 +0,0 @@
package tk.antoine_roux.wiki.configuration.Exception;
public class InvalidObjectIdException extends RuntimeException {
private static final long serialVersionUID = 6274838148439186894L;
public InvalidObjectIdException(String commitStr) {
super("Try to convert invalid string (" + commitStr + ") to commit id");
}
}

View File

@ -1,9 +0,0 @@
package tk.antoine_roux.wiki.configuration.Exception;
public class NoIdException extends RuntimeException {
private static final long serialVersionUID = 1470945508482780554L;
public NoIdException() {
super("Invalid id value");
}
}

View File

@ -1,50 +0,0 @@
package tk.antoine_roux.wiki.configuration;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Configuration(proxyBeanMethods = false)
public class GitConfiguration {
@Bean
public YAMLFactory yamlFactory() {
return YAMLFactory.builder().build();
}
@Bean(name = "YAMLObjectMapper")
public ObjectMapper ObjectMapper(YAMLFactory yamlFactory) {
return new ObjectMapper(yamlFactory);
}
@Bean
public UsernamePasswordCredentialsProvider credentialsProvider(GitlabCIContextProperties gitlabCIContextProperties, Environment environment) {
return new UsernamePasswordCredentialsProvider(gitlabCIContextProperties.getUsername(), gitlabCIContextProperties.getPassword());
}
@ConfigurationProperties("gitlab-ci")
public static class GitlabCIContextProperties {
private String username;
private String password;
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
}
}

View File

@ -1,40 +0,0 @@
package tk.antoine_roux.wiki.configuration;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import tk.antoine_roux.wiki.utilitary.Constant;
/**
* spring web configuration
*
* @see org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations
*/
@Configuration(proxyBeanMethods = false)
public class WebConfiguration implements WebMvcRegistrations {
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new ApiVersionRequestMappingHandlerMapping(Constant.VERSION_PREFIX);
}
/**
* build default Spring boot {@link ObjectMapper}
* this bean avoid spring boot to auto detect {@link GitConfiguration#ObjectMapper(YAMLFactory)}
* as default {@link ObjectMapper}
*
* @param jackson2ObjectMapperBuilder
* @return
*/
@Bean
@Primary
public ObjectMapper jackson2ObjectMapperBuilder(Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder) {
return jackson2ObjectMapperBuilder.build();
}
}

View File

@ -1,46 +0,0 @@
package tk.antoine_roux.wiki.model.internal;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import tk.antoine_roux.wiki.model.response.JobResponse;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class GitlabCI {
public String image;
@JsonProperty("before_script")
public List<String> beforeScript = Collections.emptyList();
@JsonProperty("after_script")
public List<String> afterScript = Collections.emptyList();
public Map<String, Job> jobs = Collections.emptyMap();
@JsonAnySetter
public void setJobs(String key, Job value) {
this.jobs.put(key, value);
}
public JobResponse buildJobResponse() {
return null;
}
public enum RuleEnum {
IF("if");
String content;
RuleEnum(String s) {
this.content = s;
}
}
private static class Job {
public String stage;
public String image;
public Map<String, String> variables = Collections.emptyMap();
public List<String> services = Collections.emptyList();
public Map<RuleEnum, String> rules = Collections.emptyMap();
public List<String> script = Collections.emptyList();
}
}

View File

@ -1,21 +0,0 @@
package tk.antoine_roux.wiki.model.internal;
import tk.antoine_roux.wiki.model.response.RegisterRunnerResponse;
import java.util.List;
import java.util.UUID;
/**
* in memory representation of gitlab runner
*/
public class Runner {
public String description;
public List<String> tags;
public UUID registrationToken;
public UUID authenticationToken;
public Integer id;
public RegisterRunnerResponse toRegisterResponse() {
return new RegisterRunnerResponse(String.valueOf(this.id), this.authenticationToken.toString());
}
}

View File

@ -1,29 +0,0 @@
package tk.antoine_roux.wiki.model.request;
import com.fasterxml.jackson.annotation.JsonProperty;
import tk.antoine_roux.wiki.model.request.secondary.RunnerInfo;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
/**
* Model use to add runner instance
*/
public class AddRunner {
private static final String TAG_SEPARATOR = ",";
public String description;
public List<String> tags;
public UUID token;
@JsonProperty("run_untagged")
public Boolean runUntagged;
public Boolean locked;
public Boolean active;
public RunnerInfo info;
@JsonProperty("tag_list")
public void setTags(String tags) {
this.tags = Arrays.asList(tags.split(TAG_SEPARATOR).clone());
}
}

View File

@ -1,55 +0,0 @@
package tk.antoine_roux.wiki.model.request;
import com.fasterxml.jackson.annotation.JsonProperty;
import tk.antoine_roux.wiki.model.request.secondary.Commit;
import tk.antoine_roux.wiki.model.request.secondary.Repository;
import tk.antoine_roux.wiki.model.request.secondary.User;
import java.util.List;
public class HookEvent {
public String secret;
public String ref;
public String before;
public String after;
@JsonProperty("compare_url")
public String compareUrl;
public List<Commit> commits;
@JsonProperty("head_commit")
public String headCommit;
public Repository repository;
public User pusher;
public User sender;
// AtomicInteger idIncrementer = new AtomicInteger();
//
// /**
// * convert {@link HookEvent} to {@link Job} if possible
// * else return and {@link Optional#empty()}
// */
// public Optional<Job> toJob() {
// Optional<Job> optJob;
//
// if (this.commits.isEmpty()) {
// optJob = Optional.empty();
// } else {
// // search for head commit or take first in event's list of commit
// Commit commit = this.commits.stream().filter(co -> co.id.equals(this.headCommit))
// .findFirst().orElse(this.commits.get(0));
//
// Job.Commit co = new Job.Commit(
// commit.author.email, commit.author.name, commit.timestamp,
// commit.id, commit.message, commit.id.substring(0, 8), commit.message
// );
//
// Job job = new Job(
// null, co, null, ZonedDateTime.now(), null,
// this.idIncrementer.getAndIncrement(), UUID.randomUUID().toString(), this.ref,
// null, "root", null, JobStatus.CREATED, false, this.pusher.toReducedUser()
// );
// optJob = Optional.of(job);
// }
//
// return optJob;
// }
}

View File

@ -1,10 +0,0 @@
package tk.antoine_roux.wiki.model.request;
import tk.antoine_roux.wiki.model.request.secondary.RunnerInfo;
/**
* Job Request compose from {@link RunnerInfo} and token field
*/
public class JobRequest extends TokenRunner {
public RunnerInfo info;
}

View File

@ -1,10 +0,0 @@
package tk.antoine_roux.wiki.model.request;
import java.util.UUID;
/**
* Model use to remove runner instance
*/
public class TokenRunner {
public UUID token;
}

View File

@ -1,22 +0,0 @@
package tk.antoine_roux.wiki.model.request.secondary;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.ZonedDateTime;
import java.util.List;
public class Commit {
public String id;
public String message;
public String url;
public UserReduced author;
public UserReduced committer;
public String verification;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
public ZonedDateTime timestamp;
public List<String> added;
public List<String> removed;
public List<String> modified;
}

View File

@ -1,15 +0,0 @@
package tk.antoine_roux.wiki.model.request.secondary;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* model representation of gitea tracker
*/
public class InternalTracker {
@JsonProperty("enable_time_tracker")
public boolean enableTimeTracker;
@JsonProperty("allow_only_contributors_to_track_time")
public boolean allowOnlyContributorsToTrackTime;
@JsonProperty("enable_issue_dependencies")
public boolean enableIssueDependencies;
}

View File

@ -1,10 +0,0 @@
package tk.antoine_roux.wiki.model.request.secondary;
/**
* model about repository permission
*/
public class Permission {
public boolean admin;
public boolean push;
public boolean pull;
}

View File

@ -1,71 +0,0 @@
package tk.antoine_roux.wiki.model.request.secondary;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.ZonedDateTime;
public class Repository {
public String id;
public User owner;
public String name;
@JsonProperty("full_name")
public String fullName;
public String description;
public boolean empty;
@JsonProperty("private")
public boolean isPrivate;
public boolean fork;
public boolean template;
public String parent;
public boolean mirror;
public int size;
@JsonProperty("html_url")
public String htmlUrl;
@JsonProperty("ssh_url")
public String sshUrl;
@JsonProperty("clone_url")
public String cloneUrl;
@JsonProperty("original_url")
public String originalUrl;
public String website;
@JsonProperty("stars_count")
public int starsCount;
@JsonProperty("forks_count")
public int forksCount;
@JsonProperty("watchers_count")
public int watchersCount;
@JsonProperty("open_issues_count")
public int openIssuesCount;
@JsonProperty("open_pr_count")
public int openPRCount;
@JsonProperty("release_counter")
public int releaseCounter;
@JsonProperty("default_branch")
public String defaultBranch;
public boolean archived;
@JsonProperty("created_at")
public ZonedDateTime createdAt;
@JsonProperty("updated_at")
public ZonedDateTime updatedAt;
public Permission permissions;
@JsonProperty("has_issues")
public boolean hasIssues;
@JsonProperty("internal_tracker")
public InternalTracker internalTracker;
@JsonProperty("has_wiki")
public boolean hasWiki;
@JsonProperty("has_pull_requests")
public boolean hasPullRequests;
@JsonProperty("ignore_whitespace_conflicts")
public boolean ignoreWhitespaceConflicts;
@JsonProperty("allow_merge_commits")
public boolean allowMergeCommits;
@JsonProperty("allow_rebase")
public boolean allowRebase;
@JsonProperty("allow_rebase_explicit")
public boolean allowRebaseExplicit;
@JsonProperty("allow_squash_merge")
public boolean allowSquashMerge;
@JsonProperty("avatar_url")
public String avatarUrl;
}

View File

@ -1,37 +0,0 @@
package tk.antoine_roux.wiki.model.request.secondary;
import java.util.Map;
public class RunnerInfo {
public String name;
public String version;
public String revision;
public String platform;
public String architecture;
public String executor;
public String shell;
/**
* features can contains :
* <ul>
* <li>variables</li>
* <li>image</li>
* <li>services</li>
* <li>artifacts</li>
* <li>cache</li>
* <li>shared</li>
* <li>upload_multiple_artifacts</li>
* <li>upload_raw_artifacts</li>
* <li>session</li>
* <li>terminal</li>
* <li>refspecs</li>
* <li>masking</li>
* <li>proxy</li>
* <li>raw_variables</li>
* <li>artifacts_exclude</li>
* <li>multi_build_steps</li>
* </ul>
*/
public Map<String, Boolean> features;
}

View File

@ -1,29 +0,0 @@
package tk.antoine_roux.wiki.model.request.secondary;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.ZonedDateTime;
/**
* pusher sender user information
*/
public class User {
public String id;
public String login;
public String username;
@JsonProperty("full_name")
public String fullName;
public String email;
@JsonProperty("avatar_url")
public String avatarUrl;
public String language;
@JsonProperty("is_admin")
public boolean isAdmin;
@JsonProperty("last_login")
public ZonedDateTime lastLogin;
public ZonedDateTime created;
public UserReduced toReducedUser() {
return new UserReduced(this.login, this.email, this.username);
}
}

View File

@ -1,16 +0,0 @@
package tk.antoine_roux.wiki.model.request.secondary;
/**
* Reduced user information
*/
public class UserReduced {
public String name;
public String email;
public String username;
public UserReduced(String name, String email, String username) {
this.name = name;
this.email = email;
this.username = username;
}
}

View File

@ -1,33 +0,0 @@
package tk.antoine_roux.wiki.model.response;
import com.fasterxml.jackson.annotation.JsonProperty;
import tk.antoine_roux.wiki.model.response.secondary.*;
import java.util.List;
import java.util.Map;
/**
* job format use to submit
* information to gitlab-runner
*/
public class JobResponse {
public Integer id;
public String token;
@JsonProperty("allow_git_fetch")
public boolean allowGitFetch;
@JsonProperty("job_info")
public JobInfo jobInfo;
@JsonProperty("git_info")
public GitInfo gitInfo;
public RunnerInfo runnerInfo;
public List<JobVariable> variables;
public List<Step> steps;
public Image image;
public List<Image> services;
public List<Artifact> artifacts;
public List<Cache> cache;
public List<Credential> credentials;
public List<Dependency> dependencies;
public GitLabFeatures features;
public Map<String, Secret> secrets;
}

View File

@ -1,18 +0,0 @@
package tk.antoine_roux.wiki.model.response;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Json response object for register endpoint
*/
public class RegisterRunnerResponse {
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
String id;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
String token;
public RegisterRunnerResponse(String id, String token) {
this.id = id;
this.token = token;
}
}

View File

@ -1,19 +0,0 @@
package tk.antoine_roux.wiki.model.response.secondary;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class Artifact {
public String name;
public boolean untracked;
public List<String> paths;
public List<String> exclude;
public String when;
@JsonProperty("artifact_type")
public String artifactType;
@JsonProperty("artifact_format")
public String artifactFormat;
@JsonProperty("expire_in")
public String expireIn;
}

View File

@ -1,10 +0,0 @@
package tk.antoine_roux.wiki.model.response.secondary;
import java.util.List;
public class Cache {
public String key;
public boolean untracked;
public String policy;
public List<String> paths;
}

View File

@ -1,8 +0,0 @@
package tk.antoine_roux.wiki.model.response.secondary;
public class Credential {
public String type;
public String url;
public String username;
public String password;
}

View File

@ -1,16 +0,0 @@
package tk.antoine_roux.wiki.model.response.secondary;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Dependency {
public int id;
public String token;
public String name;
@JsonProperty("artifacts_file")
public DependencyArtifactsFile artifactsFile;
public static class DependencyArtifactsFile {
public String filename;
public int size;
}
}

View File

@ -1,16 +0,0 @@
package tk.antoine_roux.wiki.model.response.secondary;
import com.fasterxml.jackson.annotation.JsonProperty;
public class GitInfo {
@JsonProperty("repo_url")
public String repoURL;
public String ref;
public String sha;
@JsonProperty("before_sha")
public String beforeSha;
@JsonProperty("ref_type")
public String refType;
public String[] refspecs;
public int depth;
}

View File

@ -1,8 +0,0 @@
package tk.antoine_roux.wiki.model.response.secondary;
import com.fasterxml.jackson.annotation.JsonProperty;
public class GitLabFeatures {
@JsonProperty("trace_sections")
public boolean traceSections;
}

View File

@ -1,26 +0,0 @@
package tk.antoine_roux.wiki.model.response.secondary;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.List;
public class Image {
public String name;
@JsonInclude(JsonInclude.Include.NON_NULL)
public String alias;
@JsonInclude(JsonInclude.Include.NON_NULL)
public List<String> command;
@JsonInclude(JsonInclude.Include.NON_NULL)
public List<String> entrypoint;
@JsonInclude(JsonInclude.Include.NON_NULL)
public Port ports;
static class Port {
@JsonInclude(JsonInclude.Include.NON_NULL)
public String name;
@JsonInclude(JsonInclude.Include.NON_NULL)
public int number;
@JsonInclude(JsonInclude.Include.NON_NULL)
public String protocol;
}
}

View File

@ -1,14 +0,0 @@
package tk.antoine_roux.wiki.model.response.secondary;
import com.fasterxml.jackson.annotation.JsonProperty;
public class JobInfo {
public String name;
public String stage;
@JsonProperty("project_id")
public String projectID;
@JsonProperty("project_name")
public String projectName;
}

View File

@ -1,15 +0,0 @@
package tk.antoine_roux.wiki.model.response.secondary;
import com.fasterxml.jackson.annotation.JsonProperty;
public class JobVariable {
public String key;
public String value;
@JsonProperty("public")
public boolean isPublic;
@JsonProperty("-")
public boolean internal;
public boolean file;
public boolean masked;
public boolean raw;
}

View File

@ -1,5 +0,0 @@
package tk.antoine_roux.wiki.model.response.secondary;
public class RunnerInfo {
public int timeout;
}

View File

@ -1,33 +0,0 @@
package tk.antoine_roux.wiki.model.response.secondary;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.Map;
public class Secret {
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public VaultSecret vault;
public static class VaultSecret {
public VaultServer server;
public VaultEngine engine;
public String path;
public String field;
}
public static class VaultServer {
public String url;
public VaultAuth auth;
}
public static class VaultAuth {
public String name;
public String path;
public Map<String, Object> data;
}
public static class VaultEngine {
public String name;
public String path;
}
}

View File

@ -1,14 +0,0 @@
package tk.antoine_roux.wiki.model.response.secondary;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class Step {
public String name;
public List<String> script;
public int timeout;
public String when;
@JsonProperty("allow_failure")
public boolean allowFailure;
}

View File

@ -1,75 +0,0 @@
package tk.antoine_roux.wiki.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import tk.antoine_roux.wiki.model.internal.GitlabCI;
import tk.antoine_roux.wiki.model.request.HookEvent;
import tk.antoine_roux.wiki.model.request.secondary.Commit;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Optional;
@Service
public class GitService {
public static final String GITLAB_CI_FILE_PATH = ".gitlab-ci.yml";
private static final String GITLAB_RUNNER_CLONE_PREFIX = "gitlab-runner-clone";
public final UsernamePasswordCredentialsProvider credentialsProvider;
private final ObjectMapper objectMapper;
@Autowired
public GitService(UsernamePasswordCredentialsProvider credentialsProvider, @Qualifier("YAMLObjectMapper") ObjectMapper objectMapper) {
this.credentialsProvider = credentialsProvider;
this.objectMapper = objectMapper;
}
/**
* return .gitlab-ci.yml content for {@link HookEvent}
*/
public Optional<GitlabCI> getYMLGitlabCI(HookEvent hookEvent) throws IOException, GitAPIException {
Optional<GitlabCI> optJob;
if (hookEvent.commits.isEmpty()) {
optJob = Optional.empty();
} else {
// search for head commit or take first in event's list of commit
Commit commit = hookEvent.commits.stream().filter(co -> co.id.equals(hookEvent.headCommit))
.findFirst().orElse(hookEvent.commits.get(0));
optJob = Optional.of(this.getGitlabCIContent(hookEvent.repository.cloneUrl, commit));
}
return optJob;
}
/**
* return .gitlab-ci.yml content for given commit into cloneUrl repository
*/
private GitlabCI getGitlabCIContent(String cloneURL, Commit commit) throws IOException, GitAPIException {
Git call = Git.cloneRepository()
.setURI(cloneURL)
.setCredentialsProvider(this.credentialsProvider)
.setDirectory(Files.createTempDirectory(GITLAB_RUNNER_CLONE_PREFIX).toFile())
.call();
RevWalk revWalk = new RevWalk(call.getRepository());
RevCommit revCommit = revWalk.parseCommit(ObjectId.fromString(commit.id));
try (TreeWalk walk = TreeWalk.forPath(call.getRepository(), GITLAB_CI_FILE_PATH, revCommit.getTree())) {
if (walk != null) {
byte[] bytes = call.getRepository().open(walk.getObjectId(0)).getBytes();
return this.objectMapper.readValue(bytes, GitlabCI.class);
} else {
throw new IllegalArgumentException("No path found.");
}
}
}
}

View File

@ -1,27 +0,0 @@
package tk.antoine_roux.wiki.service;
import org.springframework.stereotype.Service;
import tk.antoine_roux.wiki.model.request.JobRequest;
import tk.antoine_roux.wiki.model.request.HookEvent;
import tk.antoine_roux.wiki.model.response.JobResponse;
import java.util.Optional;
import java.util.concurrent.ConcurrentLinkedQueue;
@Service
public class JobManager {
/**
* concurrent list of {@link JobResponse} fill by {@link tk.antoine_roux.wiki.ControllerHandlers#webhook(HookEvent)}
* and pop by {@link tk.antoine_roux.wiki.ControllerHandlers#jobRequest(JobRequest)}
*/
ConcurrentLinkedQueue<JobResponse> jobQueue = new ConcurrentLinkedQueue<>();
public void stackJob(JobResponse newJob) {
this.jobQueue.add(newJob);
}
public Optional<JobResponse> popJob(JobRequest jobRequest) {
return Optional.ofNullable(this.jobQueue.poll());
}
}

View File

@ -1,44 +0,0 @@
package tk.antoine_roux.wiki.service;
import org.springframework.stereotype.Service;
import tk.antoine_roux.wiki.model.internal.Runner;
import tk.antoine_roux.wiki.model.request.AddRunner;
import tk.antoine_roux.wiki.model.request.TokenRunner;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Class use to perist registered gitlab runner
*/
@Service
public class RunnerRegistrar {
private final List<Runner> runners = new ArrayList<>();
AtomicInteger idIncrementer = new AtomicInteger();
public List<Runner> getRunners() {
return this.runners;
}
public Runner addRunner(AddRunner addRunner) {
Runner r = new Runner();
r.id = this.idIncrementer.getAndIncrement();
r.authenticationToken = UUID.randomUUID();
r.registrationToken = addRunner.token;
r.tags = addRunner.tags;
r.description = addRunner.description;
this.runners.add(r);
return r;
}
public boolean removeRunnerByRegistrationToken(TokenRunner r) {
return this.runners.removeIf(runner -> runner.authenticationToken.equals(r.token));
}
public boolean checkRunner(TokenRunner body) {
return this.runners.stream().anyMatch(runner -> runner.authenticationToken.equals(body.token));
}
}

View File

@ -1,19 +0,0 @@
package tk.antoine_roux.wiki.utilitary;
import java.util.function.Supplier;
public final class Boolean {
/**
* utility method throw ex if in is false, else follow in value
*
* @param in
* @param ex
* @throws Exception exception thrown when in param is false
*/
public static <T extends Exception> void trueOrElseThrow(boolean in, Supplier<T> ex) throws Exception {
if (!in) {
throw ex.get();
}
}
}

View File

@ -1,13 +0,0 @@
package tk.antoine_roux.wiki.utilitary;
/**
* Application level constant
*/
public final class Constant {
public static final String API_PREFIX = "/api";
public static final String API_NAME = "gitlab-runner-gateway";
public static final String API_VERSION = "4";
public static final String VERSION_PREFIX = "v";
public static final int OBJECT_ID_STRING_LENGTH = 40;
}

View File

@ -1,8 +0,0 @@
Arg=\
--native-image-info \
-H:ReflectionConfigurationFiles=/build/target/classes/META-INF/native-image/tk.antoine-roux/wiki-project/reflection-config.json \
-Dspring.native.mode=feature \
-Dspring.native.verbose=false \
-Dspring.native.dump-config=/tmp/computed-reflect-config.json \
-Dspring.native.remove-unused-autoconfig=true \
-Dspring.graal.skip-logback=false

View File

@ -1,14 +0,0 @@
[
{
"name": "org.springframework.boot.devtools.logger.DevToolsLogFactory.Listener",
"allDeclaredConstructors": true,
"allDeclaredMethods": true
},
{
"name": "javax.validation.ValidationException",
"allDeclaredConstructors": true,
"allDeclaredMethods": true
}
]

View File

@ -1,23 +1,7 @@
spring.main.banner-mode=off
spring.main.lazy-initialization=false
spring.main.lazy-initialization=true
spring.output.ansi.enabled=ALWAYS
spring.jackson.serialization.write-dates-as-timestamps=false
server.port=^application.port^
# wikiproject.basePath=/api
logging.level.root=INFO
# this log print request content
logging.level.org.apache.coyote.http11.Http11InputBuffer=DEBUG
# spring boot actuator
management.server.port=8080
info.name=gitlab-runner-gateway
info.more.detail=This is a REST API use to gateway gitlab runner call to gitlab instance
management.endpoints.web.exposure.include=mappings
gitlab-ci.username=
gitlab-ci.password=
wikiproject.basePath=/wikiproject