use of io.cdap.cdap.common.ArtifactAlreadyExistsException in project cdap by caskdata.
the class AppLifecycleHttpHandler method deployApplication.
private BodyConsumer deployApplication(final HttpResponder responder, final NamespaceId namespace, final String appId, final String archiveName, final String configString, @Nullable final String ownerPrincipal, final boolean updateSchedules) throws IOException {
Id.Namespace idNamespace = Id.Namespace.fromEntityId(namespace);
Location namespaceHomeLocation = namespacePathLocator.get(namespace);
if (!namespaceHomeLocation.exists()) {
String msg = String.format("Home directory %s for namespace %s not found", namespaceHomeLocation, namespace.getNamespace());
LOG.error(msg);
responder.sendString(HttpResponseStatus.NOT_FOUND, msg);
return null;
}
if (archiveName == null || archiveName.isEmpty()) {
responder.sendString(HttpResponseStatus.BAD_REQUEST, String.format("%s header not present. Please include the header and set its value to the jar name.", ARCHIVE_NAME_HEADER), new DefaultHttpHeaders().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE));
return null;
}
// TODO: (CDAP-3258) error handling needs to be refactored here, should be able just to throw the exception,
// but the caller catches all exceptions and responds with a 500
final Id.Artifact artifactId;
try {
artifactId = Id.Artifact.parse(idNamespace, archiveName);
} catch (IllegalArgumentException e) {
responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
return null;
}
KerberosPrincipalId ownerPrincipalId = ownerPrincipal == null ? null : new KerberosPrincipalId(ownerPrincipal);
// Store uploaded content to a local temp file
String namespacesDir = configuration.get(Constants.Namespace.NAMESPACES_DIR);
File localDataDir = new File(configuration.get(Constants.CFG_LOCAL_DATA_DIR));
File namespaceBase = new File(localDataDir, namespacesDir);
File tempDir = new File(new File(namespaceBase, namespace.getNamespace()), configuration.get(Constants.AppFabric.TEMP_DIR)).getAbsoluteFile();
if (!DirUtils.mkdirs(tempDir)) {
throw new IOException("Could not create temporary directory at: " + tempDir);
}
final KerberosPrincipalId finalOwnerPrincipalId = ownerPrincipalId;
return new AbstractBodyConsumer(File.createTempFile("app-", ".jar", tempDir)) {
@Override
protected void onFinish(HttpResponder responder, File uploadedFile) {
try {
// deploy app
ApplicationWithPrograms app = applicationLifecycleService.deployAppAndArtifact(namespace, appId, artifactId, uploadedFile, configString, finalOwnerPrincipalId, createProgramTerminator(), updateSchedules);
LOG.info("Successfully deployed app {} in namespace {} from artifact {} with configuration {} and " + "principal {}", app.getApplicationId().getApplication(), namespace.getNamespace(), artifactId, configString, finalOwnerPrincipalId);
responder.sendString(HttpResponseStatus.OK, String.format("Successfully deployed app %s", app.getApplicationId().getApplication()));
} catch (InvalidArtifactException e) {
responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
} catch (ArtifactAlreadyExistsException e) {
responder.sendString(HttpResponseStatus.CONFLICT, String.format("Artifact '%s' already exists. Please use the API that creates an application from an existing artifact. " + "If you are trying to replace the artifact, please delete it and then try again.", artifactId));
} catch (WriteConflictException e) {
// don't really expect this to happen. It means after multiple retries there were still write conflicts.
LOG.warn("Write conflict while trying to add artifact {}.", artifactId, e);
responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Write conflict while adding artifact. This can happen if multiple requests to add " + "the same artifact occur simultaneously. Please try again.");
} catch (UnauthorizedException e) {
responder.sendString(HttpResponseStatus.FORBIDDEN, e.getMessage());
} catch (ConflictException e) {
responder.sendString(HttpResponseStatus.CONFLICT, e.getMessage());
} catch (Exception e) {
LOG.error("Deploy failure", e);
responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
}
}
};
}
use of io.cdap.cdap.common.ArtifactAlreadyExistsException in project cdap by caskdata.
the class ArtifactStore method write.
/**
* Write the artifact and its metadata to the store. Once added, artifacts cannot be changed unless they are
* snapshot versions.
*
* @param artifactId the id of the artifact to add
* @param artifactMeta the metadata for the artifact
* @param artifactContent the file containing the content of the artifact
* @return detail about the newly added artifact
* @throws ArtifactAlreadyExistsException if a non-snapshot version of the artifact already exists
* @throws IOException if there was an exception persisting the artifact contents to the filesystem,
* of persisting the artifact metadata to the metastore
*/
public ArtifactDetail write(Id.Artifact artifactId, ArtifactMeta artifactMeta, File artifactContent, EntityImpersonator entityImpersonator) throws ArtifactAlreadyExistsException, IOException {
// if we're not a snapshot version, check that the artifact doesn't exist already.
if (!artifactId.getVersion().isSnapshot()) {
TransactionRunners.run(transactionRunner, context -> {
StructuredTable table = getTable(context, StoreDefinition.ArtifactStore.ARTIFACT_DATA_TABLE);
ArtifactCell artifactCell = new ArtifactCell(artifactId);
if (table.read(artifactCell.keys).isPresent()) {
throw new ArtifactAlreadyExistsException(artifactId.toEntityId());
}
}, ArtifactAlreadyExistsException.class, IOException.class);
}
final Location destination;
try {
destination = copyFileToDestination(artifactId, artifactContent, entityImpersonator);
} catch (Exception e) {
Throwables.propagateIfInstanceOf(e, IOException.class);
throw Throwables.propagate(e);
}
// now try and write the metadata for the artifact
try {
transactionRunner.run(context -> {
// we have to check that the metadata doesn't exist again since somebody else may have written
// the artifact while we were copying the artifact to the filesystem.
StructuredTable artifactDataTable = getTable(context, StoreDefinition.ArtifactStore.ARTIFACT_DATA_TABLE);
ArtifactCell artifactCell = new ArtifactCell(artifactId);
Optional<StructuredRow> optional = artifactDataTable.read(artifactCell.keys);
boolean isSnapshot = artifactId.getVersion().isSnapshot();
if (optional.isPresent() && !isSnapshot) {
// non-snapshot artifacts are immutable. If there is existing metadata, stop here.
throw new ArtifactAlreadyExistsException(artifactId.toEntityId());
}
ArtifactData data = new ArtifactData(destination, artifactMeta);
// this means cleaning up the old jar, and deleting plugin and app rows.
if (optional.isPresent()) {
deleteMeta(context, artifactId, GSON.fromJson(optional.get().getString(StoreDefinition.ArtifactStore.ARTIFACT_DATA_FIELD), ArtifactData.class));
}
// write artifact metadata
writeMeta(context, artifactId, data);
});
return new ArtifactDetail(new ArtifactDescriptor(artifactId.getNamespace().getId(), artifactId.toArtifactId(), destination), artifactMeta);
} catch (TransactionException e) {
destination.delete();
// should throw WriteConflictException(artifactId) on transaction conflict
throw TransactionRunners.propagate(e, ArtifactAlreadyExistsException.class, IOException.class);
}
}
use of io.cdap.cdap.common.ArtifactAlreadyExistsException in project cdap by caskdata.
the class HubPackage method installPlugin.
/**
* Downloads the plugin from the given URL and installs it in the artifact repository
*
* @param url URL that points to the plugin directory on the hub
* @param artifactRepository {@link ArtifactRepository} in which the plugin will be installed
* @param tmpDir temporary directory where plugin jar is downloaded from the hub
*/
public void installPlugin(URL url, ArtifactRepository artifactRepository, File tmpDir) throws Exception {
// Deserialize spec.json
URL specURL = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getPath() + "/packages/" + name + "/" + version + "/spec.json");
Spec spec = GSON.fromJson(HttpClients.doGetAsString(specURL), Spec.class);
for (Spec.Action action : spec.getActions()) {
// See https://cdap.atlassian.net/wiki/spaces/DOCS/pages/554401840/Hub+API?src=search#one_step_deploy_plugin
if (!action.getType().equals("one_step_deploy_plugin")) {
continue;
}
String configFilename = action.getConfigFilename();
if (configFilename == null) {
LOG.warn("Ignoring plugin {} due to missing config", name);
continue;
}
URL configURL = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getPath() + Joiner.on("/").join(Arrays.asList("/packages", name, version, configFilename)));
// Download plugin json from hub
JsonObject jsonObj = GSON.fromJson(HttpClients.doGetAsString(configURL), JsonObject.class);
List<String> parents = GSON.fromJson(jsonObj.get("parents"), new TypeToken<List<String>>() {
}.getType());
String jarName = action.getJarName();
if (jarName == null) {
LOG.warn("Ignoring plugin {} due to missing jar", name);
continue;
}
// Download plugin jar from hub
File destination = File.createTempFile("artifact-", ".jar", tmpDir);
FileChannel channel = new FileOutputStream(destination, false).getChannel();
URL jarURL = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getPath() + Joiner.on("/").join(Arrays.asList("/packages", name, version, jarName)));
HttpRequest request = HttpRequest.get(jarURL).withContentConsumer(new HttpContentConsumer() {
@Override
public boolean onReceived(ByteBuffer buffer) {
try {
channel.write(buffer);
} catch (IOException e) {
LOG.error("Failed write to file {}", destination);
return false;
}
return true;
}
@Override
public void onFinished() {
Closeables.closeQuietly(channel);
}
}).build();
HttpClients.executeStreamingRequest(request);
Set<ArtifactRange> parentArtifacts = new HashSet<>();
for (String parent : parents) {
try {
// try parsing it as a namespaced range like system:cdap-data-pipeline[6.3 1.1,7.0.0)
parentArtifacts.add(ArtifactRanges.parseArtifactRange(parent));
} catch (InvalidArtifactRangeException e) {
// if this failed, try parsing as a non-namespaced range like cdap-data-pipeline[6.3 1.1,7.0.0)
parentArtifacts.add(ArtifactRanges.parseArtifactRange(NamespaceId.DEFAULT.getNamespace(), parent));
}
}
// add the artifact to the repo
io.cdap.cdap.proto.id.ArtifactId artifactId = NamespaceId.DEFAULT.artifact(name, version);
Id.Artifact artifact = Id.Artifact.fromEntityId(artifactId);
try {
artifactRepository.addArtifact(artifact, destination, parentArtifacts, ImmutableSet.of());
} catch (ArtifactAlreadyExistsException e) {
LOG.debug("Artifact artifact {}-{} already exists", name, version);
}
Map<String, String> properties = GSON.fromJson(jsonObj.get("properties"), new TypeToken<Map<String, String>>() {
}.getType());
artifactRepository.writeArtifactProperties(Id.Artifact.fromEntityId(artifactId), properties);
if (!java.nio.file.Files.deleteIfExists(Paths.get(destination.getPath()))) {
LOG.warn("Failed to cleanup file {}", destination);
}
}
}
use of io.cdap.cdap.common.ArtifactAlreadyExistsException in project cdap by caskdata.
the class ArtifactClient method add.
/**
* Add an artifact.
*
* @param namespace the namespace to add the artifact to
* @param artifactName the name of the artifact to add
* @param artifactContents a provider for the contents of the artifact
* @param artifactVersion the version of the artifact to add. If null, the version will be derived from the
* manifest of the artifact
* @param parentArtifacts the set of artifacts this artifact extends
* @param additionalPlugins the set of plugins contained in the artifact that cannot be determined
* through jar inspection. This set should include any classes that are plugins but could
* not be annotated as such. For example, 3rd party classes like jdbc drivers fall into
* this category.
* @throws ArtifactAlreadyExistsException if the artifact already exists
* @throws BadRequestException if the request is invalid. For example, if the artifact name or version is invalid
* @throws ArtifactRangeNotFoundException if the parent artifacts do not exist
* @throws IOException if a network error occurred
* @throws UnauthenticatedException if the request is not authorized successfully in the gateway server
*/
public void add(NamespaceId namespace, String artifactName, ContentProvider<? extends InputStream> artifactContents, @Nullable String artifactVersion, @Nullable Set<ArtifactRange> parentArtifacts, @Nullable Set<PluginClass> additionalPlugins) throws ArtifactAlreadyExistsException, BadRequestException, IOException, UnauthenticatedException, ArtifactRangeNotFoundException, UnauthorizedException {
URL url = config.resolveNamespacedURLV3(namespace, String.format("artifacts/%s", artifactName));
HttpRequest.Builder requestBuilder = HttpRequest.post(url);
if (artifactVersion != null) {
requestBuilder.addHeader("Artifact-Version", artifactVersion);
}
if (parentArtifacts != null && !parentArtifacts.isEmpty()) {
requestBuilder.addHeader("Artifact-Extends", Joiner.on('/').join(parentArtifacts));
}
if (additionalPlugins != null && !additionalPlugins.isEmpty()) {
requestBuilder.addHeader("Artifact-Plugins", GSON.toJson(additionalPlugins));
}
HttpRequest request = requestBuilder.withBody(artifactContents).build();
HttpResponse response = restClient.execute(request, config.getAccessToken(), HttpURLConnection.HTTP_CONFLICT, HttpURLConnection.HTTP_BAD_REQUEST, HttpURLConnection.HTTP_NOT_FOUND);
int responseCode = response.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_CONFLICT) {
throw new ArtifactAlreadyExistsException(response.getResponseBodyAsString());
} else if (responseCode == HttpURLConnection.HTTP_BAD_REQUEST) {
throw new BadRequestException(response.getResponseBodyAsString());
} else if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
throw new ArtifactRangeNotFoundException(parentArtifacts);
}
}
use of io.cdap.cdap.common.ArtifactAlreadyExistsException in project cdap by cdapio.
the class ArtifactClient method add.
/**
* Add an artifact.
*
* @param namespace the namespace to add the artifact to
* @param artifactName the name of the artifact to add
* @param artifactContents a provider for the contents of the artifact
* @param artifactVersion the version of the artifact to add. If null, the version will be derived from the
* manifest of the artifact
* @param parentArtifacts the set of artifacts this artifact extends
* @param additionalPlugins the set of plugins contained in the artifact that cannot be determined
* through jar inspection. This set should include any classes that are plugins but could
* not be annotated as such. For example, 3rd party classes like jdbc drivers fall into
* this category.
* @throws ArtifactAlreadyExistsException if the artifact already exists
* @throws BadRequestException if the request is invalid. For example, if the artifact name or version is invalid
* @throws ArtifactRangeNotFoundException if the parent artifacts do not exist
* @throws IOException if a network error occurred
* @throws UnauthenticatedException if the request is not authorized successfully in the gateway server
*/
public void add(NamespaceId namespace, String artifactName, ContentProvider<? extends InputStream> artifactContents, @Nullable String artifactVersion, @Nullable Set<ArtifactRange> parentArtifacts, @Nullable Set<PluginClass> additionalPlugins) throws ArtifactAlreadyExistsException, BadRequestException, IOException, UnauthenticatedException, ArtifactRangeNotFoundException, UnauthorizedException {
URL url = config.resolveNamespacedURLV3(namespace, String.format("artifacts/%s", artifactName));
HttpRequest.Builder requestBuilder = HttpRequest.post(url);
if (artifactVersion != null) {
requestBuilder.addHeader("Artifact-Version", artifactVersion);
}
if (parentArtifacts != null && !parentArtifacts.isEmpty()) {
requestBuilder.addHeader("Artifact-Extends", Joiner.on('/').join(parentArtifacts));
}
if (additionalPlugins != null && !additionalPlugins.isEmpty()) {
requestBuilder.addHeader("Artifact-Plugins", GSON.toJson(additionalPlugins));
}
HttpRequest request = requestBuilder.withBody(artifactContents).build();
HttpResponse response = restClient.execute(request, config.getAccessToken(), HttpURLConnection.HTTP_CONFLICT, HttpURLConnection.HTTP_BAD_REQUEST, HttpURLConnection.HTTP_NOT_FOUND);
int responseCode = response.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_CONFLICT) {
throw new ArtifactAlreadyExistsException(response.getResponseBodyAsString());
} else if (responseCode == HttpURLConnection.HTTP_BAD_REQUEST) {
throw new BadRequestException(response.getResponseBodyAsString());
} else if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
throw new ArtifactRangeNotFoundException(parentArtifacts);
}
}
Aggregations