From bfed3d220b35705914fd1a08d843f60a14d9dbde Mon Sep 17 00:00:00 2001 From: hitanshu310 Date: Tue, 7 Jul 2026 03:48:53 +0530 Subject: [PATCH] Hithomelabs/CFTunnels#99: address PR review - specific exceptions, SLF4J logging, health endpoint --- .../controller/DeployController.java | 40 +++++++++++++++++-- .../portainer/service/DeployService.java | 23 +++++++++-- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/portainer-automation/src/main/java/com/hithomelabs/portainer/controller/DeployController.java b/portainer-automation/src/main/java/com/hithomelabs/portainer/controller/DeployController.java index afd1e34..a5d2a4f 100644 --- a/portainer-automation/src/main/java/com/hithomelabs/portainer/controller/DeployController.java +++ b/portainer-automation/src/main/java/com/hithomelabs/portainer/controller/DeployController.java @@ -1,20 +1,29 @@ package com.hithomelabs.portainer.controller; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.hithomelabs.common.portainer.client.exception.PortainerAuthenticationException; +import com.hithomelabs.common.portainer.client.exception.PortainerConnectionException; +import com.hithomelabs.common.portainer.client.exception.PortainerDeploymentException; +import com.hithomelabs.common.portainer.client.exception.PortainerResourceNotFoundException; import com.hithomelabs.portainer.config.PortainerAutomationProperties; import com.hithomelabs.portainer.service.DeployService; @RestController -@RequestMapping("/api/deploy") +@RequestMapping("/api") public class DeployController { + private static final Logger log = LoggerFactory.getLogger(DeployController.class); + private final DeployService deployService; private final PortainerAutomationProperties props; @@ -23,17 +32,42 @@ public class DeployController { this.props = props; } - @PostMapping("/{stackId}") + @GetMapping("/health") + public ResponseEntity health() { + return ResponseEntity.ok("{\"status\":\"UP\"}"); + } + + @PostMapping("/deploy/{stackId}") public ResponseEntity deploy(@PathVariable Long stackId, @RequestHeader(value = "X-API-Key", required = false) String apiKey) { String expectedApiKey = props.getService().getApiKey(); if (expectedApiKey == null || !expectedApiKey.equals(apiKey)) { + log.warn("Deploy auth failed for stack {}: invalid API key", stackId); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid API key"); } + log.info("Deployment requested for stack {}", stackId); try { deployService.redeploy(stackId); + log.info("Deployment initiated for stack {}", stackId); return ResponseEntity.ok("Deployment initiated for stack " + stackId); - } catch (Exception e) { + } catch (PortainerConnectionException e) { + log.error("Portainer connection error for stack {}: {}", stackId, e.getMessage()); + return ResponseEntity.status(HttpStatus.BAD_GATEWAY) + .body("Portainer connection failed: " + e.getMessage()); + } catch (PortainerResourceNotFoundException e) { + log.error("Stack {} not found: {}", stackId, e.getMessage()); + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body("Stack not found: " + e.getMessage()); + } catch (PortainerAuthenticationException e) { + log.error("Portainer auth error for stack {}: {}", stackId, e.getMessage()); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED) + .body("Portainer authentication failed: " + e.getMessage()); + } catch (PortainerDeploymentException e) { + log.error("Portainer deployment error for stack {}: {}", stackId, e.getMessage()); + return ResponseEntity.status(HttpStatus.BAD_GATEWAY) + .body("Deployment failed: " + e.getMessage()); + } catch (RuntimeException e) { + log.error("Unexpected error deploying stack {}: {}", stackId, e.getMessage(), e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Deployment failed: " + e.getMessage()); } diff --git a/portainer-automation/src/main/java/com/hithomelabs/portainer/service/DeployService.java b/portainer-automation/src/main/java/com/hithomelabs/portainer/service/DeployService.java index 33bde32..989299b 100644 --- a/portainer-automation/src/main/java/com/hithomelabs/portainer/service/DeployService.java +++ b/portainer-automation/src/main/java/com/hithomelabs/portainer/service/DeployService.java @@ -1,8 +1,13 @@ package com.hithomelabs.portainer.service; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import com.hithomelabs.common.portainer.client.PortainerApiClient; +import com.hithomelabs.common.portainer.client.exception.PortainerConnectionException; +import com.hithomelabs.common.portainer.client.exception.PortainerDeploymentException; +import com.hithomelabs.common.portainer.client.exception.PortainerResourceNotFoundException; import com.hithomelabs.common.portainer.model.EnvVariable; import com.hithomelabs.common.portainer.model.PortainerStack; import com.hithomelabs.portainer.config.PortainerAutomationProperties; @@ -12,6 +17,8 @@ import java.util.List; @Service public class DeployService { + private static final Logger log = LoggerFactory.getLogger(DeployService.class); + private final PortainerApiClient portainerApiClient; private final PortainerAutomationProperties props; @@ -22,8 +29,18 @@ public class DeployService { } public void redeploy(Long stackId) { - PortainerStack stack = portainerApiClient.getStack(stackId); - List env = stack.getEnv() != null ? stack.getEnv() : List.of(); - portainerApiClient.redeployGitStack(stackId, props.getEndpointId(), true, env); + log.info("Redeploying stack {}", stackId); + try { + PortainerStack stack = portainerApiClient.getStack(stackId); + List env = stack.getEnv() != null ? stack.getEnv() : List.of(); + portainerApiClient.redeployGitStack(stackId, props.getEndpointId(), true, env); + log.info("Stack {} redeployed successfully", stackId); + } catch (PortainerResourceNotFoundException | PortainerConnectionException | PortainerDeploymentException e) { + log.error("Deploy failed for stack {}: {}", stackId, e.getMessage()); + throw e; + } catch (RuntimeException e) { + log.error("Unexpected error during deploy of stack {}: {}", stackId, e.getMessage(), e); + throw e; + } } }