/* * Copyright 2013 JBoss Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.overlord.sramp.governance.services; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; import org.jboss.downloads.overlord.sramp._2013.auditing.AuditEntry; import org.jboss.downloads.overlord.sramp._2013.auditing.AuditItemType; import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.BaseArtifactType; import org.overlord.dtgov.common.Target; import org.overlord.dtgov.common.model.DtgovModel; import org.overlord.dtgov.common.targets.CustomTarget; import org.overlord.dtgov.server.i18n.Messages; import org.overlord.dtgov.services.deploy.Deployer; import org.overlord.dtgov.services.deploy.DeployerFactory; import org.overlord.sramp.atom.err.SrampAtomException; import org.overlord.sramp.client.SrampAtomApiClient; import org.overlord.sramp.client.SrampClientQuery; import org.overlord.sramp.client.query.QueryResultSet; import org.overlord.sramp.common.ArtifactType; import org.overlord.sramp.common.SrampModelUtils; import org.overlord.sramp.common.audit.AuditUtils; import org.overlord.sramp.governance.Governance; import org.overlord.sramp.governance.GovernanceConstants; import org.overlord.sramp.governance.SlashDecoder; import org.overlord.sramp.governance.SrampAtomApiClientFactory; import org.overlord.sramp.governance.ValueEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The JAX-RS resource that handles deployment specific tasks. */ @Path("/deploy") public class DeploymentResource { private static Logger logger = LoggerFactory.getLogger(DeploymentResource.class); /** * Constructor. */ public DeploymentResource() { } /** * The deployment endpoint - processes can invoke this endpoint to deploy * a deployment based on configuration of the provided target. * @param request * @param targetRef * @param uuid * @throws Exception */ @SuppressWarnings("unchecked") @POST @Path("{target}/{uuid}") @Produces("application/xml") public Map<String,ValueEntity> deploy(@Context HttpServletRequest request, @PathParam("target") String targetRef, @PathParam("uuid") String uuid) throws Exception { Governance governance = new Governance(); Map<String, ValueEntity> results = new HashMap<String,ValueEntity>(); // 0. run the decoder on the arguments targetRef = SlashDecoder.decode(targetRef); uuid = SlashDecoder.decode(uuid); // get the artifact from the repo //////////////////////////////////////////// SrampAtomApiClient client = SrampAtomApiClientFactory.createAtomApiClient(); BaseArtifactType artifact = client.getArtifactMetaData(uuid); // get the deployment environment settings //////////////////////////////////////////// Target target = governance.getTargets().get(targetRef); if (target == null) { logger.error(Messages.i18n.format("DeploymentResource.NoTarget", targetRef)); //$NON-NLS-1$ throw new SrampAtomException(Messages.i18n.format("DeploymentResource.NoTarget", targetRef)); //$NON-NLS-1$ } String targetType; String targetTypeStr; if (target.getType().equals(Target.TYPE.CUSTOM)) { targetType = ((CustomTarget) target).getCustomType(); targetTypeStr = targetType; } else { targetType = target.getType().name(); targetTypeStr = target.getType().toString(); } // get the previous version of the deployment (so we can undeploy it) //////////////////////////////////////////// BaseArtifactType prevVersionArtifact = getCurrentlyDeployedVersion(client, artifact, target); Deployer<? extends Target> deployer = DeployerFactory.createDeployer(targetType); if (deployer == null) { throw new Exception(Messages.i18n.format( "DeploymentResource.TargetTypeNotFound", target.getType())); //$NON-NLS-1$ } if (prevVersionArtifact != null) { undeploy(request, client, prevVersionArtifact, target, deployer); } // deploy the artifact (delegate based on target type) //////////////////////////////////////////// String deploymentTarget = targetTypeStr + ":"; //$NON-NLS-1$ try { deploymentTarget += ((Deployer<Target>) deployer).deploy(artifact, target, client); } catch (Exception e) { logger.error(e.getMessage(), e); results.put(GovernanceConstants.STATUS, new ValueEntity("fail")); //$NON-NLS-1$ results.put(GovernanceConstants.MESSAGE, new ValueEntity(e.getMessage())); return results; } // update the artifact meta-data to set the classifier //////////////////////////////////////////// String deploymentClassifier = target.getClassifier(); try { // refresh the artifact meta-data in case something changed since we originally retrieved it artifact = client.getArtifactMetaData(uuid); artifact.getClassifiedBy().add(deploymentClassifier); client.updateArtifactMetaData(artifact); } catch (Exception e) { logger.error(e.getMessage(), e); } // Add a custom audit record to the artifact //////////////////////////////////////////// try { AuditEntry auditEntry = new AuditEntry(); auditEntry.setType("deploy:deploy"); //$NON-NLS-1$ DatatypeFactory dtFactory = DatatypeFactory.newInstance(); XMLGregorianCalendar now = dtFactory.newXMLGregorianCalendar((GregorianCalendar)Calendar.getInstance()); auditEntry.setWhen(now); auditEntry.setWho(request.getRemoteUser()); AuditItemType item = AuditUtils.getOrCreateAuditItem(auditEntry, "deploy:info"); //$NON-NLS-1$ AuditUtils.setAuditItemProperty(item, "target", target.getName()); //$NON-NLS-1$ AuditUtils.setAuditItemProperty(item, "classifier", target.getClassifier()); //$NON-NLS-1$ client.addAuditEntry(uuid, auditEntry); } catch (Exception e) { logger.error(e.getMessage(), e); } results.put(GovernanceConstants.STATUS, new ValueEntity("success")); //$NON-NLS-1$ results.put(GovernanceConstants.TARGET, new ValueEntity(deploymentTarget)); return results; } /** * Finds the currently deployed version of the given artifact. This basically needs to * return the version of the artifact that is currently deployed to the given target. * This is important so that we can undeploy that old version prior to deploying the * new version. * @param client * @param artifact * @param target * @throws Exception */ protected BaseArtifactType getCurrentlyDeployedVersion(SrampAtomApiClient client, BaseArtifactType artifact, Target target) throws Exception { BaseArtifactType currentVersionArtifact = null; // Let's try to find the currently deployed version. String classifier = target.getClassifier(); // Try to find a currently deployed version of this artifact based on maven information. String mavenArtifactId = SrampModelUtils.getCustomProperty(artifact, "maven.artifactId"); //$NON-NLS-1$ String mavenGroupId = SrampModelUtils.getCustomProperty(artifact, "maven.groupId"); //$NON-NLS-1$ String mavenClassifier = SrampModelUtils.getCustomProperty(artifact, "maven.classifier"); //$NON-NLS-1$ if (mavenArtifactId != null && mavenGroupId != null) { String q = "/s-ramp[@maven.artifactId = ? and @maven.groupId = ? and s-ramp:exactlyClassifiedByAllOf(., ?)]"; //$NON-NLS-1$ if (mavenClassifier != null) { q = "/s-ramp[@maven.artifactId = ? and @maven.groupId = ? and s-ramp:exactlyClassifiedByAllOf(., ?) and @maven.classifier = ?]"; //$NON-NLS-1$ } SrampClientQuery qbuilder = client.buildQuery(q).parameter(mavenArtifactId).parameter(mavenGroupId).parameter(classifier); if (mavenClassifier != null) { qbuilder.parameter(mavenClassifier); } QueryResultSet resultSet = qbuilder.count(2).query(); if (resultSet.size() == 2) { throw new Exception(Messages.i18n.format("DeploymentResource.MultipleMavenDeployments", target.getName(), artifact.getName())); //$NON-NLS-1$ } if (resultSet.size() == 1) { // Found a previous maven version deployed to the target currentVersionArtifact = client.getArtifactMetaData(resultSet.get(0)); } } // Try to find a currently deployed version of this artifact based on a simple deployment name match. if (currentVersionArtifact == null) { String name = artifact.getName(); QueryResultSet resultSet = client.buildQuery("/s-ramp[@name = ? and s-ramp:exactlyClassifiedByAllOf(., ?)]") //$NON-NLS-1$ .parameter(name).parameter(classifier).count(2).query(); if (resultSet.size() == 2) { throw new Exception(Messages.i18n.format("DeploymentResource.MultipleSimpleDeployments", target.getName(), artifact.getName())); //$NON-NLS-1$ } if (resultSet.size() == 1) { // Found a previous maven version deployed to the target currentVersionArtifact = client.getArtifactMetaData(resultSet.get(0)); } } // Try to find a currently deployed version of this artifact based on a simple deployment name match. if (currentVersionArtifact == null) { // TODO: try to find currently deployed version of this artifact based on some form of versioning (TBD) } return currentVersionArtifact; } /** * Undeploys the given artifact. Uses information recorded when that artifact was originally * deployed (see {@link #recordUndeploymentInfo(BaseArtifactType, Target, Map, SrampAtomApiClient)}). * @param request * @param client * @param prevVersionArtifact * @param target * @param deployer * @throws Exception */ @SuppressWarnings("unchecked") protected void undeploy(HttpServletRequest request, SrampAtomApiClient client, BaseArtifactType prevVersionArtifact, Target target, Deployer<? extends Target> deployer) throws Exception { // Find the undeployment information for the artifact QueryResultSet resultSet = client.buildQuery("/s-ramp/ext/" + DtgovModel.UndeploymentInformationType + "[describesDeployment[@uuid = ?] and @deploy.target = ?]") //$NON-NLS-1$ //$NON-NLS-2$ .parameter(prevVersionArtifact.getUuid()).parameter(target.getName()).count(2).query(); if (resultSet.size() == 1) { // Found it BaseArtifactType undeployInfo = client.getArtifactMetaData(resultSet.get(0)); ((Deployer<Target>) deployer).undeploy(prevVersionArtifact, undeployInfo, target, client); String deploymentClassifier = SrampModelUtils.getCustomProperty(undeployInfo, DtgovModel.CUSTOM_PROPERTY_DEPLOY_CLASSIFIER); // re-fetch the artifact to get the latest meta-data prevVersionArtifact = client.getArtifactMetaData(ArtifactType.valueOf(prevVersionArtifact), prevVersionArtifact.getUuid()); // remove the deployment classifier from the deployment prevVersionArtifact.getClassifiedBy().remove(deploymentClassifier); client.updateArtifactMetaData(prevVersionArtifact); // remove the undeployment information (no longer needed) client.deleteArtifact(undeployInfo.getUuid(), ArtifactType.valueOf(undeployInfo)); // Add a custom audit record to the artifact //////////////////////////////////////////// try { AuditEntry auditEntry = new AuditEntry(); auditEntry.setType("deploy:undeploy"); //$NON-NLS-1$ DatatypeFactory dtFactory = DatatypeFactory.newInstance(); XMLGregorianCalendar now = dtFactory.newXMLGregorianCalendar((GregorianCalendar)Calendar.getInstance()); auditEntry.setWhen(now); auditEntry.setWho(request.getRemoteUser()); AuditItemType item = AuditUtils.getOrCreateAuditItem(auditEntry, "deploy:info"); //$NON-NLS-1$ AuditUtils.setAuditItemProperty(item, "target", target.getName()); //$NON-NLS-1$ AuditUtils.setAuditItemProperty(item, "classifier", target.getClassifier()); //$NON-NLS-1$ client.addAuditEntry(prevVersionArtifact.getUuid(), auditEntry); } catch (Exception e) { logger.error(e.getMessage(), e); } } else { logger.warn(Messages.i18n.format("DeploymentResource.UndeploymentInfoNotFound", prevVersionArtifact.getName())); //$NON-NLS-1$ } } }