Skip to content

Commit

Permalink
Add legacy deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
micheljung committed Mar 8, 2017
1 parent e094255 commit 08ba8f5
Show file tree
Hide file tree
Showing 30 changed files with 993 additions and 586 deletions.
10 changes: 9 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ targetCompatibility = 1.8

repositories {
mavenCentral()
maven { url "http://repo.jenkins-ci.org/public/" }
maven { url "https://jitpack.io" }
}

Expand Down Expand Up @@ -123,6 +124,11 @@ task pushDockerImage(type: DockerPushImage, dependsOn: buildDockerImage) {
tag project.version
}

configurations.all {
// Cache -SNAPSHOT for 60 seconds only
resolutionStrategy.cacheChangingModulesFor 60, 'seconds'
}

dependencies {
compile("org.springframework.boot:spring-boot-starter-actuator")
compile("org.springframework.boot:spring-boot-starter-jdbc")
Expand All @@ -132,11 +138,13 @@ dependencies {
compile("org.springframework.boot:spring-boot-starter-security")
compile("de.codecentric:spring-boot-admin-starter-client:${springBootAdminClientVersion}")

compile("com.github.FAForever:faf-java-commons:${fafCommonsVersion}")
compile("org.kohsuke:github-api:1.84")
compile("org.jolokia:jolokia-core:${jolokiaVersion}")
compile("org.springframework.security:spring-security-jwt:${springSecurityJwtVersion}")
compile("org.springframework.security.oauth:spring-security-oauth2:${springSecurityOauth2Version}")
compile("org.springframework:spring-context-support:${springContextSupportVersion}")

compile("org.eclipse.jgit:org.eclipse.jgit:${jgitVersionn}")
compile("org.jetbrains:annotations:${jetbrainsAnnotationsVersion}")
compile("com.google.guava:guava:${guavaVersion}")
compile("io.springfox:springfox-swagger-ui:${swaggerVersion}")
Expand Down
3 changes: 3 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ luajVersion=3.0.1
nocatchVersion=1.1
junitAddonsVersion=1.4
zohhakVersion=1.1.1
githubApiVersion=1.84
jgitVersionn=4.5.0.201609210915-r
fafCommonsVersion=76fb583d146082f3db68c0bece38a3ee28ead8e6
h2Version=1.4.193
38 changes: 32 additions & 6 deletions src/main/java/com/faforever/api/config/FafApiProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

@Data
@ConfigurationProperties(prefix = "faf-api", ignoreUnknownFields = false)
Expand All @@ -21,37 +23,35 @@ public class FafApiProperties {
private Map map = new Map();
private Clan clan = new Clan();
private FeaturedMods featuredMods = new FeaturedMods();
private GitHub gitHub = new GitHub();
private Deployment deployment = new Deployment();

@Data
public static class OAuth2 {

private String resourceId = "faf-api";
}

@Data
public static class Jwt {

private int accessTokenValiditySeconds = 3600;
private int refreshTokenValiditySeconds = 3600;
}

@Data
public static class Async {

private int corePoolSize = Runtime.getRuntime().availableProcessors();
private int maxPoolSize = Runtime.getRuntime().availableProcessors() * 4;
private int queueCapacity = Integer.MAX_VALUE;
}

@Data
public static class Map {

private String smallPreviewsUrlFormat = "http://content.faforever.com/faf/map_previews/small/%s";
private String largePreviewsUrlFormat = "http://content.faforever.com/faf/map_previews/large/%s";
private String downloadUrlFormat = "http://content.faforever.com/faf/vault/maps/%s";
private Path folderZipFiles = Paths.get("/content/faf/vault/maps");
private Path folderPreviewPathSmall = Paths.get("/content/faf/vault/map_previews/small");
private Path folderPreviewPathLarge = Paths.get("/content/faf/vault/map_previews/large");
private Path folderPreviewPathSmall = Paths.get("static/map_previews/small");
private Path folderPreviewPathLarge = Paths.get("static/map_previews/large");
private int previewSizeSmall = 128;
private int previewSizeLarge = 512;
private String[] allowedExtensions = new String[]{"zip"};
Expand All @@ -66,4 +66,30 @@ public static class FeaturedMods {
public static class Clan {
private long inviteLinkExpireDurationInMinutes = Duration.ofDays(3).toMinutes();
}

@Data
public static class GitHub {
private String webhookSecret;
private String repositoriesDirectory;
private String accessToken;
private String deploymentEnvironment;
}

@Data
public static class Deployment {
private String targetFolder;
private String repositoriesFolder;
private String filesFolderFormat = "updates_%s_files";
private String forgedAllianceExePath;
private List<DeploymentConfiguration> configurations = new ArrayList<>();

@Data
public static class DeploymentConfiguration {
private String repositoryUrl;
private String branch;
private String modName;
private String modFilesExtension;
private boolean replaceExisting;
}
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/faforever/api/config/GitHubConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.faforever.api.config;

import org.kohsuke.github.GitHub;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

@Configuration
public class GitHubConfig {
@Bean
public GitHub gitHub(FafApiProperties fafApiProperties) throws IOException {
return GitHub.connectUsingOAuth(fafApiProperties.getGitHub().getAccessToken());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ protected void configure(HttpSecurity http) throws Exception {
// Additional APIs
.antMatchers("/leaderboards/**").permitAll()
.antMatchers("/featuredMods/**").permitAll()
.antMatchers("/gitHub/webhook").permitAll()
// Redirects to Swagger UI
.antMatchers("/").permitAll()
// Swagger UI
Expand Down
72 changes: 72 additions & 0 deletions src/main/java/com/faforever/api/deployment/GitHubController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.faforever.api.deployment;

import com.faforever.api.config.FafApiProperties;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.kohsuke.github.GHEventPayload;
import org.kohsuke.github.GHEventPayload.Deployment;
import org.kohsuke.github.GHEventPayload.Push;
import org.kohsuke.github.GitHub;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.jwt.crypto.sign.MacSigner;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;

@RestController
@RequestMapping(path = "/gitHub")
@Slf4j
public class GitHubController {

private static final String HMAC_SHA1 = "HmacSHA1";
private final GitHubDeploymentService gitHubDeploymentService;
private final GitHub gitHub;
private final FafApiProperties apiProperties;

public GitHubController(GitHubDeploymentService gitHubDeploymentService, GitHub gitHub, FafApiProperties apiProperties) {
this.gitHubDeploymentService = gitHubDeploymentService;
this.gitHub = gitHub;
this.apiProperties = apiProperties;
}

@Async
@RequestMapping(path = "/webhook", method = RequestMethod.POST)
@SneakyThrows
public void onPush(@RequestBody String body,
@RequestHeader("X-Hub-Signature") String signature,
@RequestHeader("X-GitHub-Event") String eventType) {
verifyRequest(body, signature);
switch (eventType) {
case "push":
gitHubDeploymentService.createDeploymentIfEligible(parseEvent(body, Push.class));
break;
case "deployment":
gitHubDeploymentService.deploy(parseEvent(body, Deployment.class).getDeployment());
break;
default:
log.warn("Unhandled event: " + eventType);
}
}

@SneakyThrows
private <T extends GHEventPayload> T parseEvent(@RequestBody String body, Class<T> type) {
return gitHub.parseEventPayload(new StringReader(body), type);
}

@SneakyThrows
private void verifyRequest(String payload, String signature) {
String secret = apiProperties.getGitHub().getWebhookSecret();
MacSigner macSigner = new MacSigner(HMAC_SHA1, new SecretKeySpec(secret.getBytes(StandardCharsets.US_ASCII), HMAC_SHA1));

byte[] content = payload.getBytes(StandardCharsets.US_ASCII);
// Signature starts with "sha1="
macSigner.verify(content, DatatypeConverter.parseHexBinary(signature.substring(5)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.faforever.api.deployment;

import com.faforever.api.config.FafApiProperties;
import com.faforever.api.config.FafApiProperties.Deployment.DeploymentConfiguration;
import com.faforever.api.error.ProgrammingError;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.kohsuke.github.GHDeployment;
import org.kohsuke.github.GHEventPayload.Push;
import org.kohsuke.github.GHEventPayload.Push.PushCommit;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.Optional;


@Service
@Slf4j
public class GitHubDeploymentService {

private final ApplicationContext applicationContext;
private final FafApiProperties fafApiProperties;
private final ObjectMapper objectMapper;

public GitHubDeploymentService(ApplicationContext applicationContext, FafApiProperties fafApiProperties, ObjectMapper objectMapper) {
this.applicationContext = applicationContext;
this.fafApiProperties = fafApiProperties;
this.objectMapper = objectMapper;
}

@SneakyThrows
public void createDeploymentIfEligible(Push push) {
List<PushCommit> commits = push.getCommits();
PushCommit headCommit = commits.get(0);

if (!Objects.equals(headCommit.getSha(), push.getHead())) {
throw new ProgrammingError("Expected first commit to be the head commit, apparently that is not the case");
}

String ref = push.getRef();
if (!headCommit.isDistinct()) {
log.debug("Ignoring non-distinct commit to ref: {}", ref);
return;
}
Optional<DeploymentConfiguration> optional = fafApiProperties.getDeployment().getConfigurations().stream()
.filter(configuration ->
push.getRepository().gitHttpTransportUrl().equals(configuration.getRepositoryUrl())
&& push.getRef().replace("refs/heads/", "").equals(configuration.getBranch()))
.findFirst();

if (!optional.isPresent()) {
log.warn("No configuration present for repository '{}' and ref '{}'", push.getRepository().gitHttpTransportUrl(), push.getRef());
return;
}

GHDeployment ghDeployment = push.getRepository().createDeployment(ref)
.environment(fafApiProperties.getGitHub().getDeploymentEnvironment())
.payload(objectMapper.writeValueAsString(optional.get()))
.create();

log.info("Created deployment: {}", ghDeployment);
}

@Async
@SneakyThrows
public void deploy(GHDeployment deployment) {
String environment = deployment.getEnvironment();
if (!fafApiProperties.getGitHub().getDeploymentEnvironment().equals(environment)) {
log.warn("Ignoring deployment for environment: {}", environment);
return;
}

applicationContext.getBean(LegacyFeaturedModDeploymentTask.class)
.setConfiguration(objectMapper.readValue(deployment.getPayload(), DeploymentConfiguration.class))
.run();
}
}
Loading

0 comments on commit 08ba8f5

Please sign in to comment.