use of io.cdap.cdap.common.BadRequestException in project cdap by cdapio.
the class ArtifactClient method deleteProperties.
/**
* Delete all properties for an artifact. If no properties exist, this will be a no-op.
*
* @param artifactId the artifact to delete properties from
* @throws BadRequestException if the request is invalid. For example, if the artifact name or version is invalid
* @throws UnauthenticatedException if the request is not authorized successfully in the gateway server
* @throws ArtifactNotFoundException if the artifact does not exist
* @throws IOException if a network error occurred
*/
public void deleteProperties(ArtifactId artifactId) throws IOException, UnauthenticatedException, ArtifactNotFoundException, BadRequestException, UnauthorizedException {
String path = String.format("artifacts/%s/versions/%s/properties", artifactId.getArtifact(), artifactId.getVersion());
URL url = config.resolveNamespacedURLV3(artifactId.getParent(), path);
HttpRequest.Builder requestBuilder = HttpRequest.delete(url);
HttpRequest request = requestBuilder.build();
HttpResponse response = restClient.execute(request, config.getAccessToken(), HttpURLConnection.HTTP_BAD_REQUEST, HttpURLConnection.HTTP_NOT_FOUND);
int responseCode = response.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
throw new ArtifactNotFoundException(artifactId);
} else if (responseCode == HttpURLConnection.HTTP_BAD_REQUEST) {
throw new BadRequestException(response.getResponseBodyAsString());
}
}
use of io.cdap.cdap.common.BadRequestException in project cdap by caskdata.
the class ProgramLifecycleService method issueStop.
/**
* Issues a command to stop the specified {@link RunId} of the specified {@link ProgramId} and returns a
* {@link ListenableFuture} with the {@link ProgramRunId} for the runs that were stopped.
* Clients can wait for completion of the {@link ListenableFuture}.
*
* @param programId the {@link ProgramId program} to issue a stop for
* @param runId the runId of the program run to stop. If null, all runs of the program as returned by
* {@link ProgramRuntimeService} are stopped.
* @return a list of {@link ListenableFuture} with the {@link ProgramRunId} that clients can wait on for stop
* to complete.
* @throws NotFoundException if the app, program or run was not found
* @throws BadRequestException if an attempt is made to stop a program that is either not running or
* was started by a workflow
* @throws UnauthorizedException if the user issuing the command is not authorized to stop the program. To stop a
* program, a user requires {@link ApplicationPermission#EXECUTE} permission on
* the program.
*/
public List<ListenableFuture<ProgramRunId>> issueStop(ProgramId programId, @Nullable String runId) throws Exception {
accessEnforcer.enforce(programId, authenticationContext.getPrincipal(), ApplicationPermission.EXECUTE);
// See if the program is running as per the runtime service
Map<RunId, RuntimeInfo> runtimeInfos = findRuntimeInfo(programId, runId);
Map<ProgramRunId, RunRecordDetail> activeRunRecords = getActiveRuns(programId, runId);
if (runtimeInfos.isEmpty() && activeRunRecords.isEmpty()) {
// Error out if no run information from runtime service and from run record
Store.ensureProgramExists(programId, store.getApplication(programId.getParent()));
throw new BadRequestException(String.format("Program '%s' is not running.", programId));
}
// Stop the running program based on a combination of runtime info and run record
// It's possible that some of them are not yet available from the runtimeService due to timing
// differences between the run record was created vs being added to runtimeService
// So we retry in a loop for up to 3 seconds max to cater for those cases
Set<String> pendingStops = Stream.concat(runtimeInfos.keySet().stream().map(RunId::getId), activeRunRecords.keySet().stream().map(ProgramRunId::getRun)).collect(Collectors.toSet());
List<ListenableFuture<ProgramRunId>> futures = new ArrayList<>();
Stopwatch stopwatch = new Stopwatch().start();
Set<ProgramRunId> cancelledProvisionRuns = new HashSet<>();
while (!pendingStops.isEmpty() && stopwatch.elapsedTime(TimeUnit.SECONDS) < 3L) {
Iterator<String> iterator = pendingStops.iterator();
while (iterator.hasNext()) {
ProgramRunId activeRunId = programId.run(iterator.next());
RunRecordDetail runRecord = activeRunRecords.get(activeRunId);
if (runRecord == null) {
runRecord = store.getRun(activeRunId);
}
// Check if the program is actually started from workflow and the workflow is running
if (runRecord != null && runRecord.getProperties().containsKey("workflowrunid") && runRecord.getStatus().equals(ProgramRunStatus.RUNNING)) {
String workflowRunId = runRecord.getProperties().get("workflowrunid");
throw new BadRequestException(String.format("Cannot stop the program '%s' started by the Workflow " + "run '%s'. Please stop the Workflow.", activeRunId, workflowRunId));
}
RuntimeInfo runtimeInfo = runtimeService.lookup(programId, RunIds.fromString(activeRunId.getRun()));
// if there is a runtimeInfo, the run is in the 'starting' state or later
if (runtimeInfo != null) {
ListenableFuture<ProgramController> future = runtimeInfo.getController().stop();
futures.add(Futures.transform(future, ProgramController::getProgramRunId));
iterator.remove();
// if it was in this set, it means we cancelled a task, but it had already sent a PROVISIONED message
// by the time we cancelled it. We then waited for it to show up in the runtime service and got here.
// We added a future for this run in the lines above, but we don't want to add another duplicate future
// at the end of this loop, so remove this run from the cancelled provision runs.
cancelledProvisionRuns.remove(activeRunId);
} else {
// if there is no runtimeInfo, the run could be in the provisioning state.
Optional<ProvisioningTaskInfo> cancelledInfo = provisioningService.cancelProvisionTask(activeRunId);
cancelledInfo.ifPresent(taskInfo -> {
cancelledProvisionRuns.add(activeRunId);
// This state check is to handle a race condition where we cancel the provision task, but not in time
// to prevent it from sending the PROVISIONED notification.
// If the notification was sent, but not yet consumed, we are *not* done stopping the run.
// We have to wait for the notification to be consumed, which will start the run, and place the controller
// in the runtimeService. The next time we loop, we can find it in the runtimeService and tell it to stop.
// If the notification was not sent, then we *are* done stopping the run.
// Therefore, if the state is CREATED, we don't remove it from the iterator so that the run will get
// checked again in the next loop, when we may get the controller from the runtimeService to stop it.
// No other task states have this race condition, as the PROVISIONED notification is only sent
// after the state transitions to CREATED. Therefore it is safe to remove the runId from the iterator,
// as we know we are done stopping it.
ProvisioningOp.Status taskState = taskInfo.getProvisioningOp().getStatus();
if (taskState != ProvisioningOp.Status.CREATED) {
iterator.remove();
}
});
}
}
if (!pendingStops.isEmpty()) {
// If not able to stop all of them, it means there were some runs that didn't have a runtime info and
// didn't have a provisioning task. This can happen if the run was already finished, or the run transitioned
// from the provisioning state to the starting state during this stop operation.
// We'll get the active runs again and filter it by the pending stops. Stop will be retried for those.
Set<String> finalPendingStops = pendingStops;
activeRunRecords = getActiveRuns(programId, runId).entrySet().stream().filter(e -> finalPendingStops.contains(e.getKey().getRun())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
pendingStops = activeRunRecords.keySet().stream().map(ProgramRunId::getRun).collect(Collectors.toSet());
if (!pendingStops.isEmpty()) {
TimeUnit.MILLISECONDS.sleep(200);
}
}
}
for (ProgramRunId cancelledProvisionRun : cancelledProvisionRuns) {
SettableFuture<ProgramRunId> future = SettableFuture.create();
future.set(cancelledProvisionRun);
futures.add(future);
}
return futures;
}
use of io.cdap.cdap.common.BadRequestException in project cdap by caskdata.
the class ProgramLifecycleService method findRuntimeInfo.
private Map<RunId, ProgramRuntimeService.RuntimeInfo> findRuntimeInfo(ProgramId programId, @Nullable String runId) throws BadRequestException {
if (runId != null) {
RunId run;
try {
run = RunIds.fromString(runId);
} catch (IllegalArgumentException e) {
throw new BadRequestException("Error parsing run-id.", e);
}
ProgramRuntimeService.RuntimeInfo runtimeInfo = runtimeService.lookup(programId, run);
return runtimeInfo == null ? Collections.emptyMap() : Collections.singletonMap(run, runtimeInfo);
}
return new HashMap<>(runtimeService.list(programId));
}
use of io.cdap.cdap.common.BadRequestException in project cdap by caskdata.
the class ApplicationClientTestRun method testAppUpdate.
@Test
public void testAppUpdate() throws Exception {
String artifactName = "cfg-programs";
ArtifactId artifactIdV1 = NamespaceId.DEFAULT.artifact(artifactName, "1.0.0");
ArtifactId artifactIdV2 = NamespaceId.DEFAULT.artifact(artifactName, "2.0.0");
ApplicationId appId = NamespaceId.DEFAULT.app("ProgramsApp");
artifactClient.add(NamespaceId.DEFAULT, artifactName, () -> Files.newInputStream(createAppJarFile(ConfigurableProgramsApp.class).toPath()), "1.0.0");
artifactClient.add(NamespaceId.DEFAULT, artifactName, () -> Files.newInputStream(createAppJarFile(ConfigurableProgramsApp2.class).toPath()), "2.0.0");
try {
// deploy the app with just the worker
ConfigurableProgramsApp.Programs conf = new ConfigurableProgramsApp.Programs("worker1", null, "dataset1");
AppRequest<ConfigurableProgramsApp.Programs> request = new AppRequest<>(new ArtifactSummary(artifactIdV1.getArtifact(), artifactIdV1.getVersion()), conf);
appClient.deploy(appId, request);
// should only have the worker
Assert.assertTrue(appClient.listPrograms(appId, ProgramType.SERVICE).isEmpty());
Assert.assertEquals(1, appClient.listPrograms(appId, ProgramType.WORKER).size());
// update to use just the service
conf = new ConfigurableProgramsApp.Programs(null, "service", "dataset1");
request = new AppRequest<>(new ArtifactSummary(artifactIdV1.getArtifact(), artifactIdV1.getVersion()), conf);
appClient.update(appId, request);
// should only have the service
Assert.assertTrue(appClient.listPrograms(appId, ProgramType.WORKER).isEmpty());
Assert.assertEquals(1, appClient.listPrograms(appId, ProgramType.SERVICE).size());
// check nonexistent app is not found
try {
appClient.update(NamespaceId.DEFAULT.app("ghost"), request);
Assert.fail();
} catch (NotFoundException e) {
// expected
}
// check different artifact name is invalid
request = new AppRequest<>(new ArtifactSummary("ghost", artifactIdV1.getVersion()), conf);
try {
appClient.update(appId, request);
Assert.fail();
} catch (BadRequestException e) {
// expected
}
// check nonexistent artifact is not found
request = new AppRequest<>(new ArtifactSummary(artifactIdV1.getArtifact(), "0.0.1"), conf);
try {
appClient.update(appId, request);
Assert.fail();
} catch (NotFoundException e) {
// expected
}
// update artifact version. This version uses a different app class with that can add a workflow
ConfigurableProgramsApp2.Programs conf2 = new ConfigurableProgramsApp2.Programs(null, null, "workflow1", "dataset1");
AppRequest<ConfigurableProgramsApp2.Programs> request2 = new AppRequest<>(new ArtifactSummary(artifactIdV2.getArtifact(), artifactIdV2.getVersion()), conf2);
appClient.update(appId, request2);
// should only have a single workflow
Assert.assertTrue(appClient.listPrograms(appId, ProgramType.WORKER).isEmpty());
Assert.assertTrue(appClient.listPrograms(appId, ProgramType.SERVICE).isEmpty());
Assert.assertEquals(1, appClient.listPrograms(appId, ProgramType.WORKFLOW).size());
} finally {
appClient.delete(appId);
appClient.waitForDeleted(appId, 30, TimeUnit.SECONDS);
artifactClient.delete(artifactIdV1);
artifactClient.delete(artifactIdV2);
}
}
use of io.cdap.cdap.common.BadRequestException in project cdap by caskdata.
the class AbstractMetadataClient method makeRequest.
private HttpResponse makeRequest(String path, HttpMethod httpMethod, @Nullable String body) throws IOException, UnauthenticatedException, BadRequestException, UnauthorizedException {
URL url = resolve(path);
HttpRequest.Builder builder = HttpRequest.builder(httpMethod, url);
if (body != null) {
builder.withBody(body);
}
HttpResponse response = execute(builder.build(), HttpURLConnection.HTTP_BAD_REQUEST, HttpURLConnection.HTTP_NOT_FOUND);
if (response.getResponseCode() == HttpURLConnection.HTTP_BAD_REQUEST) {
throw new BadRequestException(response.getResponseBodyAsString());
}
return response;
}
Aggregations