Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
minwoox committed Jan 17, 2025
1 parent 0e9f087 commit a8188b2
Show file tree
Hide file tree
Showing 45 changed files with 872 additions and 453 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import static java.util.Objects.requireNonNull;

import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Nullable;

Expand All @@ -34,6 +36,15 @@
@JsonInclude(Include.NON_NULL)
public class MirrorRequest {

// TODO(minwoox): remove ._ from the ID which violates Google AIP.
public static final Pattern PROJECT_CREDENTIAL_ID_PATTERN =
Pattern.compile("^projects/([^/]+)/credentials/([a-z](?:[a-z0-9-_.]{0,61}[a-z0-9])?)$");

// TODO(minwoox): remove ._ from the ID.
public static final Pattern REPO_CREDENTIAL_ID_PATTERN =
Pattern.compile(
"^projects/([^/]+)/repos/([^/]+)/credentials/([a-z](?:[a-z0-9-_.]{0,61}[a-z0-9])?)$");

private final String id;
private final boolean enabled;
private final String projectName;
Expand Down Expand Up @@ -79,10 +90,56 @@ public MirrorRequest(@JsonProperty("id") String id,
this.remotePath = requireNonNull(remotePath, "remotePath");
this.remoteBranch = requireNonNull(remoteBranch, "remoteBranch");
this.gitignore = gitignore;
this.credentialId = requireNonNull(credentialId, "credentialId");
this.credentialId = validateCredentialId(projectName, localRepo, credentialId);
this.zone = zone;
}

private static String validateCredentialId(String projectName, String localRepo,
String credentialId) {
requireNonNull(credentialId, "credentialId");
if (credentialId.isEmpty()) {
// Allow an empty credential ID for Credential.FALLBACK.
return "";

Check warning on line 102 in common/src/main/java/com/linecorp/centraldogma/internal/api/v1/MirrorRequest.java

View check run for this annotation

Codecov / codecov/patch

common/src/main/java/com/linecorp/centraldogma/internal/api/v1/MirrorRequest.java#L102

Added line #L102 was not covered by tests
}

Matcher matcher = PROJECT_CREDENTIAL_ID_PATTERN.matcher(credentialId);
if (matcher.matches()) {
checkProjectName(projectName, matcher);
return credentialId;
}

matcher = REPO_CREDENTIAL_ID_PATTERN.matcher(credentialId);
if (!matcher.matches()) {
throw new IllegalArgumentException("invalid credentialId: " + credentialId + " (expected: " +
PROJECT_CREDENTIAL_ID_PATTERN.pattern() + " or " +
REPO_CREDENTIAL_ID_PATTERN.pattern() + ')');
}
checkProjectName(projectName, matcher);
final String repoNameGroup = matcher.group(2);
if (!localRepo.equals(repoNameGroup)) {
throw new IllegalArgumentException("localRepo and credentialId do not match: " +
localRepo + " vs " + repoNameGroup);
}

return credentialId;
}

private static void checkProjectName(String projectName, Matcher matcher) {
final String projectNameGroup = matcher.group(1);
if (!projectName.equals(projectNameGroup)) {
throw new IllegalArgumentException("projectName and credentialId do not match: " +
projectName + " vs " + projectNameGroup);
}
}

public static String projectMirrorCredentialId(String projectName, String credentialId) {
return "projects/" + projectName + "/credentials/" + credentialId;
}

public static String repoMirrorCredentialId(String projectName, String repoName, String credentialId) {
return "projects/" + projectName + "/repos/" + repoName + "/credentials/" + credentialId;
}

@JsonProperty("id")
public String id() {
return id;
Expand Down Expand Up @@ -164,21 +221,21 @@ public boolean equals(Object o) {
if (!(o instanceof MirrorRequest)) {
return false;
}
final MirrorRequest mirrorDto = (MirrorRequest) o;
return id.equals(mirrorDto.id) &&
enabled == mirrorDto.enabled &&
projectName.equals(mirrorDto.projectName) &&
Objects.equals(schedule, mirrorDto.schedule) &&
direction.equals(mirrorDto.direction) &&
localRepo.equals(mirrorDto.localRepo) &&
localPath.equals(mirrorDto.localPath) &&
remoteScheme.equals(mirrorDto.remoteScheme) &&
remoteUrl.equals(mirrorDto.remoteUrl) &&
remotePath.equals(mirrorDto.remotePath) &&
remoteBranch.equals(mirrorDto.remoteBranch) &&
Objects.equals(gitignore, mirrorDto.gitignore) &&
credentialId.equals(mirrorDto.credentialId) &&
Objects.equals(zone, mirrorDto.zone);
final MirrorRequest mirrorRequest = (MirrorRequest) o;

Check warning on line 224 in common/src/main/java/com/linecorp/centraldogma/internal/api/v1/MirrorRequest.java

View check run for this annotation

Codecov / codecov/patch

common/src/main/java/com/linecorp/centraldogma/internal/api/v1/MirrorRequest.java#L224

Added line #L224 was not covered by tests
return id.equals(mirrorRequest.id) &&
enabled == mirrorRequest.enabled &&
projectName.equals(mirrorRequest.projectName) &&
Objects.equals(schedule, mirrorRequest.schedule) &&
direction.equals(mirrorRequest.direction) &&
localRepo.equals(mirrorRequest.localRepo) &&
localPath.equals(mirrorRequest.localPath) &&
remoteScheme.equals(mirrorRequest.remoteScheme) &&
remoteUrl.equals(mirrorRequest.remoteUrl) &&
remotePath.equals(mirrorRequest.remotePath) &&
remoteBranch.equals(mirrorRequest.remoteBranch) &&
Objects.equals(gitignore, mirrorRequest.gitignore) &&
credentialId.equals(mirrorRequest.credentialId) &&
Objects.equals(zone, mirrorRequest.zone);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright 2025 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*/
package com.linecorp.centraldogma.internal.api.v1;

import static com.linecorp.centraldogma.internal.api.v1.MirrorRequest.projectMirrorCredentialId;
import static com.linecorp.centraldogma.internal.api.v1.MirrorRequest.repoMirrorCredentialId;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import org.junit.jupiter.api.Test;

class MirrorRequestTest {

@Test
void mirrorRequest() {
assertThatThrownBy(() -> newMirror("some-id"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("invalid credentialId: some-id (expected: ");

assertThatThrownBy(() -> newMirror("projects/bar/repos/bar/credentials/credential-id"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("projectName and credentialId do not match: ");

assertThatThrownBy(() -> newMirror("projects/foo/repos/foo/credentials/credential-id"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("localRepo and credentialId do not match: ");

String credentialId = repoMirrorCredentialId("foo", "bar", "credential-id");
assertThat(newMirror(credentialId).credentialId()).isEqualTo(credentialId);

assertThatThrownBy(() -> newMirror("projects/bar/credentials/credential-id"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("projectName and credentialId do not match: ");

credentialId = projectMirrorCredentialId("foo", "credential-id");
assertThat(newMirror(credentialId).credentialId()).isEqualTo(credentialId);
}

private static MirrorRequest newMirror(String credentialId) {
return new MirrorRequest("mirror-id",
true,
"foo",
"0/1 * * * * ?",
"REMOTE_TO_LOCAL",
"bar",
"/",
"git+ssh",
"github.com/line/centraldogma-authtest.git",
"/",
"main",
null,
credentialId,
null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ void shouldNotifyMirrorEvents() {

final Mirror mirror = new AbstractMirror("my-mirror-1", true, EVERY_SECOND,
MirrorDirection.REMOTE_TO_LOCAL,
Credential.FALLBACK, r, "/",
Credential.FALLBACK, "", r, "/",
URI.create("unused://uri"), "/", "", null, null) {
@Override
protected MirrorResult mirrorLocalToRemote(File workDir, int maxNumFiles, long maxNumBytes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.linecorp.centraldogma.it.mirror.git;

import static com.linecorp.centraldogma.internal.api.v1.MirrorRequest.projectMirrorCredentialId;
import static com.linecorp.centraldogma.it.mirror.git.GitTestUtil.getFileContent;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
Expand Down Expand Up @@ -225,7 +226,8 @@ private void pushMirror(String gitUri, MirrorDirection mirrorDirection) {
" \"localPath\": \"/\"," +
" \"remoteUri\": \"" + gitUri + "\"," +
" \"schedule\": \"0 0 0 1 1 ? 2099\"," +
" \"credentialId\": \"public-key-id\"" +
" \"credentialId\": \"" +
projectMirrorCredentialId(projName, "public-key-id") + '"' +
'}'))
.push().join();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.linecorp.centraldogma.it.mirror.git;

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.linecorp.centraldogma.internal.api.v1.MirrorRequest.projectMirrorCredentialId;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_COMMIT_SECTION;
Expand Down Expand Up @@ -510,7 +511,8 @@ private void pushMirrorSettings(String localRepo, @Nullable String localPath, @N
" \"localPath\": \"" + localPath0 + "\"," +
" \"remoteUri\": \"" + remoteUri + "\"," +
" \"schedule\": \"0 0 0 1 1 ? 2099\"," +
" \"credentialId\": \"none\"," +
" \"credentialId\": \"" +
projectMirrorCredentialId(projName, "none") + "\"," +
" \"gitignore\": " + firstNonNull(gitignore, "\"\"") +
'}'))
.push().join();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.linecorp.centraldogma.it.mirror.git;

import static com.google.common.base.MoreObjects.firstNonNull;
import static com.linecorp.centraldogma.internal.api.v1.MirrorRequest.projectMirrorCredentialId;
import static com.linecorp.centraldogma.it.mirror.git.GitMirrorIntegrationTest.addToGitIndex;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
Expand Down Expand Up @@ -390,7 +391,8 @@ private void pushMirrorSettings(String localRepo, @Nullable String localPath, @N
" \"remoteUri\": \"" + remoteUri + "\"," +
" \"schedule\": \"0 0 0 1 1 ? 2099\"," +
" \"gitignore\": " + firstNonNull(gitignore, "\"\"") + ',' +
" \"credentialId\": \"none\"" +
" \"credentialId\": \"" +
projectMirrorCredentialId(projName, "none") + '"' +
'}'))
.push().join();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.linecorp.centraldogma.it.mirror.git;

import static com.linecorp.centraldogma.internal.api.v1.MirrorRequest.repoMirrorCredentialId;
import static com.linecorp.centraldogma.it.mirror.git.MirrorRunnerTest.PRIVATE_KEY_FILE;
import static com.linecorp.centraldogma.it.mirror.git.MirrorRunnerTest.TEST_MIRROR_ID;
import static com.linecorp.centraldogma.it.mirror.git.MirrorRunnerTest.getCredential;
Expand Down Expand Up @@ -167,20 +168,7 @@ void testMirrorCreationEvent() throws Exception {
.isOne();
});

final MirrorRequest updating = new MirrorRequest(TEST_MIRROR_ID,
true,
TEST_PROJ,
"0/2 * * * * ?",
"REMOTE_TO_LOCAL",
TEST_REPO,
"/",
"git+ssh",
"github.com/line/centraldogma-authtest.git",
"/",
"main",
null,
PRIVATE_KEY_FILE,
null);
final MirrorRequest updating = newMirror("/foo/");

final ResponseEntity<PushResultDto> response =
client.prepare()
Expand Down Expand Up @@ -211,7 +199,7 @@ private void createMirror() throws Exception {
.execute();
assertThat(response.status()).isEqualTo(HttpStatus.CREATED);

final MirrorRequest newMirror = newMirror();
final MirrorRequest newMirror = newMirror("/");
response = client.prepare()
.post("/api/v1/projects/{proj}/repos/{repo}/mirrors")
.pathParam("proj", TEST_PROJ)
Expand All @@ -222,20 +210,20 @@ private void createMirror() throws Exception {
assertThat(response.status()).isEqualTo(HttpStatus.CREATED);
}

private static MirrorRequest newMirror() {
private static MirrorRequest newMirror(String localPath) {
return new MirrorRequest(TEST_MIRROR_ID,
true,
TEST_PROJ,
"0/1 * * * * ?",
"REMOTE_TO_LOCAL",
TEST_REPO,
"/",
localPath,
"git+ssh",
"github.com/line/centraldogma-authtest.git",
"/",
"main",
null,
PRIVATE_KEY_FILE,
repoMirrorCredentialId(TEST_PROJ, TEST_REPO, PRIVATE_KEY_FILE),
null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.linecorp.centraldogma.it.mirror.git;

import static com.linecorp.centraldogma.internal.api.v1.MirrorRequest.projectMirrorCredentialId;
import static com.linecorp.centraldogma.internal.api.v1.MirrorRequest.repoMirrorCredentialId;
import static com.linecorp.centraldogma.testing.internal.auth.TestAuthMessageUtil.PASSWORD;
import static com.linecorp.centraldogma.testing.internal.auth.TestAuthMessageUtil.USERNAME;
import static com.linecorp.centraldogma.testing.internal.auth.TestAuthMessageUtil.getAccessToken;
Expand All @@ -42,6 +44,7 @@
import com.linecorp.centraldogma.internal.api.v1.PushResultDto;
import com.linecorp.centraldogma.server.CentralDogmaBuilder;
import com.linecorp.centraldogma.server.internal.api.sysadmin.MirrorAccessControlRequest;
import com.linecorp.centraldogma.server.internal.credential.NoneCredential;
import com.linecorp.centraldogma.server.internal.credential.PublicKeyCredential;
import com.linecorp.centraldogma.server.internal.mirror.MirrorAccessControl;
import com.linecorp.centraldogma.server.mirror.MirrorResult;
Expand Down Expand Up @@ -99,17 +102,30 @@ void setUp() throws Exception {

@Test
void triggerMirroring() throws Exception {
final PublicKeyCredential credential = getCredential();
// Put an invalid credential to project with ID PRIVATE_KEY_FILE.
final NoneCredential noneCredential = new NoneCredential(PRIVATE_KEY_FILE, true);
ResponseEntity<PushResultDto> response =
systemAdminClient.prepare()
.post("/api/v1/projects/{proj}/credentials")
.pathParam("proj", FOO_PROJ)
.contentJson(credential)
.contentJson(noneCredential)
.asJson(PushResultDto.class)
.execute();
assertThat(response.status()).isEqualTo(HttpStatus.CREATED);

final MirrorRequest newMirror = newMirror();
// Put valid credential to repository with ID PRIVATE_KEY_FILE.
// This credential will be used for mirroring because it has higher priority than project credential.
final PublicKeyCredential credential = getCredential();
response = systemAdminClient.prepare()
.post("/api/v1/projects/{proj}/repos/{repo}/credentials")
.pathParam("proj", FOO_PROJ)
.pathParam("repo", BAR_REPO)
.contentJson(credential)
.asJson(PushResultDto.class)
.execute();
assertThat(response.status()).isEqualTo(HttpStatus.CREATED);

final MirrorRequest newMirror = newMirror(repoMirrorCredentialId(FOO_PROJ, BAR_REPO, PRIVATE_KEY_FILE));
response = systemAdminClient.prepare()
.post("/api/v1/projects/{proj}/repos/{repo}/mirrors")
.pathParam("proj", FOO_PROJ)
Expand Down Expand Up @@ -177,7 +193,7 @@ void shouldControlGitMirrorAccess() throws Exception {
.execute();
assertThat(response.status()).isEqualTo(HttpStatus.CREATED);

final MirrorRequest newMirror = newMirror();
final MirrorRequest newMirror = newMirror(projectMirrorCredentialId(FOO_PROJ, PRIVATE_KEY_FILE));
response = systemAdminClient.prepare()
.post("/api/v1/projects/{proj}/repos/{repo}/mirrors")
.pathParam("proj", FOO_PROJ)
Expand Down Expand Up @@ -217,7 +233,7 @@ void shouldControlGitMirrorAccess() throws Exception {
assertThat(mirrorResponse.status()).isEqualTo(HttpStatus.OK);
}

private static MirrorRequest newMirror() {
private static MirrorRequest newMirror(String credentialId) {
return new MirrorRequest(TEST_MIRROR_ID,
true,
FOO_PROJ,
Expand All @@ -230,7 +246,7 @@ private static MirrorRequest newMirror() {
"/",
"main",
null,
PRIVATE_KEY_FILE,
credentialId,
null);
}

Expand Down
Loading

0 comments on commit a8188b2

Please sign in to comment.