diff --git a/doc-gitlab-runner.md b/doc-gitlab-runner.md index 14353c5..0e5f64e 100644 --- a/doc-gitlab-runner.md +++ b/doc-gitlab-runner.md @@ -5,6 +5,99 @@ 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" +$ gitlab-runner register -non-interactive --description "manualy registered gitlab runner" --url "http://172.17.0.1:8080/" --registration-token "3b79eb1f-32f3-4db2-ad1b-6 + 702e476d839" --tag-list "docker,manual" --executor shell ``` + +same 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"}' +``` + + + +--- + +```json +{ + "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" +} +``` + +## http call use when job is run + +``` +[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 +``` + +```json +{ + "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" +} +``` + +... diff --git a/pom.xml b/pom.xml index efdbc33..9b99443 100644 --- a/pom.xml +++ b/pom.xml @@ -9,6 +9,7 @@ org.springframework.boot spring-boot-starter-parent 2.4.0-M2 + diff --git a/src/main/java/tk/antoine_roux/wiki/ControllerHandlers.java b/src/main/java/tk/antoine_roux/wiki/ControllerHandlers.java index 21f774d..ed4d004 100644 --- a/src/main/java/tk/antoine_roux/wiki/ControllerHandlers.java +++ b/src/main/java/tk/antoine_roux/wiki/ControllerHandlers.java @@ -1,42 +1,39 @@ package tk.antoine_roux.wiki; +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.Runner; +import tk.antoine_roux.wiki.model.request.AddRunner; +import tk.antoine_roux.wiki.model.request.DeleteRunner; +import tk.antoine_roux.wiki.model.response.RegisterResponse; +import tk.antoine_roux.wiki.utilitary.Boolean; +import java.lang.invoke.MethodHandles; 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; + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + private final 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 addRunner(@RequestBody String body) { - System.out.println(body); - return ResponseEntity.ok(body); - } - /** * hello test endpoint * @@ -44,7 +41,7 @@ public class ControllerHandlers { */ @ResponseBody @GetMapping("/info") - public static ResponseEntity> info() { + public ResponseEntity> info() { return ResponseEntity.ok( // sort attribute by key name new TreeMap<>( @@ -55,4 +52,32 @@ public class ControllerHandlers { ) ); } + + /** + * add register new runners + * + * @param body + * @return + */ + @ResponseBody + @ApiVersion({API_VERSION}) + @PostMapping("/runners") + public ResponseEntity addRunner(@RequestBody AddRunner body) { + + logger.debug("Receive register runner request " + body); + Runner createdRunner = this.runnerRegistrar.addRunner(body); + + RegisterResponse response = createdRunner.toRegisterResponse(); + logger.debug("Response register runner " + response); + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } + + @ApiVersion(API_VERSION) + @DeleteMapping("/runners") + public ResponseEntity deleteRunner(@RequestBody DeleteRunner 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(); + } } diff --git a/src/main/java/tk/antoine_roux/wiki/RunnerRegistrar.java b/src/main/java/tk/antoine_roux/wiki/RunnerRegistrar.java index cb66987..6edd47f 100644 --- a/src/main/java/tk/antoine_roux/wiki/RunnerRegistrar.java +++ b/src/main/java/tk/antoine_roux/wiki/RunnerRegistrar.java @@ -1,9 +1,14 @@ package tk.antoine_roux.wiki; import org.springframework.stereotype.Service; +import tk.antoine_roux.wiki.model.Runner; +import tk.antoine_roux.wiki.model.request.AddRunner; +import tk.antoine_roux.wiki.model.request.DeleteRunner; 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 @@ -12,30 +17,24 @@ import java.util.List; public class RunnerRegistrar { private final List runners = new ArrayList<>(); + AtomicInteger idIncrementer = new AtomicInteger(); + public List getRunners() { return this.runners; } - public void addRunner(Runner r) { + 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; } - /** - * 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; - } + public boolean removeRunnerByRegistrationToken(DeleteRunner r) { + return this.runners.removeIf(runner -> runner.authenticationToken.equals(r.token)); } } diff --git a/src/main/java/tk/antoine_roux/wiki/configuration/ApiVersionRequestMappingHandlerMapping.java b/src/main/java/tk/antoine_roux/wiki/configuration/ApiVersionRequestMappingHandlerMapping.java index 20bd17c..6955534 100644 --- a/src/main/java/tk/antoine_roux/wiki/configuration/ApiVersionRequestMappingHandlerMapping.java +++ b/src/main/java/tk/antoine_roux/wiki/configuration/ApiVersionRequestMappingHandlerMapping.java @@ -52,8 +52,8 @@ public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandle RequestMappingInfo requestMappingInfo = new RequestMappingInfo( new PatternsRequestCondition( new String[]{annotationApiPrefix.value()}, - false, - this.getPathMatcher() + this.getUrlPathHelper(), this.getPathMatcher(), + false ), new RequestMethodsRequestCondition(), new ParamsRequestCondition(), @@ -76,7 +76,11 @@ public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandle } return new RequestMappingInfo( - new PatternsRequestCondition(patterns, false, this.getPathMatcher()), + new PatternsRequestCondition( + patterns, this.getUrlPathHelper(), this.getPathMatcher(), + false + // , this.useTrailingSlashMatch(), this.getFileExtensions() + ), new RequestMethodsRequestCondition(), new ParamsRequestCondition(), new HeadersRequestCondition(), diff --git a/src/main/java/tk/antoine_roux/wiki/configuration/ControllerExceptionHandler.java b/src/main/java/tk/antoine_roux/wiki/configuration/ControllerExceptionHandler.java new file mode 100644 index 0000000..a756141 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/configuration/ControllerExceptionHandler.java @@ -0,0 +1,39 @@ +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 handleInternalException(NoIdException ex, WebRequest request) { + Map 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 handleNotFoundException(NoIdException ex, WebRequest request) { + Map body = new LinkedHashMap<>(); + body.put("timestamp", LocalDateTime.now()); + body.put("message", ex.getMessage()); + + return new ResponseEntity<>(body, HttpStatus.NOT_FOUND); + } +} diff --git a/src/main/java/tk/antoine_roux/wiki/configuration/Exception/DeleteRunnerException.java b/src/main/java/tk/antoine_roux/wiki/configuration/Exception/DeleteRunnerException.java new file mode 100644 index 0000000..a843147 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/configuration/Exception/DeleteRunnerException.java @@ -0,0 +1,9 @@ +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"); + } +} diff --git a/src/main/java/tk/antoine_roux/wiki/configuration/Exception/NoIdException.java b/src/main/java/tk/antoine_roux/wiki/configuration/Exception/NoIdException.java new file mode 100644 index 0000000..b23d0be --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/configuration/Exception/NoIdException.java @@ -0,0 +1,9 @@ +package tk.antoine_roux.wiki.configuration.Exception; + +public class NoIdException extends RuntimeException { + private static final long serialVersionUID = 1470945508482780554L; + + public NoIdException() { + super("Invalid id value"); + } +} diff --git a/src/main/java/tk/antoine_roux/wiki/configuration/WebConfiguration.java b/src/main/java/tk/antoine_roux/wiki/configuration/WebConfiguration.java index 9f8dcd2..448e432 100644 --- a/src/main/java/tk/antoine_roux/wiki/configuration/WebConfiguration.java +++ b/src/main/java/tk/antoine_roux/wiki/configuration/WebConfiguration.java @@ -1,25 +1,20 @@ package tk.antoine_roux.wiki.configuration; -import org.springframework.context.annotation.Bean; +import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations; 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 + * + * @see org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations */ @Configuration(proxyBeanMethods = false) -public class WebConfiguration extends WebMvcConfigurationSupport { +public class WebConfiguration implements WebMvcRegistrations { - @Bean @Override - public RequestMappingHandlerMapping requestMappingHandlerMapping(ContentNegotiationManager contentNegotiationManager, - FormattingConversionService conversionService, - ResourceUrlProvider resourceUrlProvider) { + public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { return new ApiVersionRequestMappingHandlerMapping(Constant.VERSION_PREFIX); } } diff --git a/src/main/java/tk/antoine_roux/wiki/model/Runner.java b/src/main/java/tk/antoine_roux/wiki/model/Runner.java new file mode 100644 index 0000000..ca3098d --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/Runner.java @@ -0,0 +1,21 @@ +package tk.antoine_roux.wiki.model; + +import tk.antoine_roux.wiki.model.response.RegisterResponse; + +import java.util.List; +import java.util.UUID; + +/** + * in memory representation of gitlab runner + */ +public class Runner { + public String description; + public List tags; + public UUID registrationToken; + public UUID authenticationToken; + public Integer id; + + public RegisterResponse toRegisterResponse() { + return new RegisterResponse(String.valueOf(this.id), this.authenticationToken.toString()); + } +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/request/AddRunner.java b/src/main/java/tk/antoine_roux/wiki/model/request/AddRunner.java new file mode 100644 index 0000000..f0afb5b --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/request/AddRunner.java @@ -0,0 +1,23 @@ +package tk.antoine_roux.wiki.model.request; + +import com.fasterxml.jackson.annotation.JsonProperty; + +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 tags; + public UUID token; + + @JsonProperty("tag_list") + public void setTags(String tags) { + this.tags = Arrays.asList(tags.split(TAG_SEPARATOR).clone()); + } +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/request/DeleteRunner.java b/src/main/java/tk/antoine_roux/wiki/model/request/DeleteRunner.java new file mode 100644 index 0000000..426afd0 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/request/DeleteRunner.java @@ -0,0 +1,10 @@ +package tk.antoine_roux.wiki.model.request; + +import java.util.UUID; + +/** + * Model use to remove runner instance + */ +public class DeleteRunner { + public UUID token; +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/RegisterResponse.java b/src/main/java/tk/antoine_roux/wiki/model/response/RegisterResponse.java new file mode 100644 index 0000000..59dedb4 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/RegisterResponse.java @@ -0,0 +1,18 @@ +package tk.antoine_roux.wiki.model.response; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Json response object for register endpoint + */ +public class RegisterResponse { + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + String id; + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + String token; + + public RegisterResponse(String id, String token) { + this.id = id; + this.token = token; + } +} diff --git a/src/main/java/tk/antoine_roux/wiki/utilitary/Boolean.java b/src/main/java/tk/antoine_roux/wiki/utilitary/Boolean.java new file mode 100644 index 0000000..d8ec8c6 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/utilitary/Boolean.java @@ -0,0 +1,19 @@ +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 void trueOrElseThrow(boolean in, Supplier ex) throws Exception { + if (!in) { + throw ex.get(); + } + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index fbca480..2caf3e1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,6 +8,9 @@ server.port=^application.port^ 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