-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #46 from arkaprovob/main
feat: add symlink support, exception handling, version bump, and httpd image update #46
- Loading branch information
Showing
10 changed files
with
247 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
src/main/java/io/spaship/operator/api/CommandExecutionController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package io.spaship.operator.api; | ||
|
||
import io.quarkus.security.Authenticated; | ||
import io.smallrye.mutiny.tuples.Tuple2; | ||
import io.spaship.operator.service.k8s.CommandExecutionService; | ||
import io.spaship.operator.type.CommandExecForm; | ||
import io.spaship.operator.type.Environment; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import javax.ws.rs.Consumes; | ||
import javax.ws.rs.POST; | ||
import javax.ws.rs.Path; | ||
import javax.ws.rs.Produces; | ||
import javax.ws.rs.core.MediaType; | ||
|
||
@Path("execute") | ||
@Authenticated | ||
public class CommandExecutionController { | ||
private static final Logger LOG = LoggerFactory.getLogger(CommandExecutionController.class); | ||
|
||
private final CommandExecutionService exec; | ||
public CommandExecutionController(CommandExecutionService exec) { | ||
this.exec = exec; | ||
} | ||
|
||
@POST | ||
@Path("/symlink") | ||
@Produces("text/json") | ||
@Consumes(MediaType.APPLICATION_JSON) | ||
public String createSymlink(CommandExecForm form) { | ||
LOG.debug("form content is as follows {}", form); | ||
Tuple2<String, String> sourceTargetTuple = Tuple2.of(form.metadata().get("source"), | ||
form.metadata().get("target")); | ||
boolean isCreated = exec.createSymlink(form.environment(), sourceTargetTuple); | ||
return "{\"created\":"+isCreated+"}"; | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/main/java/io/spaship/operator/exception/CommandExecutionException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package io.spaship.operator.exception; | ||
|
||
public class CommandExecutionException extends Exception{ | ||
public CommandExecutionException(String message) { | ||
super(message); | ||
} | ||
|
||
public CommandExecutionException() { | ||
super(); | ||
} | ||
|
||
public CommandExecutionException(Throwable cause) { | ||
super(cause); | ||
} | ||
|
||
public CommandExecutionException(String s, Exception e) { | ||
super(s, e); | ||
} | ||
} |
128 changes: 128 additions & 0 deletions
128
src/main/java/io/spaship/operator/service/k8s/CommandExecutionService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package io.spaship.operator.service.k8s; | ||
|
||
import io.fabric8.kubernetes.api.model.Pod; | ||
import io.fabric8.kubernetes.api.model.Status; | ||
import io.fabric8.kubernetes.client.dsl.ExecListener; | ||
import io.fabric8.kubernetes.client.dsl.ExecWatch; | ||
import io.fabric8.openshift.client.OpenShiftClient; | ||
import io.smallrye.mutiny.tuples.Tuple2; | ||
import io.spaship.operator.exception.CommandExecutionException; | ||
import io.spaship.operator.type.ApplicationConstants; | ||
import io.spaship.operator.type.Environment; | ||
import io.spaship.operator.util.ReUsableItems; | ||
import lombok.SneakyThrows; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import javax.enterprise.context.ApplicationScoped; | ||
import javax.inject.Named; | ||
import java.util.Map; | ||
import java.util.concurrent.CountDownLatch; | ||
|
||
@ApplicationScoped | ||
public class CommandExecutionService { | ||
|
||
|
||
// todo :scope of improvement: read these two vars from the config map | ||
private static final String CONTAINER_NAME = "httpd-server"; | ||
private static final String BASE_HTTP_DIR = "/var/www/html"; | ||
|
||
|
||
private final OpenShiftClient ocClient; | ||
private static final Logger LOG = LoggerFactory.getLogger(CommandExecutionService.class); | ||
|
||
public CommandExecutionService(@Named("default") OpenShiftClient ocClient) { | ||
this.ocClient = ocClient; | ||
} | ||
|
||
|
||
public boolean createSymlink(Environment environment,Tuple2<String, String> sourceTargetTuple) { | ||
boolean success = false; | ||
ReUsableItems.checkNull(environment,sourceTargetTuple); | ||
try{ | ||
execute(podLabelFrom(environment), environment.getNameSpace(), sourceTargetTuple); | ||
success = true; | ||
}catch(Exception e){ | ||
LOG.error("failed to create symlink",e); | ||
} | ||
return success; | ||
} | ||
|
||
private Map<String,String> podLabelFrom(Environment environment) { | ||
ReUsableItems.checkNull(environment); | ||
return Map.of(ApplicationConstants.MANAGED_BY, ApplicationConstants.SPASHIP, | ||
ApplicationConstants.WEBSITE, environment.getWebsiteName().toLowerCase(), | ||
ApplicationConstants.ENVIRONMENT, environment.getName().toLowerCase()); | ||
} | ||
|
||
private void execute(Map<String, String> podLabel, String namespace, | ||
Tuple2<String, String> sourceTargetTuple) { | ||
ReUsableItems.checkNull(podLabel,namespace, sourceTargetTuple); | ||
// even changing in one of the pod will reflect in all the pods as long as the volume is shared | ||
var pod = ocClient.pods().inNamespace(namespace).withLabels(podLabel).list().getItems(); | ||
if (pod.isEmpty()) { | ||
throw new RuntimeException("No pod found for label " + podLabel); | ||
} | ||
|
||
// TODO :scope of improvement: add support for multiple commands | ||
var command = symbolicLinkCommand(sourceTargetTuple.getItem1(), sourceTargetTuple.getItem2()); | ||
|
||
pod.forEach(p -> { | ||
try { | ||
executeCommandInContainer(p, command); | ||
} catch (CommandExecutionException e) { | ||
throw new RuntimeException(e); | ||
} | ||
}); | ||
} | ||
|
||
// todo :must do: 1. check both source and destination exists 2. Validate and sanitize the | ||
// command | ||
private String[] symbolicLinkCommand(String source, String target) { | ||
ReUsableItems.checkNull(source,target); | ||
source = (BASE_HTTP_DIR.concat("/").concat(source)).toLowerCase(); | ||
target = (BASE_HTTP_DIR.concat("/").concat(target)).toLowerCase(); | ||
LOG.debug("creating a symlink of source {} to {}",source,target); | ||
LOG.debug("command to be executed [ln] [-s] [{}] [{}]",source,target); | ||
return new String[]{"ln", "-s", source, target}; | ||
} | ||
|
||
private void executeCommandInContainer(Pod httpdPod, String[] command) throws CommandExecutionException { | ||
ReUsableItems.checkNull(httpdPod,command); | ||
CountDownLatch latch = new CountDownLatch(1); | ||
try (ExecWatch ignored = ocClient.pods().inNamespace(httpdPod.getMetadata().getNamespace()) | ||
.withName(httpdPod.getMetadata().getName()) | ||
.inContainer(CONTAINER_NAME).readingInput(System.in). //TODO replace the deprecated method | ||
// with the new method | ||
writingOutput(System.out).writingError(System.err).withTTY() | ||
.usingListener(new ExecListener() { | ||
@Override | ||
public void onOpen() { | ||
LOG.debug("Executing command in container"); | ||
} | ||
|
||
@Override | ||
public void onClose(int code, String reason) { | ||
LOG.debug("closing the listener"); | ||
latch.countDown(); | ||
} | ||
|
||
@SneakyThrows | ||
@Override | ||
public void onFailure(Throwable t, Response failureResponse) { | ||
LOG.error("Failed to execute command in container due to {}",failureResponse.body()); | ||
latch.countDown(); | ||
} | ||
|
||
@Override | ||
public void onExit(int code, Status status) { | ||
LOG.error("Command executed in container code {} reason {}",code,status); | ||
latch.countDown(); | ||
} | ||
}).exec(command)) { | ||
latch.await(); | ||
} catch (Exception e) { | ||
throw new CommandExecutionException("Error while executing command in container", e); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
src/main/java/io/spaship/operator/type/ApplicationConstants.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package io.spaship.operator.type; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
public class ApplicationConstants { | ||
|
||
public static final Logger LOG = LoggerFactory.getLogger(ApplicationConstants.class); | ||
public static final String MANAGED_BY = "managedBy"; | ||
public static final String WEBSITE = "website"; | ||
public static final String ENVIRONMENT = "environment"; | ||
public static final String SPASHIP = "spaship"; | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/java/io/spaship/operator/type/CommandExecForm.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package io.spaship.operator.type; | ||
|
||
import java.util.Map; | ||
|
||
public record CommandExecForm(Environment environment, Map<String,String> metadata) { | ||
@Override | ||
public String toString() { | ||
return "CommandExecForm{" + | ||
"environment=" + environment + | ||
", metadata=" + metadata + | ||
'}'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters