Commit c33a65a7 authored by Christos Tranoris's avatar Christos Tranoris
Browse files

Pull latest changes from remote before writing

parent f70ded1c
Loading
Loading
Loading
Loading
+67 −7
Original line number Diff line number Diff line
@@ -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",
@@ -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);
@@ -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);
@@ -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 = "";

+1 −1
Original line number Diff line number Diff line
@@ -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: