Skip to content

Commit

Permalink
feat: add endpoints for MVP ADS projects management #3094
Browse files Browse the repository at this point in the history
  • Loading branch information
jgomer2001 committed Dec 8, 2022
1 parent cc6c5e1 commit 2b0c97a
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ private ApiConstants() {}
public static final String SESSIONID_PATH = "/{sessionId}";
public static final String USERDN_PATH = "/{userDn}";
public static final String AGAMA = "/agama";
public static final String ADS_DEPLOYMENTS = "/ads-deployment";
public static final String QNAME_PATH = "{qname}";
public static final String ENABLED = "enabled";
public static final String QNAME = "qname";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ public Set<Class<?>> getClasses() {
classes.add(HealthCheckResource.class);
classes.add(OrganizationResource.class);
classes.add(AgamaResource.class);
classes.add(ADSDeploymentsResource.class);
classes.add(SessionResource.class);

return classes;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package io.jans.configapi.rest.resource.auth;

import io.jans.ads.model.Deployment;
import io.jans.orm.model.PagedResult;
import io.jans.configapi.core.rest.ProtectedApi;
import io.jans.configapi.util.ApiAccessConstants;
import io.jans.configapi.util.ApiConstants;
import io.jans.configapi.service.auth.ADSDeploymentsService;

import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

@Path(ApiConstants.ADS_DEPLOYMENTS)
@Produces(MediaType.APPLICATION_JSON)
public class ADSDeploymentsResource extends ConfigBaseResource {

@Inject
private ADSDeploymentsService ads;

@GET
@Path("list")
@ProtectedApi(scopes = { ApiAccessConstants.AGAMA_READ_ACCESS }, groupScopes = {
ApiAccessConstants.AGAMA_WRITE_ACCESS }, superScopes = { ApiAccessConstants.SUPER_ADMIN_READ_ACCESS })
@Produces(MediaType.APPLICATION_JSON)
public Response getDeployments(@QueryParam("start") int start, @QueryParam("count") int count) {

// this is NOT a search but a paged listing
int maxcount = getMaxCount();
PagedResult<Deployment> res = ads.list(start > 0 ? start - 1 : 0, count > 0 ? count : maxcount, maxcount);
res.getEntries().forEach(d -> d.getDetails().setFolders(null));
res.setStart(start + 1);
return Response.ok(res).build();

}

@GET
@ProtectedApi(scopes = { ApiAccessConstants.AGAMA_READ_ACCESS }, groupScopes = {
ApiAccessConstants.AGAMA_WRITE_ACCESS }, superScopes = { ApiAccessConstants.SUPER_ADMIN_READ_ACCESS })
@Produces(MediaType.APPLICATION_JSON)
public Response getDeployment(@QueryParam("name") String projectName) {

if (projectName == null) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Project name missing in query string").build();
}

Deployment d = ads.getDeployment(projectName);

if (d == null)
return Response.status(Response.Status.NOT_FOUND)
.entity("Unknown project " + projectName).build();

if (d.getFinishedAt() == null)
return Response.noContent().build();

d.getDetails().setFolders(null);
return Response.ok(d).build();

}

@POST
@Consumes("application/zip")
@ProtectedApi(scopes = { ApiAccessConstants.AGAMA_WRITE_ACCESS },
superScopes = { ApiAccessConstants.SUPER_ADMIN_WRITE_ACCESS })
public Response deploy(@QueryParam("name") String projectName, byte[] gamaBinary) {

if (projectName == null || gamaBinary == null)
return Response.status(Response.Status.BAD_REQUEST)
.entity("Project name or binary data missing").build();

if (ads.createDeploymentTask(projectName, gamaBinary))
return Response.accepted().entity("A deployment task for project " + projectName +
" has been queued. Use the GET endpoint to poll status").build();

return Response.status(Response.Status.CONFLICT)
.entity("There is an active deployment task for " + projectName +
". Wait for an OK response from the GET endpoint").build();

}

@DELETE
@ProtectedApi(scopes = { ApiAccessConstants.AGAMA_WRITE_ACCESS },
superScopes = { ApiAccessConstants.SUPER_ADMIN_WRITE_ACCESS })
public Response undeploy(@QueryParam("name") String projectName) {

if (projectName == null)
return Response.status(Response.Status.BAD_REQUEST)
.entity("Project name missing in query string").build();

Boolean result = ads.createUndeploymentTask(projectName);

if (result == null)
return Response.status(Response.Status.NOT_FOUND)
.entity("Unknown project " + projectName).build();

if (!result)
return Response.status(Response.Status.CONFLICT)
.entity("Cannot undeploy project " + projectName + ": it is currently being deployed").build();

return Response.noContent().build();

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package io.jans.configapi.service.auth;

import io.jans.ads.model.Deployment;
import io.jans.ads.model.DeploymentDetails;
import io.jans.orm.model.PagedResult;
import io.jans.orm.PersistenceEntryManager;
import io.jans.orm.search.filter.Filter;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import java.util.Base64;
import java.util.Date;

import org.slf4j.Logger;

import static java.nio.charset.StandardCharsets.UTF_8;

@ApplicationScoped
public class ADSDeploymentsService {

private static final String BASE_DN = "ou=deployments,ou=agama,o=jans";

@Inject
private Logger logger;

@Inject
private PersistenceEntryManager entryManager;

public PagedResult<Deployment> list(int start, int count, int maxCount) {

String[] attrs = new String[]{ "jansId", "jansStartDate", "jansActive",
"jansEndDate", "adsPrjDeplDetails" };
Filter filter = Filter.createPresenceFilter("jansId");

return entryManager.findPagedEntries(BASE_DN, Deployment.class,
filter, attrs, "jansStartDate", null, start, count, maxCount);

}

public Deployment getDeployment(String name) {

String[] attrs = new String[]{ "jansStartDate", "jansEndDate", "adsPrjDeplDetails" };
logger.info("Looking up project named {}", name);

Deployment d = null;
try {
d = entryManager.find(dnOfProject(idFromName(name)), Deployment.class, attrs);
} catch (Exception e) {
logger.warn(e.getMessage());
}
return d;

}

public boolean createDeploymentTask(String name, byte[] gamaBinary) {

Deployment d = null;
String id = idFromName(name);
try {
String[] attrs = new String[]{ "jansActive", "jansEndDate", "dn" };
d = entryManager.find(dnOfProject(id), Deployment.class, attrs);
} catch (Exception e) {
logger.debug("No already existing deployment for project {}", name);
}

boolean existing = d != null;
if (existing && d.getFinishedAt() == null) {
logger.info("A deployment is still in course for this project!");

if (!d.isTaskActive()) {
logger.info("No node is in charge of this task yet");
}
return false;
}

DeploymentDetails dd = new DeploymentDetails();
dd.setProjectName(name);

if (!existing) {
d = new Deployment();
d.setDn(dnOfProject(id));
}
d.setId(id);
d.setCreatedAt(new Date());
d.setTaskActive(false);
d.setFinishedAt(null);
d.setDetails(dd);

byte[] encoded = Base64.getEncoder().encode(gamaBinary);
d.setAssets(new String(encoded, UTF_8));

logger.info("Persisting deployment task for project {}", name);
if (existing) {
entryManager.merge(d);
} else {
entryManager.persist(d);
}
return true;

}

public Boolean createUndeploymentTask(String name) {

String dn = dnOfProject(idFromName(name));
String[] attrs = new String[]{ "jansActive", "dn" };
Deployment d = null;

try {
d = entryManager.find(dn, Deployment.class, attrs);
} catch (Exception e) {
logger.warn(e.getMessage());
}

if (d == null) return null;

//A project can be undeployed when there is no deployment in course or
//when there is but no node has taken it yet
boolean deploymentInProcess = d.isTaskActive();
if (!deploymentInProcess) {
logger.info("Removing deployment of project {}", name);
entryManager.remove(dn, Deployment.class);
}

return !deploymentInProcess;

}

private static String dnOfProject(String prjId) {
return String.format("jansId=%s,%s", prjId, BASE_DN);
}

private static String idFromName(String name) {
String hash = Integer.toString(name.hashCode());
if (hash.startsWith("-")) hash = hash.substring(1);
return hash;
}

}

0 comments on commit 2b0c97a

Please sign in to comment.