From 8283da89b2a0f6d52d8a7998e56edacf037a1f3a Mon Sep 17 00:00:00 2001 From: Antoine Date: Sat, 3 Oct 2020 17:03:48 +0200 Subject: [PATCH] start .gitlab-ci.yml parsing --- .gitignore | 1 + pom.xml | 10 ++ .../antoine_roux/wiki/ControllerHandlers.java | 25 +++-- .../tk/antoine_roux/wiki/MainLauncher.java | 3 + .../wiki/configuration/GitConfiguration.java | 50 ++++++++++ .../wiki/configuration/WebConfiguration.java | 20 ++++ .../wiki/model/internal/GitlabCI.java | 46 +++++++++ .../wiki/model/internal/HookEvent.java | 56 ----------- .../antoine_roux/wiki/model/internal/Job.java | 93 ------------------- .../wiki/model/internal/JobStatus.java | 12 --- .../wiki/model/request/AddRunner.java | 1 + .../wiki/model/request/HookEvent.java | 55 +++++++++++ .../wiki/model/request/JobRequest.java | 2 + .../secondary}/Commit.java | 4 +- .../secondary}/InternalTracker.java | 2 +- .../secondary}/Permission.java | 2 +- .../secondary}/Repository.java | 2 +- .../request/{ => secondary}/RunnerInfo.java | 2 +- .../{internal => request/secondary}/User.java | 2 +- .../secondary}/UserReduced.java | 2 +- .../wiki/model/response/JobResponse.java | 33 +++++++ .../model/response/secondary/Artifact.java | 19 ++++ .../wiki/model/response/secondary/Cache.java | 10 ++ .../model/response/secondary/Credential.java | 8 ++ .../model/response/secondary/Dependency.java | 16 ++++ .../model/response/secondary/GitInfo.java | 16 ++++ .../response/secondary/GitLabFeatures.java | 8 ++ .../wiki/model/response/secondary/Image.java | 26 ++++++ .../model/response/secondary/JobInfo.java | 14 +++ .../model/response/secondary/JobVariable.java | 15 +++ .../model/response/secondary/RunnerInfo.java | 5 + .../wiki/model/response/secondary/Secret.java | 33 +++++++ .../wiki/model/response/secondary/Step.java | 14 +++ .../antoine_roux/wiki/service/GitService.java | 75 +++++++++++++++ .../antoine_roux/wiki/service/JobManager.java | 12 +-- src/main/resources/application.properties | 4 + 36 files changed, 514 insertions(+), 184 deletions(-) create mode 100644 src/main/java/tk/antoine_roux/wiki/configuration/GitConfiguration.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/internal/GitlabCI.java delete mode 100644 src/main/java/tk/antoine_roux/wiki/model/internal/HookEvent.java delete mode 100644 src/main/java/tk/antoine_roux/wiki/model/internal/Job.java delete mode 100644 src/main/java/tk/antoine_roux/wiki/model/internal/JobStatus.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/request/HookEvent.java rename src/main/java/tk/antoine_roux/wiki/model/{internal => request/secondary}/Commit.java (76%) rename src/main/java/tk/antoine_roux/wiki/model/{internal => request/secondary}/InternalTracker.java (88%) rename src/main/java/tk/antoine_roux/wiki/model/{internal => request/secondary}/Permission.java (73%) rename src/main/java/tk/antoine_roux/wiki/model/{internal => request/secondary}/Repository.java (97%) rename src/main/java/tk/antoine_roux/wiki/model/request/{ => secondary}/RunnerInfo.java (94%) rename src/main/java/tk/antoine_roux/wiki/model/{internal => request/secondary}/User.java (92%) rename src/main/java/tk/antoine_roux/wiki/model/{internal => request/secondary}/UserReduced.java (85%) create mode 100644 src/main/java/tk/antoine_roux/wiki/model/response/JobResponse.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/response/secondary/Artifact.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/response/secondary/Cache.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/response/secondary/Credential.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/response/secondary/Dependency.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/response/secondary/GitInfo.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/response/secondary/GitLabFeatures.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/response/secondary/Image.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/response/secondary/JobInfo.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/response/secondary/JobVariable.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/response/secondary/RunnerInfo.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/response/secondary/Secret.java create mode 100644 src/main/java/tk/antoine_roux/wiki/model/response/secondary/Step.java create mode 100644 src/main/java/tk/antoine_roux/wiki/service/GitService.java diff --git a/.gitignore b/.gitignore index aab87fa..00ba3cd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ target/ testGit/ +application-local.properties diff --git a/pom.xml b/pom.xml index 5750576..0850c95 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,7 @@ 0.8.0 20.2.0 2.11.2 + 5.9.0.202009080501-r @@ -60,10 +61,19 @@ org.springframework.boot spring-boot-starter-actuator + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + com.fasterxml.jackson.datatype jackson-datatype-jsr310 + + org.eclipse.jgit + org.eclipse.jgit + ${org.eclipse.jgit.version} + diff --git a/src/main/java/tk/antoine_roux/wiki/ControllerHandlers.java b/src/main/java/tk/antoine_roux/wiki/ControllerHandlers.java index 125cf8f..7b21d91 100644 --- a/src/main/java/tk/antoine_roux/wiki/ControllerHandlers.java +++ b/src/main/java/tk/antoine_roux/wiki/ControllerHandlers.java @@ -1,5 +1,6 @@ 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; @@ -9,17 +10,20 @@ 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.HookEvent; -import tk.antoine_roux.wiki.model.internal.Job; +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; @@ -35,11 +39,13 @@ public class ControllerHandlers { private final RunnerRegistrar runnerRegistrar; private final JobManager jobManager; + private final GitService gitService; @Autowired - public ControllerHandlers(RunnerRegistrar runnerRegistrar, JobManager jobManager) { + public ControllerHandlers(RunnerRegistrar runnerRegistrar, JobManager jobManager, GitService gitService) { this.runnerRegistrar = runnerRegistrar; this.jobManager = jobManager; + this.gitService = gitService; } /** @@ -102,8 +108,8 @@ public class ControllerHandlers { @ApiVersion(API_VERSION) @PostMapping("/jobs/request") - public ResponseEntity jobRequest(@RequestBody JobRequest jobRequest) { - Optional currentJob = this.jobManager.popJob(jobRequest); + public ResponseEntity jobRequest(@RequestBody JobRequest jobRequest) { + Optional currentJob = this.jobManager.popJob(jobRequest); return currentJob .map(job -> ResponseEntity.status(HttpStatus.CREATED).body(job)) .orElseGet(() -> ResponseEntity.noContent().build()); @@ -118,12 +124,13 @@ public class ControllerHandlers { @ApiVersion(API_VERSION) @PostMapping("/webhook") - public ResponseEntity webhook(@RequestBody HookEvent webHookData) { + public ResponseEntity webhook(@RequestBody HookEvent webHookData) throws IOException, GitAPIException { ResponseEntity.BodyBuilder responseEntity; - Optional job = webHookData.toJob(); + Optional gitlabCI = this.gitService.getYMLGitlabCI(webHookData); - if (job.isPresent()) { - this.jobManager.stackJob(job.get()); + if (gitlabCI.isPresent()) { + JobResponse job = gitlabCI.get().buildJobResponse(); + this.jobManager.stackJob(job); responseEntity = ResponseEntity.ok(); } else { responseEntity = ResponseEntity.unprocessableEntity(); diff --git a/src/main/java/tk/antoine_roux/wiki/MainLauncher.java b/src/main/java/tk/antoine_roux/wiki/MainLauncher.java index 3a6f6cc..a7cfccd 100644 --- a/src/main/java/tk/antoine_roux/wiki/MainLauncher.java +++ b/src/main/java/tk/antoine_roux/wiki/MainLauncher.java @@ -2,15 +2,18 @@ package tk.antoine_roux.wiki; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; 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 { /** diff --git a/src/main/java/tk/antoine_roux/wiki/configuration/GitConfiguration.java b/src/main/java/tk/antoine_roux/wiki/configuration/GitConfiguration.java new file mode 100644 index 0000000..16c903e --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/configuration/GitConfiguration.java @@ -0,0 +1,50 @@ +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; + } + } +} 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 903850b..f051f40 100644 --- a/src/main/java/tk/antoine_roux/wiki/configuration/WebConfiguration.java +++ b/src/main/java/tk/antoine_roux/wiki/configuration/WebConfiguration.java @@ -1,7 +1,13 @@ 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; @@ -17,4 +23,18 @@ public class WebConfiguration implements WebMvcRegistrations { 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(); + } } diff --git a/src/main/java/tk/antoine_roux/wiki/model/internal/GitlabCI.java b/src/main/java/tk/antoine_roux/wiki/model/internal/GitlabCI.java new file mode 100644 index 0000000..04c4eb2 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/internal/GitlabCI.java @@ -0,0 +1,46 @@ +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 beforeScript = Collections.emptyList(); + @JsonProperty("after_script") + public List afterScript = Collections.emptyList(); + public Map 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 variables = Collections.emptyMap(); + public List services = Collections.emptyList(); + public Map rules = Collections.emptyMap(); + public List script = Collections.emptyList(); + } +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/internal/HookEvent.java b/src/main/java/tk/antoine_roux/wiki/model/internal/HookEvent.java deleted file mode 100644 index 48f4740..0000000 --- a/src/main/java/tk/antoine_roux/wiki/model/internal/HookEvent.java +++ /dev/null @@ -1,56 +0,0 @@ -package tk.antoine_roux.wiki.model.internal; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.time.ZonedDateTime; -import java.util.List; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; - -public class HookEvent { - public String secret; - public String ref; - public String before; - public String after; - @JsonProperty("compare_url") - public String compareUrl; - public List 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 toJob() { - Optional 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; - } -} diff --git a/src/main/java/tk/antoine_roux/wiki/model/internal/Job.java b/src/main/java/tk/antoine_roux/wiki/model/internal/Job.java deleted file mode 100644 index 7c2801b..0000000 --- a/src/main/java/tk/antoine_roux/wiki/model/internal/Job.java +++ /dev/null @@ -1,93 +0,0 @@ -package tk.antoine_roux.wiki.model.internal; - -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.time.ZonedDateTime; - -// ID int `json:"id"` -// Token string `json:"token"` -// AllowGitFetch bool `json:"allow_git_fetch"` -// JobInfo JobInfo `json:"job_info"` -// GitInfo GitInfo `json:"git_info"` -// RunnerInfo RunnerInfo `json:"runner_info"` -// Variables JobVariables `json:"variables"` -// Steps Steps `json:"steps"` -// Image Image `json:"image"` -// Services Services `json:"services"` -// Artifacts Artifacts `json:"artifacts"` -// Cache Caches `json:"cache"` -// Credentials []Credentials `json:"credentials"` -// Dependencies Dependencies `json:"dependencies"` -// Features GitlabFeatures `json:"features"` -// Secrets Secrets `json:"secrets,omitempty"` - -public class Job { - @JsonProperty("artifacts_file") - public String artifactsFile; - public Commit commit; - public String coverage; - @JsonProperty("created_at") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssSSSXXX") - public ZonedDateTime createdAt; - @JsonProperty("finished_at") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssSSSXXX") - public ZonedDateTime finishedAt; - public Integer id; - public String name; - public String ref; - public Runner runner; - public String stage; - @JsonProperty("started_at") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssSSSXXX") - public ZonedDateTime startedAt; - public JobStatus status; - public boolean tag; - public UserReduced user; - - public Job( - String artifactsFile, Commit commit, String coverage, ZonedDateTime createdAt, ZonedDateTime finishedAt, - Integer id, String name, String ref, Runner runner, String stage, ZonedDateTime startedAt, JobStatus status, - boolean tag, UserReduced user - ) { - this.artifactsFile = artifactsFile; - this.commit = commit; - this.coverage = coverage; - this.createdAt = createdAt; - this.finishedAt = finishedAt; - this.id = id; - this.name = name; - this.ref = ref; - this.runner = runner; - this.stage = stage; - this.startedAt = startedAt; - this.status = status; - this.tag = tag; - this.user = user; - } - - public static class Commit { - @JsonProperty("author_email") - public String authorEmail; - @JsonProperty("author_name") - public String authorName; - @JsonProperty("created_at") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssSSSXXX") - public ZonedDateTime createdAt; - public String id; - public String message; - @JsonProperty("short_id") - public String shortId; - public String title; - - public Commit(String authorEmail, String authorName, ZonedDateTime createdAt, String id, String message, String shortId, String title) { - this.authorEmail = authorEmail; - this.authorName = authorName; - this.createdAt = createdAt; - this.id = id; - this.message = message; - this.shortId = shortId; - this.title = title; - } - } -} diff --git a/src/main/java/tk/antoine_roux/wiki/model/internal/JobStatus.java b/src/main/java/tk/antoine_roux/wiki/model/internal/JobStatus.java deleted file mode 100644 index ca99ddd..0000000 --- a/src/main/java/tk/antoine_roux/wiki/model/internal/JobStatus.java +++ /dev/null @@ -1,12 +0,0 @@ -package tk.antoine_roux.wiki.model.internal; - -/** - * This enum represent all state a job could take during - * his lifecycle - */ -public enum JobStatus { - CREATED, - STARTED, - STOPPED, - FINISHED -} 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 index b80c3a9..51bfe5f 100644 --- a/src/main/java/tk/antoine_roux/wiki/model/request/AddRunner.java +++ b/src/main/java/tk/antoine_roux/wiki/model/request/AddRunner.java @@ -1,6 +1,7 @@ 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; diff --git a/src/main/java/tk/antoine_roux/wiki/model/request/HookEvent.java b/src/main/java/tk/antoine_roux/wiki/model/request/HookEvent.java new file mode 100644 index 0000000..9d8ea75 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/request/HookEvent.java @@ -0,0 +1,55 @@ +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 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 toJob() { +// Optional 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; +// } +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/request/JobRequest.java b/src/main/java/tk/antoine_roux/wiki/model/request/JobRequest.java index d880009..01b7b1d 100644 --- a/src/main/java/tk/antoine_roux/wiki/model/request/JobRequest.java +++ b/src/main/java/tk/antoine_roux/wiki/model/request/JobRequest.java @@ -1,5 +1,7 @@ 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 */ diff --git a/src/main/java/tk/antoine_roux/wiki/model/internal/Commit.java b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/Commit.java similarity index 76% rename from src/main/java/tk/antoine_roux/wiki/model/internal/Commit.java rename to src/main/java/tk/antoine_roux/wiki/model/request/secondary/Commit.java index 7f775c1..b2b1d52 100644 --- a/src/main/java/tk/antoine_roux/wiki/model/internal/Commit.java +++ b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/Commit.java @@ -1,4 +1,4 @@ -package tk.antoine_roux.wiki.model.internal; +package tk.antoine_roux.wiki.model.request.secondary; import com.fasterxml.jackson.annotation.JsonFormat; @@ -13,7 +13,7 @@ public class Commit { public UserReduced committer; public String verification; - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX") + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssXXX") public ZonedDateTime timestamp; public List added; diff --git a/src/main/java/tk/antoine_roux/wiki/model/internal/InternalTracker.java b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/InternalTracker.java similarity index 88% rename from src/main/java/tk/antoine_roux/wiki/model/internal/InternalTracker.java rename to src/main/java/tk/antoine_roux/wiki/model/request/secondary/InternalTracker.java index 552983f..2d93f3d 100644 --- a/src/main/java/tk/antoine_roux/wiki/model/internal/InternalTracker.java +++ b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/InternalTracker.java @@ -1,4 +1,4 @@ -package tk.antoine_roux.wiki.model.internal; +package tk.antoine_roux.wiki.model.request.secondary; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/src/main/java/tk/antoine_roux/wiki/model/internal/Permission.java b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/Permission.java similarity index 73% rename from src/main/java/tk/antoine_roux/wiki/model/internal/Permission.java rename to src/main/java/tk/antoine_roux/wiki/model/request/secondary/Permission.java index 1895119..c4825ac 100644 --- a/src/main/java/tk/antoine_roux/wiki/model/internal/Permission.java +++ b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/Permission.java @@ -1,4 +1,4 @@ -package tk.antoine_roux.wiki.model.internal; +package tk.antoine_roux.wiki.model.request.secondary; /** * model about repository permission diff --git a/src/main/java/tk/antoine_roux/wiki/model/internal/Repository.java b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/Repository.java similarity index 97% rename from src/main/java/tk/antoine_roux/wiki/model/internal/Repository.java rename to src/main/java/tk/antoine_roux/wiki/model/request/secondary/Repository.java index 2a665f1..b1f3a85 100644 --- a/src/main/java/tk/antoine_roux/wiki/model/internal/Repository.java +++ b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/Repository.java @@ -1,4 +1,4 @@ -package tk.antoine_roux.wiki.model.internal; +package tk.antoine_roux.wiki.model.request.secondary; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/src/main/java/tk/antoine_roux/wiki/model/request/RunnerInfo.java b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/RunnerInfo.java similarity index 94% rename from src/main/java/tk/antoine_roux/wiki/model/request/RunnerInfo.java rename to src/main/java/tk/antoine_roux/wiki/model/request/secondary/RunnerInfo.java index a4d1fe7..70210c3 100644 --- a/src/main/java/tk/antoine_roux/wiki/model/request/RunnerInfo.java +++ b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/RunnerInfo.java @@ -1,4 +1,4 @@ -package tk.antoine_roux.wiki.model.request; +package tk.antoine_roux.wiki.model.request.secondary; import java.util.Map; diff --git a/src/main/java/tk/antoine_roux/wiki/model/internal/User.java b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/User.java similarity index 92% rename from src/main/java/tk/antoine_roux/wiki/model/internal/User.java rename to src/main/java/tk/antoine_roux/wiki/model/request/secondary/User.java index dc62cc4..ad04d0b 100644 --- a/src/main/java/tk/antoine_roux/wiki/model/internal/User.java +++ b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/User.java @@ -1,4 +1,4 @@ -package tk.antoine_roux.wiki.model.internal; +package tk.antoine_roux.wiki.model.request.secondary; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/src/main/java/tk/antoine_roux/wiki/model/internal/UserReduced.java b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/UserReduced.java similarity index 85% rename from src/main/java/tk/antoine_roux/wiki/model/internal/UserReduced.java rename to src/main/java/tk/antoine_roux/wiki/model/request/secondary/UserReduced.java index e28788e..9977400 100644 --- a/src/main/java/tk/antoine_roux/wiki/model/internal/UserReduced.java +++ b/src/main/java/tk/antoine_roux/wiki/model/request/secondary/UserReduced.java @@ -1,4 +1,4 @@ -package tk.antoine_roux.wiki.model.internal; +package tk.antoine_roux.wiki.model.request.secondary; /** * Reduced user information diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/JobResponse.java b/src/main/java/tk/antoine_roux/wiki/model/response/JobResponse.java new file mode 100644 index 0000000..033dc88 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/JobResponse.java @@ -0,0 +1,33 @@ +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 variables; + public List steps; + public Image image; + public List services; + public List artifacts; + public List cache; + public List credentials; + public List dependencies; + public GitLabFeatures features; + public Map secrets; +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Artifact.java b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Artifact.java new file mode 100644 index 0000000..6b287ee --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Artifact.java @@ -0,0 +1,19 @@ +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 paths; + public List exclude; + public String when; + @JsonProperty("artifact_type") + public String artifactType; + @JsonProperty("artifact_format") + public String artifactFormat; + @JsonProperty("expire_in") + public String expireIn; +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Cache.java b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Cache.java new file mode 100644 index 0000000..af508f1 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Cache.java @@ -0,0 +1,10 @@ +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 paths; +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Credential.java b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Credential.java new file mode 100644 index 0000000..f19a232 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Credential.java @@ -0,0 +1,8 @@ +package tk.antoine_roux.wiki.model.response.secondary; + +public class Credential { + public String type; + public String url; + public String username; + public String password; +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Dependency.java b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Dependency.java new file mode 100644 index 0000000..986b722 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Dependency.java @@ -0,0 +1,16 @@ +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; + } +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/secondary/GitInfo.java b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/GitInfo.java new file mode 100644 index 0000000..c6a63ec --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/GitInfo.java @@ -0,0 +1,16 @@ +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; +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/secondary/GitLabFeatures.java b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/GitLabFeatures.java new file mode 100644 index 0000000..e84b280 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/GitLabFeatures.java @@ -0,0 +1,8 @@ +package tk.antoine_roux.wiki.model.response.secondary; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class GitLabFeatures { + @JsonProperty("trace_sections") + public boolean traceSections; +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Image.java b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Image.java new file mode 100644 index 0000000..1b567dd --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Image.java @@ -0,0 +1,26 @@ +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 command; + @JsonInclude(JsonInclude.Include.NON_NULL) + public List 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; + } +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/secondary/JobInfo.java b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/JobInfo.java new file mode 100644 index 0000000..5cdcf27 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/JobInfo.java @@ -0,0 +1,14 @@ +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; +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/secondary/JobVariable.java b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/JobVariable.java new file mode 100644 index 0000000..b1f0459 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/JobVariable.java @@ -0,0 +1,15 @@ +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; +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/secondary/RunnerInfo.java b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/RunnerInfo.java new file mode 100644 index 0000000..f21fbd0 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/RunnerInfo.java @@ -0,0 +1,5 @@ +package tk.antoine_roux.wiki.model.response.secondary; + +public class RunnerInfo { + public int timeout; +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Secret.java b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Secret.java new file mode 100644 index 0000000..d64d524 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Secret.java @@ -0,0 +1,33 @@ +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 data; + } + + public static class VaultEngine { + public String name; + public String path; + } +} diff --git a/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Step.java b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Step.java new file mode 100644 index 0000000..24103f3 --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/model/response/secondary/Step.java @@ -0,0 +1,14 @@ +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 script; + public int timeout; + public String when; + @JsonProperty("allow_failure") + public boolean allowFailure; +} diff --git a/src/main/java/tk/antoine_roux/wiki/service/GitService.java b/src/main/java/tk/antoine_roux/wiki/service/GitService.java new file mode 100644 index 0000000..05300cf --- /dev/null +++ b/src/main/java/tk/antoine_roux/wiki/service/GitService.java @@ -0,0 +1,75 @@ +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 getYMLGitlabCI(HookEvent hookEvent) throws IOException, GitAPIException { + Optional 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."); + } + } + } +} diff --git a/src/main/java/tk/antoine_roux/wiki/service/JobManager.java b/src/main/java/tk/antoine_roux/wiki/service/JobManager.java index be19773..dd6f00b 100644 --- a/src/main/java/tk/antoine_roux/wiki/service/JobManager.java +++ b/src/main/java/tk/antoine_roux/wiki/service/JobManager.java @@ -1,9 +1,9 @@ package tk.antoine_roux.wiki.service; import org.springframework.stereotype.Service; -import tk.antoine_roux.wiki.model.internal.HookEvent; -import tk.antoine_roux.wiki.model.internal.Job; 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; @@ -12,16 +12,16 @@ import java.util.concurrent.ConcurrentLinkedQueue; public class JobManager { /** - * concurrent list of {@link Job} fill by {@link tk.antoine_roux.wiki.ControllerHandlers#webhook(HookEvent)} + * 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 jobQueue = new ConcurrentLinkedQueue<>(); + ConcurrentLinkedQueue jobQueue = new ConcurrentLinkedQueue<>(); - public void stackJob(Job newJob) { + public void stackJob(JobResponse newJob) { this.jobQueue.add(newJob); } - public Optional popJob(JobRequest jobRequest) { + public Optional popJob(JobRequest jobRequest) { return Optional.ofNullable(this.jobQueue.poll()); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 2caf3e1..9d951cc 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,7 @@ spring.main.banner-mode=off spring.main.lazy-initialization=false spring.output.ansi.enabled=ALWAYS +spring.jackson.serialization.write-dates-as-timestamps=false server.port=^application.port^ @@ -17,3 +18,6 @@ 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=