Skip to content

Commit

Permalink
Add pause resume (#413)
Browse files Browse the repository at this point in the history
  • Loading branch information
olevitt committed Apr 26, 2024
1 parent 5bd5971 commit 8765544
Show file tree
Hide file tree
Showing 8 changed files with 373 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,62 @@ public class HelmInstallService {
private static final String MANIFEST_INFO_TYPE = "manifest";
private static final String NOTES_INFO_TYPE = "notes";

public void resume(
HelmConfiguration configuration,
String chart,
String namespace,
String name,
String version,
boolean dryRun,
final boolean skipTlsVerify,
String caFile)
throws InvalidExitValueException,
IOException,
InterruptedException,
TimeoutException,
IllegalArgumentException {
installChart(
configuration,
chart,
namespace,
name,
version,
dryRun,
null,
Map.of("global.suspend", "false"),
skipTlsVerify,
caFile,
true);
}

public void suspend(
HelmConfiguration configuration,
String chart,
String namespace,
String name,
String version,
boolean dryRun,
final boolean skipTlsVerify,
String caFile)
throws InvalidExitValueException,
IOException,
InterruptedException,
TimeoutException,
IllegalArgumentException {
installChart(
configuration,
chart,
namespace,
name,
version,
dryRun,
null,
Map.of("global.suspend", "true"),
skipTlsVerify,
caFile,
true);
}

public HelmInstaller installChart(
HelmConfiguration configuration,
String chart,
Expand All @@ -50,6 +106,37 @@ public HelmInstaller installChart(
InterruptedException,
TimeoutException,
IllegalArgumentException {
return installChart(
configuration,
chart,
namespace,
name,
version,
dryRun,
values,
env,
skipTlsVerify,
caFile,
false);
}

public HelmInstaller installChart(
HelmConfiguration configuration,
String chart,
String namespace,
String name,
String version,
boolean dryRun,
File values,
Map<String, String> env,
final boolean skipTlsVerify,
String caFile,
boolean reuseValues)
throws InvalidExitValueException,
IOException,
InterruptedException,
TimeoutException,
IllegalArgumentException {
StringBuilder command = new StringBuilder("helm upgrade --install ");
if (skipTlsVerify) {
command.append("--insecure-skip-tls-verify ");
Expand Down Expand Up @@ -86,6 +173,9 @@ public HelmInstaller installChart(
if (dryRun) {
command.append(" --dry-run");
}
if (reuseValues) {
command.append(" --reuse-values");
}
String res =
Command.executeAndGetResponseAsJson(configuration, command.toString())
.getOutput()
Expand Down Expand Up @@ -176,7 +266,7 @@ private String buildEnvVar(Map<String, String> env) {
if (env != null) {
Set<String> envKeys = env.keySet();
return envKeys.stream()
.map(key -> "--set " + key + "=" + env.get(key))
.map(key -> " --set " + key + "=" + env.get(key))
.collect(Collectors.joining(" "));
}
return "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import fr.insee.onyxia.api.configuration.CatalogWrapper;
import fr.insee.onyxia.api.configuration.Catalogs;
import fr.insee.onyxia.api.configuration.NotFoundException;
import fr.insee.onyxia.api.controller.exception.ServiceNotSuspendableException;
import fr.insee.onyxia.api.services.AppsService;
import fr.insee.onyxia.api.services.CatalogService;
import fr.insee.onyxia.api.services.UserProvider;
Expand Down Expand Up @@ -144,6 +145,80 @@ public Service getApp(
return null;
}

@PostMapping("/app/suspend")
public void suspendApp(
@Parameter(hidden = true) Region region,
@Parameter(hidden = true) Project project,
@RequestBody SuspendOrResumeRequestDTO request)
throws Exception {
suspendOrResume(region, project, request.getServiceID(), true);
}

@PostMapping("/app/resume")
public void resumeApp(
@Parameter(hidden = true) Region region,
@Parameter(hidden = true) Project project,
@RequestBody SuspendOrResumeRequestDTO request)
throws Exception {
suspendOrResume(region, project, request.getServiceID(), false);
}

private void suspendOrResume(Region region, Project project, String serviceId, boolean suspend)
throws Exception {
if (Service.ServiceType.KUBERNETES.equals(region.getServices().getType())) {
User user = userProvider.getUser(region);
Service userService =
helmAppsService.getUserService(
region, project, userProvider.getUser(region), serviceId);
if (!userService.isSuspendable()) {
throw new ServiceNotSuspendableException();
}
String chart = userService.getChart();
int split = chart.lastIndexOf('-');
String chartName = chart.substring(0, split);
String version = chart.substring(split + 1);
List<CatalogWrapper> elligibleCatalogs =
catalogService.getCatalogs(region, user).getCatalogs().stream()
.filter(
catalog ->
catalog.getCatalog()
.getPackageByNameAndVersion(chartName, version)
.isPresent())
.toList();
if (elligibleCatalogs.isEmpty()) {
throw new NotFoundException();
}
if (elligibleCatalogs.size() > 1) {
throw new IllegalStateException("Chart is present in multiple catalogs, abort");
}
CatalogWrapper catalog = elligibleCatalogs.getFirst();
Pkg pkg = catalog.getCatalog().getPackageByNameAndVersion(chartName, version).get();
if (suspend) {
helmAppsService.suspend(
region,
project,
catalog.getId(),
pkg,
user,
serviceId,
catalog.getSkipTlsVerify(),
catalog.getCaFile(),
false);
} else {
helmAppsService.resume(
region,
project,
catalog.getId(),
pkg,
user,
serviceId,
catalog.getSkipTlsVerify(),
catalog.getCaFile(),
false);
}
}
}

@Operation(
summary = "Get the logs of a task in an installed service.",
description =
Expand Down Expand Up @@ -356,4 +431,16 @@ private Collection<Object> publishApps(
return helmAppsService.installApp(
region, project, requestDTO, catalogId, pkg, user, fusion, skipTlsVerify, caFile);
}

public static class SuspendOrResumeRequestDTO {
private String serviceID;

public String getServiceID() {
return serviceID;
}

public void setServiceID(String serviceID) {
this.serviceID = serviceID;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package fr.insee.onyxia.api.controller.exception;

import fr.insee.onyxia.api.services.impl.HelmAppsService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class ServiceNotSuspendableException extends RuntimeException {
public ServiceNotSuspendableException() {
super(
"This service is not suspendable. To be suspendable, a service must define "
+ HelmAppsService.SUSPEND_KEY
+ " as a key in values.schema.json");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package fr.insee.onyxia.api.events;

public class SuspendResumeServiceEvent extends InstallServiceEvent {

private boolean isSuspend;

public SuspendResumeServiceEvent() {}

public SuspendResumeServiceEvent(
String username,
String namespace,
String releaseName,
String packageName,
String catalogId,
boolean suspend) {
super(username, namespace, releaseName, packageName, catalogId);
this.isSuspend = suspend;
}

@Override
public String getType() {
return isSuspend ? "service.suspend" : "service.install";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeoutException;
import org.springframework.scheduling.annotation.Async;

public interface AppsService {
Expand Down Expand Up @@ -52,4 +53,28 @@ UninstallService destroyService(

Watch getEvents(Region region, Project project, User user, Watcher<Event> watcher)
throws HelmInstallService.MultipleServiceFound, ParseException;

void resume(
Region region,
Project project,
String catalogId,
Pkg pkg,
User user,
String serviceId,
boolean skipTlsVerify,
String caFile,
boolean dryRun)
throws IOException, InterruptedException, TimeoutException;

void suspend(
Region region,
Project project,
String catalogId,
Pkg pkg,
User user,
String serviceId,
boolean skipTlsVerify,
String caFile,
boolean dryRun)
throws IOException, InterruptedException, TimeoutException;
}
Loading

0 comments on commit 8765544

Please sign in to comment.