Loading src/main/java/org/etsi/osl/controllers/giter/service/ResourceRepoService.java +67 −7 Original line number Diff line number Diff line Loading @@ -104,7 +104,18 @@ public class ResourceRepoService { // Step 1: Validate Custom Resource against CRD schema validateCustomResource(cr, messages, operationType); // Step 2: Write Custom Resource to Git repository (filename: name-resourceid.yaml) // Step 2: Pull latest changes from remote before writing try { gitAdapter.pull(); log.info("Pulled latest changes from remote before {}", operationType); } catch (Exception pullException) { log.warn("Failed to pull latest changes before {}: {}. Proceeding anyway.", operationType, pullException.getMessage()); messages.append(String.format("Warning: Pull failed (%s), proceeding with local state. ", pullException.getMessage())); } // Step 3: Write Custom Resource to Git repository (filename: name-resourceid.yaml) crFilePath = gitAdapter.writeCustomResource(cr, resourceid); String s = String.format("Custom Resource %s in Git - Path: %s", Loading @@ -112,7 +123,7 @@ public class ResourceRepoService { messages.append(s); log.info(s); // Step 3: Commit changes // Step 4: Commit changes String commitMessage = String.format("[%s] %s %s-%s", operationType, cr.getKind(), cr.getMetadata().getName(), resourceid); String commitId = gitAdapter.commit(commitMessage, resourceid); Loading @@ -121,12 +132,10 @@ public class ResourceRepoService { messages.append(s); log.info(s); // Step 4: Push to remote gitAdapter.push(); log.info("Changes pushed to remote"); messages.append(" Pushed to remote."); // Step 5: Push to remote (with retry on conflict) pushWithRetry(messages); // Step 5: Handle reconciliation // Step 6: Handle reconciliation handleReconciliation(operationType, resourceid, cr, crFilePath, messages); s = String.format(" %s operation completed successfully", operationType); Loading Loading @@ -227,6 +236,57 @@ public class ResourceRepoService { } } /** * Push to remote with retry on conflict. * If push fails due to non-fast-forward, pulls latest changes and retries. */ private void pushWithRetry(StringBuilder messages) throws IOException { int maxRetries = 3; int retryCount = 0; while (retryCount < maxRetries) { try { gitAdapter.push(); log.info("Changes pushed to remote"); messages.append(" Pushed to remote."); return; // Success, exit loop } catch (Exception pushException) { retryCount++; String errorMsg = pushException.getMessage() != null ? pushException.getMessage() : "Unknown error"; // Check if it's a non-fast-forward error (conflict) if (errorMsg.contains("non-fast-forward") || errorMsg.contains("rejected") || errorMsg.contains("failed to push") || errorMsg.contains("fetch first")) { if (retryCount < maxRetries) { log.warn("Push failed (attempt {}/{}): {}. Pulling and retrying...", retryCount, maxRetries, errorMsg); messages.append(String.format(" Push conflict (attempt %d), pulling and retrying.", retryCount)); try { // Pull latest changes (this should merge/rebase) gitAdapter.pull(); log.info("Pulled latest changes after push conflict"); } catch (Exception pullEx) { log.error("Failed to pull after push conflict: {}", pullEx.getMessage()); messages.append(String.format(" Pull after conflict failed: %s.", pullEx.getMessage())); throw new IOException("Push conflict and unable to resolve: " + pullEx.getMessage(), pullEx); } } else { log.error("Push failed after {} retries: {}", maxRetries, errorMsg); messages.append(String.format(" Push failed after %d retries: %s.", maxRetries, errorMsg)); throw new IOException("Push failed after " + maxRetries + " retries: " + errorMsg, pushException); } } else { // Not a conflict error, fail immediately log.error("Push failed with error: {}", errorMsg); messages.append(String.format(" Push failed: %s.", errorMsg)); throw new IOException("Push failed: " + errorMsg, pushException); } } } } public Resource deleteResource( Map<String, Object> headers, ResourceUpdate r) { String resourceid = ""; Loading src/main/resources/application.yml +1 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ producer: status-watcher: poll-interval-ms: ${PRODUCER_STATUS_WATCHER_POLL_INTERVAL:10000} timeout-ms: ${PRODUCER_STATUS_WATCHER_TIMEOUT:30000} persistence-path: ${PRODUCER_STATUS_WATCHER_PERSISTENCE_PATH:} persistence-path: ${PRODUCER_STATUS_WATCHER_PERSISTENCE_PATH:/tmp/.} default-resource-status-type: ${PRODUCER_STATUS_WATCHER_DEFAULT_RESOURCE_STATUS_TYPE:UNKNOWN} default-operational-state-type: ${PRODUCER_STATUS_WATCHER_DEFAULT_OPERATIONAL_STATE_TYPE:DISABLE} status-mappings: Loading Loading
src/main/java/org/etsi/osl/controllers/giter/service/ResourceRepoService.java +67 −7 Original line number Diff line number Diff line Loading @@ -104,7 +104,18 @@ public class ResourceRepoService { // Step 1: Validate Custom Resource against CRD schema validateCustomResource(cr, messages, operationType); // Step 2: Write Custom Resource to Git repository (filename: name-resourceid.yaml) // Step 2: Pull latest changes from remote before writing try { gitAdapter.pull(); log.info("Pulled latest changes from remote before {}", operationType); } catch (Exception pullException) { log.warn("Failed to pull latest changes before {}: {}. Proceeding anyway.", operationType, pullException.getMessage()); messages.append(String.format("Warning: Pull failed (%s), proceeding with local state. ", pullException.getMessage())); } // Step 3: Write Custom Resource to Git repository (filename: name-resourceid.yaml) crFilePath = gitAdapter.writeCustomResource(cr, resourceid); String s = String.format("Custom Resource %s in Git - Path: %s", Loading @@ -112,7 +123,7 @@ public class ResourceRepoService { messages.append(s); log.info(s); // Step 3: Commit changes // Step 4: Commit changes String commitMessage = String.format("[%s] %s %s-%s", operationType, cr.getKind(), cr.getMetadata().getName(), resourceid); String commitId = gitAdapter.commit(commitMessage, resourceid); Loading @@ -121,12 +132,10 @@ public class ResourceRepoService { messages.append(s); log.info(s); // Step 4: Push to remote gitAdapter.push(); log.info("Changes pushed to remote"); messages.append(" Pushed to remote."); // Step 5: Push to remote (with retry on conflict) pushWithRetry(messages); // Step 5: Handle reconciliation // Step 6: Handle reconciliation handleReconciliation(operationType, resourceid, cr, crFilePath, messages); s = String.format(" %s operation completed successfully", operationType); Loading Loading @@ -227,6 +236,57 @@ public class ResourceRepoService { } } /** * Push to remote with retry on conflict. * If push fails due to non-fast-forward, pulls latest changes and retries. */ private void pushWithRetry(StringBuilder messages) throws IOException { int maxRetries = 3; int retryCount = 0; while (retryCount < maxRetries) { try { gitAdapter.push(); log.info("Changes pushed to remote"); messages.append(" Pushed to remote."); return; // Success, exit loop } catch (Exception pushException) { retryCount++; String errorMsg = pushException.getMessage() != null ? pushException.getMessage() : "Unknown error"; // Check if it's a non-fast-forward error (conflict) if (errorMsg.contains("non-fast-forward") || errorMsg.contains("rejected") || errorMsg.contains("failed to push") || errorMsg.contains("fetch first")) { if (retryCount < maxRetries) { log.warn("Push failed (attempt {}/{}): {}. Pulling and retrying...", retryCount, maxRetries, errorMsg); messages.append(String.format(" Push conflict (attempt %d), pulling and retrying.", retryCount)); try { // Pull latest changes (this should merge/rebase) gitAdapter.pull(); log.info("Pulled latest changes after push conflict"); } catch (Exception pullEx) { log.error("Failed to pull after push conflict: {}", pullEx.getMessage()); messages.append(String.format(" Pull after conflict failed: %s.", pullEx.getMessage())); throw new IOException("Push conflict and unable to resolve: " + pullEx.getMessage(), pullEx); } } else { log.error("Push failed after {} retries: {}", maxRetries, errorMsg); messages.append(String.format(" Push failed after %d retries: %s.", maxRetries, errorMsg)); throw new IOException("Push failed after " + maxRetries + " retries: " + errorMsg, pushException); } } else { // Not a conflict error, fail immediately log.error("Push failed with error: {}", errorMsg); messages.append(String.format(" Push failed: %s.", errorMsg)); throw new IOException("Push failed: " + errorMsg, pushException); } } } } public Resource deleteResource( Map<String, Object> headers, ResourceUpdate r) { String resourceid = ""; Loading
src/main/resources/application.yml +1 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ producer: status-watcher: poll-interval-ms: ${PRODUCER_STATUS_WATCHER_POLL_INTERVAL:10000} timeout-ms: ${PRODUCER_STATUS_WATCHER_TIMEOUT:30000} persistence-path: ${PRODUCER_STATUS_WATCHER_PERSISTENCE_PATH:} persistence-path: ${PRODUCER_STATUS_WATCHER_PERSISTENCE_PATH:/tmp/.} default-resource-status-type: ${PRODUCER_STATUS_WATCHER_DEFAULT_RESOURCE_STATUS_TYPE:UNKNOWN} default-operational-state-type: ${PRODUCER_STATUS_WATCHER_DEFAULT_OPERATIONAL_STATE_TYPE:DISABLE} status-mappings: Loading