add spring actuator, and api version and prefix support

This commit is contained in:
Antoine 2020-09-10 20:03:50 +02:00
parent 3b75eb6ed6
commit 6b65fa8e20
Signed by: antoine
GPG Key ID: 098FB66FC0475E70
11 changed files with 278 additions and 11 deletions

10
doc-gitlab-runner.md Normal file
View File

@ -0,0 +1,10 @@
# gitlab runner
command used to register new 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
$ gitlab-runner register -non-interactive --description "manualy registered gitlab runner" --url "http://172.17.0.1
:8080/" --registration-token "registration_token" --tag-list "docker,manual"
```

View File

@ -54,6 +54,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,11 @@
package tk.antoine_roux.wiki;
/**
* 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";
}

View File

@ -0,0 +1,58 @@
package tk.antoine_roux.wiki;
import org.springframework.beans.factory.annotation.Autowired;
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 java.util.Map;
import java.util.TreeMap;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static tk.antoine_roux.wiki.Constant.*;
@RestController
@ApiPrefix(API_PREFIX)
public class ControllerHandlers {
private RunnerRegistrar runnerRegistrar;
@Autowired
public ControllerHandlers(RunnerRegistrar runnerRegistrar) {
this.runnerRegistrar = runnerRegistrar;
}
/**
* add register new runners
*
* @param body
* @return
*/
@ResponseBody
@ApiVersion({API_VERSION})
@PostMapping(value = "/runners", produces = APPLICATION_JSON_VALUE)
public static ResponseEntity<String> addRunner(@RequestBody String body) {
System.out.println(body);
return ResponseEntity.ok(body);
}
/**
* hello test endpoint
*
* @return
*/
@ResponseBody
@GetMapping("/info")
public static 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
)
)
);
}
}

View File

@ -1,9 +1,7 @@
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.context.annotation.Bean;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.RouterFunctions;
import org.springframework.web.servlet.function.ServerResponse;
@ -15,9 +13,6 @@ import org.springframework.web.servlet.function.ServerResponse;
@SpringBootApplication(proxyBeanMethods = false)
public class MainLauncher {
@Value("${wikiproject.basePath}")
private String basePath;
/**
* Entrypoint for application
*/
@ -28,11 +23,11 @@ public class MainLauncher {
/**
* Routing declaration
*/
@Bean
// @Bean
public RouterFunction<ServerResponse> routes() {
return RouterFunctions.route()
.GET(this.basePath + "/hello", serverRequest ->
ServerResponse.ok().body("Hello world !")
).build();
// .POST("/runners", accept(APPLICATION_FORM_URLENCODED), ControllerHandlers::addRunner)
// .GET("/hello", ControllerHandlers::hello)
.build();
}
}

View File

@ -0,0 +1,41 @@
package tk.antoine_roux.wiki;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* Class use to perist registered gitlab runner
*/
@Service
public class RunnerRegistrar {
private final List<Runner> runners = new ArrayList<>();
public List<Runner> getRunners() {
return this.runners;
}
public void addRunner(Runner r) {
this.runners.add(r);
}
/**
* in memory representation of gitlab runner
*/
public static class Runner {
private static final String TAG_SEPARATOR = ",";
public String id;
public String description;
public String[] tags;
public String registrationToken;
public Runner(String id, String description, String tags, String registrationToken) {
this.id = id;
this.description = description;
this.tags = tags.split(TAG_SEPARATOR);
this.registrationToken = registrationToken;
}
}
}

View File

@ -0,0 +1,12 @@
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

@ -0,0 +1,12 @@
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

@ -0,0 +1,91 @@
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

@ -0,0 +1,25 @@
package tk.antoine_roux.wiki.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.resource.ResourceUrlProvider;
import tk.antoine_roux.wiki.Constant;
/**
* spring web configuration
*/
@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {
@Bean
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping(ContentNegotiationManager contentNegotiationManager,
FormattingConversionService conversionService,
ResourceUrlProvider resourceUrlProvider) {
return new ApiVersionRequestMappingHandlerMapping(Constant.VERSION_PREFIX);
}
}

View File

@ -1,7 +1,15 @@
spring.main.banner-mode=off
spring.main.lazy-initialization=true
spring.main.lazy-initialization=false
spring.output.ansi.enabled=ALWAYS
server.port=^application.port^
wikiproject.basePath=/wikiproject
# wikiproject.basePath=/api
logging.level.root=INFO
# 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