Skip to content

Commit

Permalink
Merge branch 'cdapio:develop' into e2e-hub-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
priyabhatnagar25 authored Oct 27, 2023
2 parents a557ed9 + a784b2d commit 8609788
Show file tree
Hide file tree
Showing 303 changed files with 4,927 additions and 1,865 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright © 2015-2017 Cask Data, Inc.
* Copyright © 2023 Cask Data, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
Expand All @@ -14,11 +14,11 @@
* the License.
*/

package io.cdap.cdap.common;
package io.cdap.cdap.api.service;

import io.cdap.cdap.api.common.HttpErrorStatusProvider;
import io.cdap.cdap.api.retry.RetryableException;
import io.netty.handler.codec.http.HttpResponseStatus;
import java.net.HttpURLConnection;

/**
* Exception thrown when the service is not running.
Expand Down Expand Up @@ -55,6 +55,6 @@ public String getServiceName() {

@Override
public int getStatusCode() {
return HttpResponseStatus.SERVICE_UNAVAILABLE.code();
return HttpURLConnection.HTTP_UNAVAILABLE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
import io.cdap.cdap.master.environment.MasterEnvironments;
import io.cdap.cdap.master.spi.environment.MasterEnvironment;
import io.cdap.cdap.messaging.client.ClientMessagingService;
import io.cdap.cdap.messaging.guice.MessagingClientModule;
import io.cdap.cdap.messaging.guice.MessagingServiceModule;
import io.cdap.cdap.metadata.MetadataReaderWriterModules;
import io.cdap.cdap.metadata.PreferencesFetcher;
import io.cdap.cdap.metadata.RemotePreferencesFetcherInternal;
Expand Down Expand Up @@ -163,7 +163,7 @@ private List<Module> getCoreModules() {
modules.add(new IOModule());
modules.add(new DFSLocationModule());
modules.add(new MetricsClientRuntimeModule().getDistributedModules());
modules.add(new MessagingClientModule());
modules.add(new MessagingServiceModule(cConf));
modules.add(new AuditModule());
modules.add(new AuthorizationEnforcementModule().getDistributedModules());
modules.add(new SecureStoreClientModule());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import com.google.common.base.Throwables;
import com.google.common.util.concurrent.Futures;
import io.cdap.cdap.common.ServiceUnavailableException;
import io.cdap.cdap.api.service.ServiceUnavailableException;
import io.cdap.cdap.proto.id.ProgramId;
import io.cdap.cdap.security.impersonation.Impersonator;
import java.util.Map;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
import io.cdap.cdap.internal.capability.CapabilityReader;
import io.cdap.cdap.internal.capability.CapabilityStatusStore;
import io.cdap.cdap.internal.pipeline.SynchronousPipelineFactory;
import io.cdap.cdap.messaging.MessagingService;
import io.cdap.cdap.messaging.spi.MessagingService;
import io.cdap.cdap.metadata.DefaultMetadataAdmin;
import io.cdap.cdap.metadata.MetadataAdmin;
import io.cdap.cdap.metadata.PreferencesFetcher;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package io.cdap.cdap.app.services;

import io.cdap.cdap.api.ServiceDiscoverer;
import io.cdap.cdap.common.ServiceUnavailableException;
import io.cdap.cdap.api.service.ServiceUnavailableException;
import io.cdap.cdap.common.conf.Constants;
import io.cdap.cdap.common.internal.remote.RemoteClient;
import io.cdap.cdap.common.internal.remote.RemoteClientFactory;
Expand Down
20 changes: 20 additions & 0 deletions cdap-app-fabric/src/main/java/io/cdap/cdap/app/store/Store.java
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,16 @@ List<ProgramHistory> getRuns(Collection<ProgramReference> programs, ProgramRunSt
*/
int addApplication(ApplicationId id, ApplicationMeta meta) throws ConflictException;

/**
* Marks existing applications as latest.
*
* @param applicationIds List of application ids
* @throws IOException if the apps cannot be marked latest because of any IO failure
* @throws ApplicationNotFoundException when any of the applications is not found
*/
void markApplicationsLatest(Collection<ApplicationId> applicationIds)
throws IOException, ApplicationNotFoundException;

/**
* Return a list of program specifications that are deleted comparing the specification in the store with the
* spec that is passed.
Expand All @@ -379,6 +389,16 @@ List<ProgramHistory> getRuns(Collection<ProgramReference> programs, ProgramRunSt
List<ProgramSpecification> getDeletedProgramSpecifications(ApplicationReference appRef,
ApplicationSpecification specification);

/**
* Updates source control metadata for one or more applications.
* If any of the applications in the given collection are not found, this method ignores them.
*
* @param updateRequests Map of {@link ApplicationId} to {@link SourceControlMeta}
* @throws IOException if scm meta update fails
*/
void updateApplicationSourceControlMeta(Map<ApplicationId, SourceControlMeta> updateRequests)
throws IOException;

/**
* Returns application specification by id.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
import io.cdap.cdap.internal.app.runtime.distributed.DistributedProgramRunner;
import io.cdap.cdap.logging.appender.LogAppenderInitializer;
import io.cdap.cdap.logging.context.LoggingContextHelper;
import io.cdap.cdap.messaging.MessagingService;
import io.cdap.cdap.messaging.spi.MessagingService;
import io.cdap.cdap.proto.id.ProgramRunId;
import java.io.File;
import java.io.IOException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import io.cdap.cdap.internal.app.runtime.SystemArguments;
import io.cdap.cdap.internal.app.store.profile.ProfileStore;
import io.cdap.cdap.internal.profile.AdminEventPublisher;
import io.cdap.cdap.messaging.MessagingService;
import io.cdap.cdap.messaging.spi.MessagingService;
import io.cdap.cdap.messaging.context.MultiThreadMessagingContext;
import io.cdap.cdap.proto.EntityScope;
import io.cdap.cdap.proto.PreferencesDetail;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* Copyright © 2023 Cask Data, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package io.cdap.cdap.gateway.handlers;

import com.google.common.base.Throwables;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.cdap.cdap.api.artifact.ArtifactSummary;
import io.cdap.cdap.api.dataset.DatasetManagementException;
import io.cdap.cdap.api.security.AccessException;
import io.cdap.cdap.app.runtime.ProgramController;
import io.cdap.cdap.app.runtime.ProgramRuntimeService;
import io.cdap.cdap.common.ArtifactNotFoundException;
import io.cdap.cdap.common.BadRequestException;
import io.cdap.cdap.common.ConflictException;
import io.cdap.cdap.common.InvalidArtifactException;
import io.cdap.cdap.common.NamespaceNotFoundException;
import io.cdap.cdap.common.conf.CConfiguration;
import io.cdap.cdap.common.conf.Constants;
import io.cdap.cdap.common.http.AbstractBodyConsumer;
import io.cdap.cdap.common.io.CaseInsensitiveEnumTypeAdapterFactory;
import io.cdap.cdap.common.namespace.NamespaceQueryAdmin;
import io.cdap.cdap.gateway.handlers.util.AbstractAppFabricHttpHandler;
import io.cdap.cdap.internal.app.deploy.ProgramTerminator;
import io.cdap.cdap.internal.app.deploy.pipeline.ApplicationWithPrograms;
import io.cdap.cdap.internal.app.services.ApplicationLifecycleService;
import io.cdap.cdap.proto.ApplicationRecord;
import io.cdap.cdap.proto.artifact.AppRequest;
import io.cdap.cdap.proto.id.ApplicationId;
import io.cdap.cdap.proto.id.EntityId;
import io.cdap.cdap.proto.id.KerberosPrincipalId;
import io.cdap.cdap.proto.id.NamespaceId;
import io.cdap.cdap.proto.id.ProgramId;
import io.cdap.cdap.security.spi.authorization.UnauthorizedException;
import io.cdap.http.BodyConsumer;
import io.cdap.http.HttpResponder;
import io.netty.handler.codec.http.HttpResponseStatus;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Optional;
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The abstract base class for the {@link AppLifecycleHttpHandler} and {@link AppLifecycleHttpHandlerInternal}
* It contains the common methods used in both handlers mentioned above.
*/
public abstract class AbstractAppLifecycleHttpHandler extends AbstractAppFabricHttpHandler {
protected static final Gson GSON = new Gson();
protected static final Gson DECODE_GSON = new GsonBuilder()
.registerTypeAdapterFactory(new CaseInsensitiveEnumTypeAdapterFactory())
.create();
protected static final Logger LOG = LoggerFactory.getLogger(AbstractAppLifecycleHttpHandler.class);

protected final CConfiguration configuration;
protected final NamespaceQueryAdmin namespaceQueryAdmin;
protected final ProgramRuntimeService runtimeService;
protected final ApplicationLifecycleService applicationLifecycleService;
protected final File tmpDir;

/**
* Constructor for the abstract class.
*
* @param configuration CConfiguration, passed from the derived class where it's injected
* @param namespaceQueryAdmin passed from the derived class where it's injected
* @param runtimeService passed from the derived class where it's injected
*/
public AbstractAppLifecycleHttpHandler(CConfiguration configuration,
NamespaceQueryAdmin namespaceQueryAdmin,
ProgramRuntimeService runtimeService,
ApplicationLifecycleService applicationLifecycleService) {
this.configuration = configuration;
this.namespaceQueryAdmin = namespaceQueryAdmin;
this.runtimeService = runtimeService;
this.applicationLifecycleService = applicationLifecycleService;
this.tmpDir = new File(new File(configuration.get(Constants.CFG_LOCAL_DATA_DIR)),
configuration.get(Constants.AppFabric.TEMP_DIR)).getAbsoluteFile();
}

protected ApplicationId validateApplicationVersionId(NamespaceId namespaceId, String appId,
String versionId) throws BadRequestException {
if (appId == null) {
throw new BadRequestException("Path parameter app-id cannot be empty");
}
if (!EntityId.isValidId(appId)) {
throw new BadRequestException(String.format("Invalid app name '%s'", appId));
}
if (versionId == null) {
throw new BadRequestException("Path parameter version-id cannot be empty");
}
if (EntityId.isValidVersionId(versionId)) {
return namespaceId.app(appId, versionId);
}
throw new BadRequestException(String.format("Invalid version '%s'", versionId));
}

protected NamespaceId validateNamespace(@Nullable String namespace)
throws BadRequestException, NamespaceNotFoundException, AccessException {

if (namespace == null) {
throw new BadRequestException("Path parameter namespace-id cannot be empty");
}

NamespaceId namespaceId;
try {
namespaceId = new NamespaceId(namespace);
} catch (IllegalArgumentException e) {
throw new BadRequestException(String.format("Invalid namespace '%s'", namespace), e);
}

try {
if (!namespaceId.equals(NamespaceId.SYSTEM)) {
namespaceQueryAdmin.get(namespaceId);
}
} catch (NamespaceNotFoundException | AccessException e) {
throw e;
} catch (Exception e) {
// This can only happen when NamespaceAdmin uses HTTP calls to interact with namespaces.
// In AppFabricServer, NamespaceAdmin is bound to DefaultNamespaceAdmin, which interacts directly with the MDS.
// Hence, this exception will never be thrown
throw Throwables.propagate(e);
}
return namespaceId;
}

protected ProgramTerminator createProgramTerminator() {
return programId -> {
switch (programId.getType()) {
case SERVICE:
case WORKER:
killProgramIfRunning(programId);
break;
default:
break;
}
};
}

protected void killProgramIfRunning(ProgramId programId) {
ProgramRuntimeService.RuntimeInfo programRunInfo = findRuntimeInfo(programId, runtimeService);
if (programRunInfo != null) {
ProgramController controller = programRunInfo.getController();
controller.kill();
}
}

protected ApplicationRecord getApplicationRecord(ApplicationWithPrograms deployedApp) {
return new ApplicationRecord(
ArtifactSummary.from(deployedApp.getArtifactId().toApiArtifactId()),
deployedApp.getApplicationId().getApplication(),
deployedApp.getApplicationId().getVersion(),
deployedApp.getSpecification().getDescription(),
Optional.ofNullable(deployedApp.getOwnerPrincipal()).map(KerberosPrincipalId::getPrincipal)
.orElse(null),
deployedApp.getChangeDetail(), null);
}

protected BodyConsumer deployAppFromArtifact(
final ApplicationId appId,
final boolean skipMarkingLatest)
throws IOException {
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);

try {
ApplicationWithPrograms app = applicationLifecycleService.deployApp(appId, appRequest,
null, createProgramTerminator(), skipMarkingLatest);
responder.sendJson(HttpResponseStatus.OK, GSON.toJson(getApplicationRecord(app)));
} catch (DatasetManagementException e) {
if (e.getCause() instanceof UnauthorizedException) {
throw (UnauthorizedException) e.getCause();
} else {
throw e;
}
}
} 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, e);
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());
}
}
};
}
}
Loading

0 comments on commit 8609788

Please sign in to comment.