use of io.cdap.cdap.common.InvalidArtifactException in project cdap by caskdata.
the class AppLifecycleHttpHandler method deployAppFromArtifact.
// normally we wouldn't want to use a body consumer but would just want to read the request body directly
// since it wont be big. But the deploy app API has one path with different behavior based on content type
// the other behavior requires a BodyConsumer and only have one method per path is allowed,
// so we have to use a BodyConsumer
private BodyConsumer deployAppFromArtifact(final ApplicationId appId) throws IOException {
// Perform auth checks outside BodyConsumer as only the first http request containing auth header
// to populate SecurityRequestContext while http chunk doesn't. BodyConsumer runs in the thread
// that processes the last http chunk.
accessEnforcer.enforce(appId, authenticationContext.getPrincipal(), StandardPermission.CREATE);
// createTempFile() needs a prefix of at least 3 characters
return new AbstractBodyConsumer(File.createTempFile("apprequest-" + appId, ".json", tmpDir)) {
@Override
protected void onFinish(HttpResponder responder, File uploadedFile) {
try (FileReader fileReader = new FileReader(uploadedFile)) {
AppRequest<?> appRequest = DECODE_GSON.fromJson(fileReader, AppRequest.class);
ArtifactSummary artifactSummary = appRequest.getArtifact();
KerberosPrincipalId ownerPrincipalId = appRequest.getOwnerPrincipal() == null ? null : new KerberosPrincipalId(appRequest.getOwnerPrincipal());
// if we don't null check, it gets serialized to "null"
Object config = appRequest.getConfig();
String configString = config == null ? null : config instanceof String ? (String) config : GSON.toJson(config);
try {
applicationLifecycleService.deployApp(appId.getParent(), appId.getApplication(), appId.getVersion(), artifactSummary, configString, createProgramTerminator(), ownerPrincipalId, appRequest.canUpdateSchedules(), false, Collections.emptyMap());
} catch (DatasetManagementException e) {
if (e.getCause() instanceof UnauthorizedException) {
throw (UnauthorizedException) e.getCause();
} else {
throw e;
}
}
responder.sendString(HttpResponseStatus.OK, "Deploy Complete");
} catch (ArtifactNotFoundException e) {
responder.sendString(HttpResponseStatus.NOT_FOUND, e.getMessage());
} catch (ConflictException e) {
responder.sendString(HttpResponseStatus.CONFLICT, e.getMessage());
} catch (UnauthorizedException e) {
responder.sendString(HttpResponseStatus.FORBIDDEN, e.getMessage());
} catch (InvalidArtifactException e) {
responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
} catch (IOException e) {
LOG.error("Error reading request body for creating app {}.", appId);
responder.sendString(HttpResponseStatus.INTERNAL_SERVER_ERROR, String.format("Error while reading json request body for app %s.", appId));
} catch (Exception e) {
LOG.error("Deploy failure", e);
responder.sendString(HttpResponseStatus.BAD_REQUEST, e.getMessage());
}
}
};
}
use of io.cdap.cdap.common.InvalidArtifactException in project cdap by caskdata.
the class AppCreator method execute.
@Override
public void execute(Arguments arguments) throws Exception {
ApplicationId appId = arguments.getId();
ArtifactSummary artifactSummary = arguments.getArtifact();
if (appExists(appId) && !arguments.overwrite) {
return;
}
KerberosPrincipalId ownerPrincipalId = arguments.getOwnerPrincipal() == null ? null : new KerberosPrincipalId(arguments.getOwnerPrincipal());
// if we don't null check, it gets serialized to "null"
String configString = arguments.getConfig() == null ? null : GSON.toJson(arguments.getConfig());
try {
appLifecycleService.deployApp(appId.getParent(), appId.getApplication(), appId.getVersion(), artifactSummary, configString, x -> {
}, ownerPrincipalId, arguments.canUpdateSchedules(), false, Collections.emptyMap());
} catch (NotFoundException | UnauthorizedException | InvalidArtifactException e) {
// up to the default time limit
throw e;
} catch (DatasetManagementException e) {
if (e.getCause() instanceof UnauthorizedException) {
throw (UnauthorizedException) e.getCause();
} else {
throw new RetryableException(e);
}
} catch (Exception e) {
throw new RetryableException(e);
}
}
use of io.cdap.cdap.common.InvalidArtifactException in project cdap by caskdata.
the class ApplicationLifecycleService method upgradeApplication.
/**
* Upgrades an existing application by upgrading application artifact versions and plugin artifact versions.
*
* @param appId the id of the application to upgrade.
* @param allowedArtifactScopes artifact scopes allowed while looking for latest artifacts for upgrade.
* @param allowSnapshot whether to consider snapshot version of artifacts or not for upgrade.
* @throws IllegalStateException if something unexpected happened during upgrade.
* @throws IOException if there was an IO error during initializing application class from artifact.
* @throws JsonIOException if there was an error in serializing or deserializing app config.
* @throws UnsupportedOperationException if application does not support upgrade operation.
* @throws InvalidArtifactException if candidate application artifact is invalid for upgrade purpose.
* @throws NotFoundException if any object related to upgrade is not found like application/artifact.
* @throws Exception if there was an exception during the upgrade of application. This exception will often wrap
* the actual exception
*/
public void upgradeApplication(ApplicationId appId, Set<ArtifactScope> allowedArtifactScopes, boolean allowSnapshot) throws Exception {
// Check if the current user has admin privileges on it before updating.
accessEnforcer.enforce(appId, authenticationContext.getPrincipal(), StandardPermission.UPDATE);
// check that app exists
ApplicationSpecification currentSpec = store.getApplication(appId);
if (currentSpec == null) {
LOG.info("Application {} not found for upgrade.", appId);
throw new NotFoundException(appId);
}
ArtifactId currentArtifact = currentSpec.getArtifactId();
ArtifactSummary candidateArtifact = getLatestAppArtifactForUpgrade(appId, currentArtifact, allowedArtifactScopes, allowSnapshot);
ArtifactVersion candidateArtifactVersion = new ArtifactVersion(candidateArtifact.getVersion());
// Current artifact should not have higher version than candidate artifact.
if (currentArtifact.getVersion().compareTo(candidateArtifactVersion) > 0) {
String error = String.format("The current artifact has a version higher %s than any existing artifact.", currentArtifact.getVersion());
throw new InvalidArtifactException(error);
}
ArtifactId newArtifactId = new ArtifactId(candidateArtifact.getName(), candidateArtifactVersion, candidateArtifact.getScope());
Id.Artifact newArtifact = Id.Artifact.fromEntityId(Artifacts.toProtoArtifactId(appId.getParent(), newArtifactId));
ArtifactDetail newArtifactDetail = artifactRepository.getArtifact(newArtifact);
updateApplicationInternal(appId, currentSpec.getConfiguration(), programId -> {
}, newArtifactDetail, Collections.singletonList(ApplicationConfigUpdateAction.UPGRADE_ARTIFACT), allowedArtifactScopes, allowSnapshot, ownerAdmin.getOwner(appId), false);
}
use of io.cdap.cdap.common.InvalidArtifactException in project cdap by caskdata.
the class ApplicationLifecycleService method deployApp.
private ApplicationWithPrograms deployApp(NamespaceId namespaceId, @Nullable String appName, @Nullable String appVersion, @Nullable String configStr, ProgramTerminator programTerminator, ArtifactDetail artifactDetail, @Nullable KerberosPrincipalId ownerPrincipal, boolean updateSchedules, boolean isPreview, Map<String, String> userProps) throws Exception {
// Now to deploy an app, we need ADMIN privilege on the owner principal if it is present, and also ADMIN on the app
// But since at this point, app name is unknown to us, so the enforcement on the app is happening in the deploy
// pipeline - LocalArtifactLoaderStage
// need to enforce on the principal id if impersonation is involved
KerberosPrincipalId effectiveOwner = SecurityUtil.getEffectiveOwner(ownerAdmin, namespaceId, ownerPrincipal == null ? null : ownerPrincipal.getPrincipal());
Principal requestingUser = authenticationContext.getPrincipal();
// impersonated principal
if (effectiveOwner != null) {
accessEnforcer.enforce(effectiveOwner, requestingUser, AccessPermission.SET_OWNER);
}
ApplicationClass appClass = Iterables.getFirst(artifactDetail.getMeta().getClasses().getApps(), null);
if (appClass == null) {
throw new InvalidArtifactException(String.format("No application class found in artifact '%s' in namespace '%s'.", artifactDetail.getDescriptor().getArtifactId(), namespaceId));
}
if (!NamespaceId.SYSTEM.equals(namespaceId)) {
capabilityReader.checkAllEnabled(appClass.getRequirements().getCapabilities());
}
// deploy application with newly added artifact
AppDeploymentInfo deploymentInfo = new AppDeploymentInfo(Artifacts.toProtoArtifactId(namespaceId, artifactDetail.getDescriptor().getArtifactId()), artifactDetail.getDescriptor().getLocation(), namespaceId, appClass, appName, appVersion, configStr, ownerPrincipal, updateSchedules, isPreview ? new AppDeploymentRuntimeInfo(null, userProps, Collections.emptyMap()) : null);
Manager<AppDeploymentInfo, ApplicationWithPrograms> manager = managerFactory.create(programTerminator);
// TODO: (CDAP-3258) Manager needs MUCH better error handling.
ApplicationWithPrograms applicationWithPrograms;
try {
applicationWithPrograms = manager.deploy(deploymentInfo).get();
} catch (ExecutionException e) {
Throwables.propagateIfPossible(e.getCause(), Exception.class);
throw Throwables.propagate(e.getCause());
}
adminEventPublisher.publishAppCreation(applicationWithPrograms.getApplicationId(), applicationWithPrograms.getSpecification());
return applicationWithPrograms;
}
use of io.cdap.cdap.common.InvalidArtifactException in project cdap by caskdata.
the class ApplicationLifecycleService method deployAppAndArtifact.
/**
* Deploy an application by first adding the application jar to the artifact repository, then creating an application
* using that newly added artifact.
*
* @param namespace the namespace to deploy the application and artifact in
* @param appName the name of the app. If null, the name will be set based on the application spec
* @param artifactId the id of the artifact to add and create the application from
* @param jarFile the application jar to add as an artifact and create the application from
* @param configStr the configuration to send to the application when generating the application specification
* @param programTerminator a program terminator that will stop programs that are removed when updating an app.
* For example, if an update removes a flow, the terminator defines how to stop that flow.
* @return information about the deployed application
* @throws InvalidArtifactException the the artifact is invalid. For example, if it does not contain any app classes
* @throws ArtifactAlreadyExistsException if the specified artifact already exists
* @throws IOException if there was an IO error writing the artifact
*/
public ApplicationWithPrograms deployAppAndArtifact(NamespaceId namespace, @Nullable String appName, Id.Artifact artifactId, File jarFile, @Nullable String configStr, @Nullable KerberosPrincipalId ownerPrincipal, ProgramTerminator programTerminator, boolean updateSchedules) throws Exception {
ArtifactDetail artifactDetail = artifactRepository.addArtifact(artifactId, jarFile);
try {
return deployApp(namespace, appName, null, configStr, programTerminator, artifactDetail, ownerPrincipal, updateSchedules, false, Collections.emptyMap());
} catch (Exception e) {
// to the state we were in before this call.
try {
artifactRepository.deleteArtifact(artifactId);
} catch (Exception e2) {
// if the delete fails, nothing we can do, just log it and continue on
LOG.warn("Failed to delete artifact {} after deployment of artifact and application failed.", artifactId, e2);
e.addSuppressed(e2);
}
throw e;
}
}
Aggregations