use of io.cdap.cdap.common.BadRequestException in project cdap by cdapio.
the class ProgramLifecycleHttpHandler method readScheduleDetailBody.
private ScheduleDetail readScheduleDetailBody(FullHttpRequest request, String scheduleName) throws BadRequestException, IOException {
JsonElement json;
try (Reader reader = new InputStreamReader(new ByteBufInputStream(request.content()), Charsets.UTF_8)) {
// The schedule spec in the request body does not contain the program information
json = DECODE_GSON.fromJson(reader, JsonElement.class);
} catch (IOException e) {
throw new IOException("Error reading request body", e);
} catch (JsonSyntaxException e) {
throw new BadRequestException("Request body is invalid json: " + e.getMessage());
}
if (!json.isJsonObject()) {
throw new BadRequestException("Expected a json object in the request body but received " + GSON.toJson(json));
}
ScheduleDetail scheduleDetail;
try {
scheduleDetail = DECODE_GSON.fromJson(json, ScheduleDetail.class);
} catch (JsonSyntaxException e) {
throw new BadRequestException("Error parsing request body as a schedule specification: " + e.getMessage());
}
// If the schedule name is present in the request body, it should match the name in path params
if (scheduleDetail.getName() != null && !scheduleName.equals(scheduleDetail.getName())) {
throw new BadRequestException(String.format("Schedule name in the body of the request (%s) does not match the schedule name in the path parameter (%s)", scheduleDetail.getName(), scheduleName));
}
return scheduleDetail;
}
use of io.cdap.cdap.common.BadRequestException in project cdap by cdapio.
the class ProgramLifecycleHttpHandler method getProgramStatusSchedules.
/**
* Get schedules containing {@link ProgramStatusTrigger} filtered by triggering program, and optionally by
* triggering program statuses or schedule status
* @param triggerNamespaceId namespace of the triggering program in {@link ProgramStatusTrigger}
* @param triggerAppName application name of the triggering program in {@link ProgramStatusTrigger}
* @param triggerAppVersion application version of the triggering program in {@link ProgramStatusTrigger}
* @param triggerProgramType program type of the triggering program in {@link ProgramStatusTrigger}
* @param triggerProgramName program name of the triggering program in {@link ProgramStatusTrigger}
* @param triggerProgramStatuses comma separated {@link ProgramStatus} in {@link ProgramStatusTrigger}.
* Schedules with {@link ProgramStatusTrigger} triggered by none of the
* {@link ProgramStatus} in triggerProgramStatuses will be filtered out.
* If not specified, schedules will be returned regardless of triggering program status.
* @param scheduleStatus status of the schedule. Can only be one of "SCHEDULED" or "SUSPENDED".
* If specified, only schedules with matching status will be returned.
*/
@GET
@Path("schedules/trigger-type/program-status")
public void getProgramStatusSchedules(HttpRequest request, HttpResponder responder, @QueryParam("trigger-namespace-id") String triggerNamespaceId, @QueryParam("trigger-app-name") String triggerAppName, @QueryParam("trigger-app-version") @DefaultValue(ApplicationId.DEFAULT_VERSION) String triggerAppVersion, @QueryParam("trigger-program-type") String triggerProgramType, @QueryParam("trigger-program-name") String triggerProgramName, @QueryParam("trigger-program-statuses") String triggerProgramStatuses, @QueryParam("schedule-status") String scheduleStatus) throws Exception {
if (triggerNamespaceId == null) {
throw new BadRequestException("Must specify trigger-namespace-id as a query param");
}
if (triggerAppName == null) {
throw new BadRequestException("Must specify trigger-app-name as a query param");
}
if (triggerProgramType == null) {
throw new BadRequestException("Must specify trigger-program-type as a query param");
}
if (triggerProgramName == null) {
throw new BadRequestException("Must specify trigger-program-name as a query param");
}
ProgramType programType = getProgramType(triggerProgramType);
ProgramScheduleStatus programScheduleStatus;
try {
programScheduleStatus = scheduleStatus == null ? null : ProgramScheduleStatus.valueOf(scheduleStatus);
} catch (IllegalArgumentException e) {
throw new BadRequestException(String.format("Invalid schedule status '%s'. Must be one of %s.", scheduleStatus, Joiner.on(',').join(ProgramScheduleStatus.values())), e);
}
ProgramId triggerProgramId = new NamespaceId(triggerNamespaceId).app(triggerAppName, triggerAppVersion).program(programType, triggerProgramName);
Set<io.cdap.cdap.api.ProgramStatus> queryProgramStatuses = new HashSet<>();
if (triggerProgramStatuses != null) {
try {
for (String status : triggerProgramStatuses.split(",")) {
queryProgramStatuses.add(io.cdap.cdap.api.ProgramStatus.valueOf(status));
}
} catch (Exception e) {
throw new BadRequestException(String.format("Unable to parse program statuses '%s'. Must be comma separated " + "valid ProgramStatus names such as COMPLETED, FAILED, KILLED.", triggerProgramStatuses), e);
}
} else {
// Query for schedules with all the statuses if no query status is specified
Collections.addAll(queryProgramStatuses, io.cdap.cdap.api.ProgramStatus.values());
}
List<ScheduleDetail> details = programScheduleService.findTriggeredBy(triggerProgramId, queryProgramStatuses).stream().filter(record -> programScheduleStatus == null || record.getMeta().getStatus().equals(programScheduleStatus)).map(ProgramScheduleRecord::toScheduleDetail).collect(Collectors.toList());
responder.sendJson(HttpResponseStatus.OK, GSON.toJson(details, Schedulers.SCHEDULE_DETAILS_TYPE));
}
use of io.cdap.cdap.common.BadRequestException in project cdap by cdapio.
the class ArtifactHttpHandler method addArtifact.
@POST
@Path("/namespaces/{namespace-id}/artifacts/{artifact-name}")
@AuditPolicy(AuditDetail.HEADERS)
public BodyConsumer addArtifact(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") final String namespaceId, @PathParam("artifact-name") final String artifactName, @HeaderParam(VERSION_HEADER) final String artifactVersion, @HeaderParam(EXTENDS_HEADER) final String parentArtifactsStr, @HeaderParam(PLUGINS_HEADER) String pluginClasses) throws NamespaceNotFoundException, BadRequestException {
final NamespaceId namespace = validateAndGetNamespace(namespaceId);
// that processes the last http chunk.
if (artifactVersion != null && !artifactVersion.isEmpty()) {
ArtifactId artifactId = validateAndGetArtifactId(namespace, artifactName, artifactVersion);
// If the artifact ID is available, use it to perform an authorization check.
contextAccessEnforcer.enforce(artifactId, StandardPermission.CREATE);
} else {
// If there is no version, we perform an enforceOnParent check in which the entityID is not needed.
contextAccessEnforcer.enforceOnParent(EntityType.ARTIFACT, namespace, StandardPermission.CREATE);
}
final Set<ArtifactRange> parentArtifacts = parseExtendsHeader(namespace, parentArtifactsStr);
final Set<PluginClass> additionalPluginClasses;
if (pluginClasses == null || pluginClasses.isEmpty()) {
additionalPluginClasses = ImmutableSet.of();
} else {
try {
additionalPluginClasses = GSON.fromJson(pluginClasses, PLUGINS_TYPE);
additionalPluginClasses.forEach(PluginClass::validate);
} catch (JsonParseException e) {
throw new BadRequestException(String.format("%s header '%s' is invalid.", PLUGINS_HEADER, pluginClasses), e);
} catch (IllegalArgumentException e) {
throw new BadRequestException(String.format("Invalid PluginClasses '%s'.", pluginClasses), e);
}
}
try {
// copy the artifact contents to local tmp directory
Files.createDirectories(tmpDir.toPath());
File destination = File.createTempFile("artifact-", ".jar", tmpDir);
return new AbstractBodyConsumer(destination) {
@Override
protected void onFinish(HttpResponder responder, File uploadedFile) {
try {
String version = (artifactVersion == null || artifactVersion.isEmpty()) ? getBundleVersion(uploadedFile) : artifactVersion;
ArtifactId artifactId = validateAndGetArtifactId(namespace, artifactName, version);
// add the artifact to the repo
artifactRepository.addArtifact(Id.Artifact.fromEntityId(artifactId), uploadedFile, parentArtifacts, additionalPluginClasses);
responder.sendString(HttpResponseStatus.OK, "Artifact added successfully");
} catch (ArtifactRangeNotFoundException e) {
responder.sendString(HttpResponseStatus.NOT_FOUND, e.getMessage());
} catch (ArtifactAlreadyExistsException e) {
responder.sendString(HttpResponseStatus.CONFLICT, e.getMessage());
} catch (WriteConflictException e) {
responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Conflict while writing artifact, please try again.");
} catch (IOException e) {
LOG.error("Exception while trying to write artifact {}-{}-{}.", namespaceId, artifactName, artifactVersion, e);
responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Error performing IO while writing artifact.");
} catch (BadRequestException e) {
responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
} catch (UnauthorizedException e) {
responder.sendString(HttpResponseStatus.FORBIDDEN, e.getMessage());
} catch (Exception e) {
LOG.error("Error while writing artifact {}-{}-{}", namespaceId, artifactName, artifactVersion, e);
responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Error while adding artifact.");
}
}
private String getBundleVersion(File file) throws BadRequestException, IOException {
try (JarFile jarFile = new JarFile(file)) {
Manifest manifest = jarFile.getManifest();
if (manifest == null) {
throw new BadRequestException("Unable to derive version from artifact because it does not contain a manifest. " + "Please package the jar with a manifest, or explicitly specify the artifact version.");
}
Attributes attributes = manifest.getMainAttributes();
String version = attributes == null ? null : attributes.getValue(ManifestFields.BUNDLE_VERSION);
if (version == null) {
throw new BadRequestException("Unable to derive version from artifact because manifest does not contain Bundle-Version attribute. " + "Please include Bundle-Version in the manifest, or explicitly specify the artifact version.");
}
return version;
} catch (ZipException e) {
throw new BadRequestException("Artifact is not in zip format. Please make sure it is a jar file.");
}
}
};
} catch (IOException e) {
LOG.error("Exception creating temp file to place artifact {} contents", artifactName, e);
responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, "Server error creating temp file for artifact.");
return null;
}
}
use of io.cdap.cdap.common.BadRequestException in project cdap by cdapio.
the class ImpersonationHandler method getCredentials.
@POST
@Path("/credentials")
public void getCredentials(FullHttpRequest request, HttpResponder responder) throws Exception {
String requestContent = request.content().toString(StandardCharsets.UTF_8);
if (requestContent == null) {
throw new BadRequestException("Request body is empty.");
}
ImpersonationRequest impersonationRequest = GSON.fromJson(requestContent, ImpersonationRequest.class);
LOG.debug("Fetching credentials for {}", impersonationRequest);
UGIWithPrincipal ugiWithPrincipal;
try {
ugiWithPrincipal = ugiProvider.getConfiguredUGI(impersonationRequest);
} catch (AccessException e) {
throw new ServiceException(e, HttpResponseStatus.INTERNAL_SERVER_ERROR);
}
Credentials credentials = ImpersonationUtils.doAs(ugiWithPrincipal.getUGI(), new Callable<Credentials>() {
@Override
public Credentials call() throws Exception {
return tokenSecureStoreRenewer.createCredentials();
}
});
// example: hdfs:///cdap/credentials
Location credentialsDir = locationFactory.create("credentials");
if (credentialsDir.isDirectory() || credentialsDir.mkdirs() || credentialsDir.isDirectory()) {
// the getTempFile() doesn't create the file within the directory that you call it on. It simply appends the path
// without a separator, which is why we manually append the "tmp"
// example: hdfs:///cdap/credentials/tmp.5960fe60-6fd8-4f3e-8e92-3fb6d4726006.credentials
Location credentialsFile = credentialsDir.append("tmp").getTempFile(".credentials");
// 600 is owner-only READ_WRITE
try (DataOutputStream os = new DataOutputStream(new BufferedOutputStream(credentialsFile.getOutputStream("600")))) {
credentials.writeTokenStorageToStream(os);
}
LOG.debug("Wrote credentials for user {} to {}", ugiWithPrincipal.getPrincipal(), credentialsFile);
PrincipalCredentials principalCredentials = new PrincipalCredentials(ugiWithPrincipal.getPrincipal(), credentialsFile.toURI().toString());
responder.sendJson(HttpResponseStatus.OK, GSON.toJson(principalCredentials));
} else {
throw new IllegalStateException("Unable to create credentials directory.");
}
}
use of io.cdap.cdap.common.BadRequestException in project cdap by cdapio.
the class MonitorHandler method setServiceInstance.
/**
* Sets the number of instances of CDAP Services
*/
@Path("/system/services/{service-name}/instances")
@PUT
@AuditPolicy(AuditDetail.REQUEST_BODY)
public void setServiceInstance(FullHttpRequest request, HttpResponder responder, @PathParam("service-name") final String serviceName) throws Exception {
if (!serviceManagementMap.containsKey(serviceName)) {
throw new NotFoundException(String.format("Invalid service name %s", serviceName));
}
SystemServiceId systemServiceId = new SystemServiceId(serviceName);
contextAccessEnforcer.enforce(systemServiceId, StandardPermission.UPDATE);
MasterServiceManager serviceManager = serviceManagementMap.get(serviceName);
int instances = getInstances(request);
if (!serviceManager.isServiceEnabled()) {
throw new ForbiddenException(String.format("Service %s is not enabled", serviceName));
}
int currentInstances = getSystemServiceInstanceCount(serviceName);
if (instances < serviceManager.getMinInstances() || instances > serviceManager.getMaxInstances()) {
String response = String.format("Instance count should be between [%s,%s]", serviceManager.getMinInstances(), serviceManager.getMaxInstances());
throw new BadRequestException(response);
} else if (instances == currentInstances) {
responder.sendStatus(HttpResponseStatus.OK);
return;
}
serviceStore.setServiceInstance(serviceName, instances);
if (serviceManager.setInstances(instances)) {
responder.sendStatus(HttpResponseStatus.OK);
} else {
throw new BadRequestException("Operation did not succeed");
}
}
Aggregations