Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move Mirroring Configuration to Individual Repositories #1086

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 "";
}

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 All @@ -109,6 +166,7 @@ public String direction() {
return direction;
}

// TODO(minwoox): Remove this property after migration is done.
@JsonProperty("localRepo")
public String localRepo() {
return localRepo;
Expand Down Expand Up @@ -164,21 +222,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;
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 Expand Up @@ -125,8 +125,7 @@ protected MirrorResult mirrorRemoteToLocal(File workDir, CommandExecutor executo
final DefaultMirrorAccessController ac = new DefaultMirrorAccessController();
ac.setRepository(repositoryExtension.crudRepository());
final MirrorSchedulingService service = new MirrorSchedulingService(
temporaryFolder, pm, new SimpleMeterRegistry(), 1, 1, 1, null,
ac);
temporaryFolder, pm, new SimpleMeterRegistry(), 1, 1, 1, null, false, ac);
final CommandExecutor executor = mock(CommandExecutor.class);
service.start(executor);

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 @@ -106,7 +107,7 @@ void afterEach() {
.forRepo(projName, Project.REPO_META)
.commit("cleanup",
Change.ofRemoval("/credentials/public-key-id.json"),
Change.ofRemoval("/mirrors/foo.json"))
Change.ofRemoval("/repos/" + REPO_FOO + "/mirrors/foo.json"))
.push().join();
}

Expand Down Expand Up @@ -215,7 +216,7 @@ private void pushCredentials(String pubKey, String privKey) {
private void pushMirror(String gitUri, MirrorDirection mirrorDirection) {
dogma.client().forRepo(projName, Project.REPO_META)
.commit("Add a mirror",
Change.ofJsonUpsert("/mirrors/foo.json",
Change.ofJsonUpsert("/repos/" + REPO_FOO + "/mirrors/foo.json",
'{' +
" \"id\": \"foo\"," +
" \"enabled\": true," +
Expand All @@ -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 @@ -499,8 +500,8 @@ private void pushMirrorSettings(String localRepo, @Nullable String localPath, @N
}
}
client.forRepo(projName, Project.REPO_META)
.commit("Add /mirrors/foo.json",
Change.ofJsonUpsert("/mirrors/foo.json",
.commit("Add /repos/" + localRepo + "/mirrors/foo.json",
Change.ofJsonUpsert("/repos/" + localRepo + "/mirrors/foo.json",
'{' +
" \"id\": \"foo\"," +
" \"enabled\": true," +
Expand All @@ -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 @@ -378,8 +379,8 @@ private void pushMirrorSettings(String localRepo, @Nullable String localPath, @N
}
}
client.forRepo(projName, Project.REPO_META)
.commit("Add /mirrors/foo.json",
Change.ofJsonUpsert("/mirrors/foo.json",
.commit("Add /repos/" + localRepo + "/mirrors/foo.json",
Change.ofJsonUpsert("/repos/" + localRepo + "/mirrors/foo.json",
'{' +
" \"id\": \"foo\"," +
" \"enabled\": true," +
Expand All @@ -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
Loading
Loading