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

Conversation

minwoox
Copy link
Contributor

@minwoox minwoox commented Jan 3, 2025

Motivation:
Mirroring configurations should belong to the repository where the mirroring is executed to ensure proper ownership and management alignment.

Modifications:

  • Implemented migration job that relocated mirroring configurations to their respective repositories.
    • Updated Mirroring REST API to reflect these changes.
  • Updated credential IDs in mirroring configurations to use a full resource ID format:
    • Example for a project credential: projects/foo/credentials/foo-credential.
    • Example for a repository credential: projects/foo/repos/bar/credentials/bar-credential.
  • Adjusted role requirements for credential management:
    • Project OWNER: Can create, update, and delete credentials.
    • Project MEMBER: Can read credentials.
    • Removed dependency on Meta repository access roles.

Result:

  • Mirroring configurations are now repository-specific.
  • Dependency on Meta repository access roles has been eliminated.

@minwoox
Copy link
Contributor Author

minwoox commented Jan 7, 2025

Will continue after #1085 is merged. 😉

Copy link

codecov bot commented Jan 17, 2025

Codecov Report

Attention: Patch coverage is 78.86905% with 71 lines in your changes missing coverage. Please review.

Project coverage is 70.37%. Comparing base (f4dda40) to head (a8188b2).
Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
...nal/mirror/MigratingMirrorToRepositoryService.java 72.94% 17 Missing and 6 partials ⚠️
...rp/centraldogma/internal/api/v1/MirrorRequest.java 61.90% 15 Missing and 1 partial ⚠️
...traldogma/server/internal/mirror/MirrorRunner.java 68.75% 8 Missing and 2 partials ⚠️
...rnal/storage/repository/DefaultMetaRepository.java 90.58% 6 Missing and 2 partials ⚠️
...rver/internal/storage/repository/MirrorConfig.java 77.14% 3 Missing and 5 partials ⚠️
...ldogma/server/internal/api/MirroringServiceV1.java 87.50% 2 Missing and 1 partial ⚠️
...erver/internal/mirror/MirrorSchedulingService.java 57.14% 3 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #1086      +/-   ##
============================================
+ Coverage     70.29%   70.37%   +0.08%     
- Complexity     4284     4443     +159     
============================================
  Files           427      442      +15     
  Lines         17258    17882     +624     
  Branches       1915     1974      +59     
============================================
+ Hits          12131    12585     +454     
- Misses         4077     4240     +163     
- Partials       1050     1057       +7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@minwoox minwoox changed the title Move Mirror Config to Each Repository Move Mirroring Configuration to Individual Repositories Jan 17, 2025
@minwoox minwoox added this to the 0.74.0 milestone Jan 17, 2025
Comment on lines 80 to 87
commandExecutor.execute(Command.updateServerStatus(ServerStatus.REPLICATION_ONLY))
.get(1, TimeUnit.MINUTES);
logger.info("Starting Mirrors migration ...");
if (commandExecutor instanceof ZooKeeperCommandExecutor) {
logger.debug("Waiting for 10 seconds to make sure that all cluster have been notified of the " +
"read-only mode ...");
Thread.sleep(10000);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer migrating without entering read-only mode because

  • It's not critical if mirroring is temporarily unavailable for a minute.
  • Any issues can be promptly addressed if they arise.

If this is acceptable, I will remove these lines of code. Please let me know your opinion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood the changes

if (entries.isEmpty()) {
return false;
}
repository.parent().name();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
repository.parent().name();

@ikhoon
Copy link
Contributor

ikhoon commented Jan 21, 2025

While reviewing this PR, I found out two bugs in DefaultMirrorAccessControler. Fixed at 5cae78a (#1086)

Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't fully review this PR. I will do another round tomorrow.

@@ -66,7 +67,7 @@ public CredentialServiceV1(ProjectApiManager projectApiManager, CommandExecutor
*
* <p>Returns the list of the credentials in the project.
*/
@RequiresRepositoryRole(value = RepositoryRole.READ, repository = Project.REPO_META)
@RequiresProjectRole(ProjectRole.MEMBER)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, credentials are only exposed to owners in the UI. Do you want to allow members to read credentials? It may not be harmful since all sensitive information is masked.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to allow the admin of each repository to read the credentials. Since it's read, I thought we could give the read permission to all members.

* It is in the form of {@code "projects/<project>/credentials/<credential>"} or
* {@code "projects/<project>/repos/<repo>/credentials/<credential>"}.
*/
String mirrorCredentialId();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, let me try that. 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ikhoon
I tried that approach, but it seems to make the logic and codebase more complicated.
What do you think of just removing credential.id() and using crendential.name() as we do for representing an xDS resource?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind. Let me just add the resourceName field to the classes. 😉

throw new MirrorAccessException(
"The mirroring from " + mirror.remoteRepoUri() +
" is not allowed: " +
mirrorKey.projectName + '/' + mirrorKey.mirrorId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To apply the same approach as above here

Suggested change
mirrorKey.projectName + '/' + mirrorKey.mirrorId);
mirrorKey.repoName + '/' + mirrorKey.mirrorId);

@@ -125,57 +152,124 @@ public CompletableFuture<Mirror> mirror(String id, Revision revision) {
throw new RepositoryMetadataException("failed to load the mirror configuration", e);
}

final CompletableFuture<List<Credential>> credentials;
if (Strings.isNullOrEmpty(c.credentialId())) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
if (Strings.isNullOrEmpty(c.credentialId())) {
if (c.credentialId().isEmpty()) {

for (Credential c : credentials) {
private Credential findCredential(Map<String, List<Credential>> repoCredentials,
List<Credential> projectCredentials) {
if (repoCredential) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion) This logic seems complicated to be included in a data class. What do you think of extracting the methods to MirrorConverter?

continue;
}
final MirrorConfig newMirrorConfig = mirrorConfig.withCredentialId(
projectMirrorCredentialId(repository.parent().name(), mirrorConfig.credentialId()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we skip migration for a MirrorConfig whose the credential ID starts with projects/? Because we don't rollback if the migration process is halt somehow. This migration could be partially applied.


final List<String> credentialRepoNames;
final boolean hasProjectCredential;
if (c.repoCredential()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion) What do you think of adding a new resource repository layer to find a credential from a given ID? This logic could be simplified as we don't have to care if the ID is a repo credential.

interface MetaResourceRepository {
    T find(String resourceName, Class<T> clazz);
}

Credential credential = repositoryRepository.find(mirror.credentialId(), Credential.class);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've used this logic not to fetch the resource one by one when multiple credentials are needed to be found. Let me encapsulate the logic into another class then.

Comment on lines 80 to 87
commandExecutor.execute(Command.updateServerStatus(ServerStatus.REPLICATION_ONLY))
.get(1, TimeUnit.MINUTES);
logger.info("Starting Mirrors migration ...");
if (commandExecutor instanceof ZooKeeperCommandExecutor) {
logger.debug("Waiting for 10 seconds to make sure that all cluster have been notified of the " +
"read-only mode ...");
Thread.sleep(10000);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants