package alien4cloud.deployment;
import static alien4cloud.utils.AlienUtils.safe;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Map;
import java.util.stream.Stream;
import javax.annotation.Resource;
import org.alien4cloud.tosca.catalog.repository.CsarFileRepository;
import org.alien4cloud.tosca.editor.EditorRepositoryService;
import org.alien4cloud.tosca.model.definitions.AbstractArtifact;
import org.alien4cloud.tosca.model.definitions.DeploymentArtifact;
import org.alien4cloud.tosca.model.definitions.Interface;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import alien4cloud.component.repository.ArtifactRepositoryConstants;
import alien4cloud.component.repository.IFileRepository;
import alien4cloud.deployment.exceptions.UnresolvableArtifactException;
import alien4cloud.exception.NotFoundException;
import alien4cloud.paas.model.PaaSNodeTemplate;
import alien4cloud.paas.model.PaaSRelationshipTemplate;
import alien4cloud.paas.model.PaaSTopologyDeploymentContext;
import alien4cloud.repository.services.RepositoryService;
import alien4cloud.utils.FileUtil;
import lombok.extern.slf4j.Slf4j;
/**
* Download all artifacts before deployment
*/
@Slf4j
@Component
public class ArtifactProcessorService {
@Resource
private RepositoryService repositoryService;
@Resource
private CsarFileRepository repository;
@Resource
private EditorRepositoryService editorRepositoryService;
@Resource
private IFileRepository artifactRepository;
private Path tempDir;
private String resolveArtifact(AbstractArtifact artifact) {
return repositoryService.resolveArtifact(artifact.getArtifactRef(), artifact.getRepositoryURL(), artifact.getArtifactRepository(),
artifact.getRepositoryCredential());
}
private void processLocalArtifact(AbstractArtifact artifact) {
try {
Path csarPath = repository.getExpandedCSAR(artifact.getArchiveName(), artifact.getArchiveVersion());
Path resolvedPath = csarPath.resolve(artifact.getArtifactRef());
if (!Files.exists(resolvedPath)) {
throw new UnresolvableArtifactException("Artifact could not be accessed " + artifact);
}
artifact.setArtifactPath(resolvedPath.toString());
} catch (NotFoundException e) {
throw new UnresolvableArtifactException("Artifact could not be found " + artifact, e);
}
}
private void processArtifact(AbstractArtifact artifact) {
if (ArtifactRepositoryConstants.ALIEN_ARTIFACT_REPOSITORY.equals(artifact.getArtifactRepository())) {
artifact.setArtifactPath(artifactRepository.resolveFile(artifact.getArtifactRef()).toString());
return;
}
URL artifactURL = null;
if (artifact.getRepositoryName() == null) {
// Short notation
try {
// Test if it's an URL
artifactURL = new URL(artifact.getArtifactRef());
} catch (MalformedURLException e) {
if (log.isDebugEnabled()) {
log.debug("Processing local artifact {}", artifact);
}
// Not a URL then must be a relative path to a file inside the csar
processLocalArtifact(artifact);
return;
}
}
if (log.isDebugEnabled()) {
log.debug("Processing remote artifact {}", artifact);
}
String artifactPath = resolveArtifact(artifact);
if (artifactPath == null) {
if (artifactURL != null) {
try (InputStream artifactStream = artifactURL.openStream()) {
// In a best effort try in a generic manner to obtain the artifact
Path tempPath = Files.createTempFile(tempDir, "url-artifact", FilenameUtils.getExtension(artifact.getArtifactRef()));
artifactPath = tempPath.toString();
Files.copy(artifactStream, tempPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new UnresolvableArtifactException("Artifact could not be found " + artifact, e);
}
} else {
throw new UnresolvableArtifactException("Artifact could not be found " + artifact);
}
}
if (log.isDebugEnabled()) {
log.debug("Remote artifact from {} resolved to {}", artifact.getArtifactRef(), artifactPath);
}
artifact.setArtifactPath(artifactPath);
}
private void processInterfaces(Map<String, Interface> interfaceMap) {
if (interfaceMap != null) {
interfaceMap.values().stream().filter(interfazz -> interfazz.getOperations() != null).forEach(interfazz -> interfazz.getOperations().values()
.stream().filter(operation -> operation.getImplementationArtifact() != null).forEach(operation -> {
processArtifact(operation.getImplementationArtifact());
safe(operation.getDependencies()).forEach(this::processArtifact);
}));
}
}
private void processImplementationArtifacts(PaaSTopologyDeploymentContext deploymentContext) {
if (deploymentContext.getPaaSTopology().getAllNodes() != null) {
for (PaaSNodeTemplate paaSNodeTemplate : deploymentContext.getPaaSTopology().getAllNodes().values()) {
processInterfaces(paaSNodeTemplate.getInterfaces());
if (paaSNodeTemplate.getRelationshipTemplates() != null) {
for (PaaSRelationshipTemplate relationshipTemplate : paaSNodeTemplate.getRelationshipTemplates()) {
processInterfaces(relationshipTemplate.getInterfaces());
}
}
}
}
}
private Stream<DeploymentArtifact> getDeploymentArtifactStream(PaaSTopologyDeploymentContext deploymentContext) {
return Stream.concat(
safe(deploymentContext.getDeploymentTopology().getNodeTemplates()).values().stream()
.flatMap(nodeTemplate -> safe(nodeTemplate.getArtifacts()).values().stream()),
safe(deploymentContext.getDeploymentTopology().getNodeTemplates()).values().stream()
.flatMap(nodeTemplate -> safe(nodeTemplate.getRelationships()).values().stream())
.flatMap(relationship -> safe(relationship.getArtifacts()).values().stream()));
}
private boolean isArtifactFromTopologyEditor(AbstractArtifact artifact) {
return ArtifactRepositoryConstants.ALIEN_TOPOLOGY_REPOSITORY.equals(artifact.getArtifactRepository());
}
private void processDeploymentArtifacts(PaaSTopologyDeploymentContext deploymentContext) {
if (deploymentContext.getDeploymentTopology().getNodeTemplates() != null) {
// Artifact which comes from the archive or from internal repository
getDeploymentArtifactStream(deploymentContext).filter(deploymentArtifact -> !isArtifactFromTopologyEditor(deploymentArtifact))
.forEach(this::processArtifact);
// Artifact which does not come from the archive, which comes from topology's edition
getDeploymentArtifactStream(deploymentContext).filter(this::isArtifactFromTopologyEditor).forEach(deploymentArtifact -> {
Path artifactPath = editorRepositoryService.resolveArtifact(deploymentContext.getDeploymentTopology().getInitialTopologyId(),
deploymentArtifact.getArtifactRef());
deploymentArtifact.setArtifactPath(artifactPath.toString());
});
}
}
public void processArtifacts(PaaSTopologyDeploymentContext deploymentContext) {
processImplementationArtifacts(deploymentContext);
processDeploymentArtifacts(deploymentContext);
}
@Value("${directories.alien}/${directories.upload_temp}")
public void setTempDir(String tempDir) throws IOException {
this.tempDir = FileUtil.createDirectoryIfNotExists(tempDir);
}
}