/*
* Copyright 2015 the original author or authors.
*
* 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.springframework.xd.dirt.server.admin.deployment.zk;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.xd.dirt.cluster.Container;
import org.springframework.xd.dirt.cluster.NoContainerException;
import org.springframework.xd.dirt.container.store.ContainerRepository;
import org.springframework.xd.dirt.core.DeploymentUnitStatus;
import org.springframework.xd.dirt.core.Job;
import org.springframework.xd.dirt.job.JobFactory;
import org.springframework.xd.dirt.server.admin.deployment.ContainerMatcher;
import org.springframework.xd.dirt.server.admin.deployment.DeploymentUnitStateCalculator;
import org.springframework.xd.dirt.server.admin.deployment.ModuleDeploymentPropertiesProvider;
import org.springframework.xd.dirt.server.admin.deployment.ModuleDeploymentStatus;
import org.springframework.xd.dirt.server.admin.deployment.RuntimeModuleDeploymentPropertiesProvider;
import org.springframework.xd.dirt.zookeeper.Paths;
import org.springframework.xd.dirt.zookeeper.ZooKeeperUtils;
import org.springframework.xd.module.ModuleDeploymentProperties;
import org.springframework.xd.module.ModuleDescriptor;
import org.springframework.xd.module.RuntimeModuleDeploymentProperties;
/**
* Deployment handler that is responsible for the deployment of a Job.
*
* @author Patrick Peralta
* @author Mark Fisher
* @author Ilayaperumal Gopinathan
*/
public class ZKJobDeploymentHandler extends ZKDeploymentHandler {
/**
* Logger.
*/
private static final Logger logger = LoggerFactory.getLogger(ZKJobDeploymentHandler.class);
/**
* Factory to construct {@link org.springframework.xd.dirt.core.Job} instance
*/
@Autowired
private JobFactory jobFactory;
/**
* Matcher that applies container matching criteria
*/
@Autowired
private ContainerMatcher containerMatcher;
/**
* Repository for the containers
*/
@Autowired
private ContainerRepository containerRepository;
/**
* Utility that writes module deployment requests to ZK path
*/
@Autowired
private ModuleDeploymentWriter moduleDeploymentWriter;
/**
* Deployment unit state calculator
*/
@Autowired
private DeploymentUnitStateCalculator stateCalculator;
/**
* Deploy the Job with the given name.
*
* @param jobName the job name
* @throws Exception
*/
@Override
public void deploy(String jobName) throws Exception {
CuratorFramework client = zkConnection.getClient();
deployJob(client, DeploymentLoader.loadJob(client, jobName, jobFactory));
}
/**
* Issue deployment requests for a job. This deployment will occur if:
* <ul>
* <li>the job has not been destroyed</li>
* <li>the job has not been undeployed</li>
* <li>there is a container that can deploy the job</li>
* </ul>
*
* @param job the job instance to redeploy
* @throws InterruptedException
*/
private void deployJob(CuratorFramework client, final Job job) throws InterruptedException {
if (job != null) {
// Ensure that the path for modules used by the container to write
// ephemeral nodes exists. The presence of this path is assumed
// by the supervisor when it calculates stream state when it is
// assigned leadership. See XD-2170 for details.
try {
client.create().creatingParentsIfNeeded().forPath(Paths.build(
Paths.JOB_DEPLOYMENTS, job.getName(), Paths.MODULES));
}
catch (Exception e) {
ZooKeeperUtils.wrapAndThrowIgnoring(e, KeeperException.NodeExistsException.class);
}
String statusPath = Paths.build(Paths.JOB_DEPLOYMENTS, job.getName(), Paths.STATUS);
DeploymentUnitStatus deployingStatus = null;
try {
deployingStatus = new DeploymentUnitStatus(ZooKeeperUtils.bytesToMap(
client.getData().forPath(statusPath)));
}
catch (Exception e) {
// an exception indicates that the status has not been set
}
Assert.state(deployingStatus != null
&& deployingStatus.getState() == DeploymentUnitStatus.State.deploying,
String.format("Expected 'deploying' status for job '%s'; current status: %s",
job.getName(), deployingStatus));
ModuleDeploymentPropertiesProvider<ModuleDeploymentProperties> provider =
new DefaultModuleDeploymentPropertiesProvider(job);
try {
Collection<ModuleDeploymentStatus> deploymentStatuses = new ArrayList<ModuleDeploymentStatus>();
for (ModuleDescriptor descriptor : job.getModuleDescriptors()) {
RuntimeModuleDeploymentProperties deploymentProperties = new RuntimeModuleDeploymentProperties();
deploymentProperties.putAll(provider.propertiesForDescriptor(descriptor));
Deque<Container> matchedContainers = new ArrayDeque<Container>(containerMatcher.match(descriptor,
deploymentProperties,
containerRepository.findAll()));
// Modules count == 0
if (deploymentProperties.getCount() == 0) {
deploymentProperties.setSequence(0);
createModuleDeploymentRequestsPath(client, descriptor, deploymentProperties);
}
// Modules count > 0
else {
for (int i = 1; i <= deploymentProperties.getCount(); i++) {
deploymentProperties.setSequence(i);
createModuleDeploymentRequestsPath(client, descriptor, deploymentProperties);
}
}
RuntimeModuleDeploymentPropertiesProvider deploymentRuntimeProvider =
new RuntimeModuleDeploymentPropertiesProvider(provider);
try {
deploymentStatuses.addAll(moduleDeploymentWriter.writeDeployment(
descriptor, deploymentRuntimeProvider, matchedContainers));
}
catch (NoContainerException e) {
logger.warn("No containers available for deployment of job {}", job.getName());
}
DeploymentUnitStatus status = stateCalculator.calculate(job, provider, deploymentStatuses);
logger.info("Deployment status for job '{}': {}", job.getName(), status);
client.setData().forPath(statusPath, ZooKeeperUtils.mapToBytes(status.toMap()));
}
}
catch (InterruptedException e) {
throw e;
}
catch (Exception e) {
throw ZooKeeperUtils.wrapThrowable(e);
}
}
}
}