Skip to content

Commit a5584ed

Browse files
ruhan1claude
andcommitted
Add bulk repair endpoint for Koji repository volume URLs
This change adds a new REST endpoint to repair volume URLs for all Koji remote repositories at once, replacing the storage root URL for repositories containing /brewroot/ in their URL. Changes: - Add repairAllVol() method in KojiRepairManager to process all Koji remotes - Add repairRepositoryVol() helper method to update individual repository URLs - Add new REST endpoint POST /vol/all in KojiRepairResource - Add VOL_ALL constant in IndyKojiConstants Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 9fbb28c commit a5584ed

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

addons/koji/common/src/main/java/org/commonjava/indy/koji/data/KojiRepairManager.java

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,128 @@ public KojiRepairResult repairMetadataTimeout( KojiRepairRequest request, String
635635
return repairMetadataTimeout( request, user, false );
636636
}
637637

638+
public KojiMultiRepairResult repairAllVol( final String user, boolean isDryRun )
639+
throws KojiRepairException, IndyWorkflowException
640+
{
641+
KojiMultiRepairResult result = new KojiMultiRepairResult();
642+
643+
if ( opLock.tryLock() )
644+
{
645+
try
646+
{
647+
List<RemoteRepository> kojiRemotes = getAllKojiRemotes();
648+
649+
final String newStorageRoot = config.getStorageRootUrl();
650+
if ( newStorageRoot == null || newStorageRoot.trim().isEmpty() )
651+
{
652+
throw new KojiRepairException( "Storage root URL is not configured." );
653+
}
654+
655+
DrainingExecutorCompletionService<KojiRepairResult> repairService =
656+
new DrainingExecutorCompletionService<>( repairExecutor );
657+
658+
detectOverloadVoid( () -> kojiRemotes.forEach( r -> repairService.submit( () -> {
659+
logger.info( "Attempting to repair volume URL in Koji remote: {}", r.getKey() );
660+
661+
try
662+
{
663+
return repairRepositoryVol( r, user, isDryRun, newStorageRoot );
664+
}
665+
catch ( KojiRepairException e )
666+
{
667+
logger.error( "Failed to execute repair for: " + r.getKey(), e );
668+
}
669+
670+
return null;
671+
} ) ) );
672+
673+
List<KojiRepairResult> results = new ArrayList<>();
674+
try
675+
{
676+
repairService.drain( r -> {
677+
if ( r != null )
678+
{
679+
results.add( r );
680+
}
681+
} );
682+
}
683+
catch ( InterruptedException | ExecutionException e )
684+
{
685+
logger.error( "Failed to repair volume URLs.", e );
686+
}
687+
688+
result.setResults( results );
689+
}
690+
catch ( IndyDataException e )
691+
{
692+
throw new KojiRepairException( "Failed to list Koji remote repositories for repair. Reason: %s", e, e.getMessage() );
693+
}
694+
finally
695+
{
696+
opLock.unlock();
697+
}
698+
}
699+
else
700+
{
701+
throw new KojiRepairException( "Koji repair manager is busy." );
702+
}
703+
704+
return result;
705+
}
706+
707+
private KojiRepairResult repairRepositoryVol( RemoteRepository repository, String user, boolean isDryRun, String newStorageRoot )
708+
throws KojiRepairException
709+
{
710+
StoreKey storeKey = repository.getKey();
711+
KojiRepairRequest request = new KojiRepairRequest( storeKey, isDryRun );
712+
KojiRepairResult ret = new KojiRepairResult( request );
713+
714+
String oldUrl = repository.getUrl();
715+
716+
// Find /brewroot/ and replace everything before it with the new storage root
717+
int brewrootIndex = oldUrl.indexOf( "/brewroot/" );
718+
if ( brewrootIndex == -1 )
719+
{
720+
String error = String.format( "Repository URL does not contain '/brewroot/': %s", oldUrl );
721+
logger.warn( error );
722+
return ret.withError( error );
723+
}
724+
725+
// Get the part after /brewroot (e.g., /vol/... or /packages/...)
726+
String suffix = oldUrl.substring( brewrootIndex + "/brewroot".length() );
727+
String newUrl = newStorageRoot + suffix;
728+
729+
boolean changed = !oldUrl.equals( newUrl );
730+
if ( changed )
731+
{
732+
KojiRepairResult.RepairResult repairResult = new KojiRepairResult.RepairResult( storeKey );
733+
repairResult.withPropertyChange( "url", oldUrl, newUrl );
734+
ret.withResult( repairResult );
735+
736+
if ( !isDryRun )
737+
{
738+
try
739+
{
740+
repository.setUrl( newUrl );
741+
ChangeSummary changeSummary = new ChangeSummary( user, "Repair " + storeKey + " volume URL to use new storage root" );
742+
boolean fireEvents = false;
743+
boolean skipIfExists = false;
744+
storeManager.storeArtifactStore( repository, changeSummary, skipIfExists, fireEvents, new EventMetadata() );
745+
}
746+
catch ( IndyDataException e )
747+
{
748+
throw new KojiRepairException( "Failed to repair store: %s. Reason: %s", e, storeKey, e.getMessage() );
749+
}
750+
}
751+
}
752+
else
753+
{
754+
ret.withNoChange( storeKey );
755+
}
756+
757+
return ret;
758+
}
759+
638760

639761
private List<RemoteRepository> getAllKojiRemotes()
640762
throws IndyDataException

addons/koji/jaxrs/src/main/java/org/commonjava/indy/koji/bind/jaxrs/KojiRepairResource.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,4 +208,33 @@ public KojiMultiRepairResult repairAllMetadataTimeout( final @Context HttpServle
208208
return null;
209209
}
210210

211+
@ApiOperation(
212+
"Repair koji repository remote url /vol for all koji remote repositories by replacing the storage root URL." )
213+
@ApiImplicitParam( name = "isDryRun", paramType = "query",
214+
value = "boolean value to specify if this request is a dry run request", defaultValue = "false",
215+
dataType = "java.lang.Boolean" )
216+
@ApiResponse( code = 200, message = "Operation finished (consult response content for success/failure).",
217+
response = KojiMultiRepairResult.class )
218+
@POST
219+
@Path( "/vol/all" )
220+
@Consumes( ApplicationContent.application_json )
221+
public KojiMultiRepairResult repairAllVolumes( final @Context HttpServletRequest servletRequest,
222+
final @QueryParam( "isDryRun" ) Boolean isDryRun,
223+
final @Context SecurityContext securityContext )
224+
{
225+
String user = securityManager.getUser( securityContext, servletRequest );
226+
final boolean dryRun = isDryRun == null ? false : isDryRun;
227+
try
228+
{
229+
return repairManager.repairAllVol( user, dryRun );
230+
}
231+
catch ( KojiRepairException | IndyWorkflowException e )
232+
{
233+
logger.error( e.getMessage(), e );
234+
responseHelper.throwError( e );
235+
}
236+
237+
return null;
238+
}
239+
211240
}

0 commit comments

Comments
 (0)