diff --git a/addons/koji/common/src/main/java/org/commonjava/indy/koji/data/KojiRepairManager.java b/addons/koji/common/src/main/java/org/commonjava/indy/koji/data/KojiRepairManager.java index 4f13febe10..fc9882b59c 100644 --- a/addons/koji/common/src/main/java/org/commonjava/indy/koji/data/KojiRepairManager.java +++ b/addons/koji/common/src/main/java/org/commonjava/indy/koji/data/KojiRepairManager.java @@ -635,6 +635,128 @@ public KojiRepairResult repairMetadataTimeout( KojiRepairRequest request, String return repairMetadataTimeout( request, user, false ); } + public KojiMultiRepairResult repairAllVol( final String user, boolean isDryRun ) + throws KojiRepairException, IndyWorkflowException + { + KojiMultiRepairResult result = new KojiMultiRepairResult(); + + if ( opLock.tryLock() ) + { + try + { + List kojiRemotes = getAllKojiRemotes(); + + final String newStorageRoot = config.getStorageRootUrl(); + if ( newStorageRoot == null || newStorageRoot.trim().isEmpty() ) + { + throw new KojiRepairException( "Storage root URL is not configured." ); + } + + DrainingExecutorCompletionService repairService = + new DrainingExecutorCompletionService<>( repairExecutor ); + + detectOverloadVoid( () -> kojiRemotes.forEach( r -> repairService.submit( () -> { + logger.info( "Attempting to repair volume URL in Koji remote: {}", r.getKey() ); + + try + { + return repairRepositoryVol( r, user, isDryRun, newStorageRoot ); + } + catch ( KojiRepairException e ) + { + logger.error( "Failed to execute repair for: " + r.getKey(), e ); + } + + return null; + } ) ) ); + + List results = new ArrayList<>(); + try + { + repairService.drain( r -> { + if ( r != null ) + { + results.add( r ); + } + } ); + } + catch ( InterruptedException | ExecutionException e ) + { + logger.error( "Failed to repair volume URLs.", e ); + } + + result.setResults( results ); + } + catch ( IndyDataException e ) + { + throw new KojiRepairException( "Failed to list Koji remote repositories for repair. Reason: %s", e, e.getMessage() ); + } + finally + { + opLock.unlock(); + } + } + else + { + throw new KojiRepairException( "Koji repair manager is busy." ); + } + + return result; + } + + private KojiRepairResult repairRepositoryVol( RemoteRepository repository, String user, boolean isDryRun, String newStorageRoot ) + throws KojiRepairException + { + StoreKey storeKey = repository.getKey(); + KojiRepairRequest request = new KojiRepairRequest( storeKey, isDryRun ); + KojiRepairResult ret = new KojiRepairResult( request ); + + String oldUrl = repository.getUrl(); + + // Find /brewroot/ and replace everything before it with the new storage root + int brewrootIndex = oldUrl.indexOf( "/brewroot/" ); + if ( brewrootIndex == -1 ) + { + String error = String.format( "Repository URL does not contain '/brewroot/': %s", oldUrl ); + logger.warn( error ); + return ret.withError( error ); + } + + // Get the part after /brewroot (e.g., /vol/... or /packages/...) + String suffix = oldUrl.substring( brewrootIndex + "/brewroot".length() ); + String newUrl = newStorageRoot + suffix; + + boolean changed = !oldUrl.equals( newUrl ); + if ( changed ) + { + KojiRepairResult.RepairResult repairResult = new KojiRepairResult.RepairResult( storeKey ); + repairResult.withPropertyChange( "url", oldUrl, newUrl ); + ret.withResult( repairResult ); + + if ( !isDryRun ) + { + try + { + repository.setUrl( newUrl ); + ChangeSummary changeSummary = new ChangeSummary( user, "Repair " + storeKey + " volume URL to use new storage root" ); + boolean fireEvents = false; + boolean skipIfExists = false; + storeManager.storeArtifactStore( repository, changeSummary, skipIfExists, fireEvents, new EventMetadata() ); + } + catch ( IndyDataException e ) + { + throw new KojiRepairException( "Failed to repair store: %s. Reason: %s", e, storeKey, e.getMessage() ); + } + } + } + else + { + ret.withNoChange( storeKey ); + } + + return ret; + } + private List getAllKojiRemotes() throws IndyDataException diff --git a/addons/koji/jaxrs/src/main/java/org/commonjava/indy/koji/bind/jaxrs/KojiRepairResource.java b/addons/koji/jaxrs/src/main/java/org/commonjava/indy/koji/bind/jaxrs/KojiRepairResource.java index 13c7424f0c..a4b3f81db8 100644 --- a/addons/koji/jaxrs/src/main/java/org/commonjava/indy/koji/bind/jaxrs/KojiRepairResource.java +++ b/addons/koji/jaxrs/src/main/java/org/commonjava/indy/koji/bind/jaxrs/KojiRepairResource.java @@ -208,4 +208,33 @@ public KojiMultiRepairResult repairAllMetadataTimeout( final @Context HttpServle return null; } + @ApiOperation( + "Repair koji repository remote url /vol for all koji remote repositories by replacing the storage root URL." ) + @ApiImplicitParam( name = "isDryRun", paramType = "query", + value = "boolean value to specify if this request is a dry run request", defaultValue = "false", + dataType = "java.lang.Boolean" ) + @ApiResponse( code = 200, message = "Operation finished (consult response content for success/failure).", + response = KojiMultiRepairResult.class ) + @POST + @Path( "/vol/all" ) + @Consumes( ApplicationContent.application_json ) + public KojiMultiRepairResult repairAllVolumes( final @Context HttpServletRequest servletRequest, + final @QueryParam( "isDryRun" ) Boolean isDryRun, + final @Context SecurityContext securityContext ) + { + String user = securityManager.getUser( securityContext, servletRequest ); + final boolean dryRun = isDryRun == null ? false : isDryRun; + try + { + return repairManager.repairAllVol( user, dryRun ); + } + catch ( KojiRepairException | IndyWorkflowException e ) + { + logger.error( e.getMessage(), e ); + responseHelper.throwError( e ); + } + + return null; + } + }