diff --git a/cdap-app-fabric/src/main/java/io/cdap/cdap/internal/app/deploy/RemoteConfigurator.java b/cdap-app-fabric/src/main/java/io/cdap/cdap/internal/app/deploy/RemoteConfigurator.java index bbcac0f62922..d740ef757f98 100644 --- a/cdap-app-fabric/src/main/java/io/cdap/cdap/internal/app/deploy/RemoteConfigurator.java +++ b/cdap-app-fabric/src/main/java/io/cdap/cdap/internal/app/deploy/RemoteConfigurator.java @@ -74,6 +74,7 @@ public ListenableFuture config() { try { RunnableTaskRequest request = RunnableTaskRequest.getBuilder(ConfiguratorTask.class.getName()) .withParam(GSON.toJson(deploymentInfo)) + .withNamespace(deploymentInfo.getNamespaceId().getNamespace()) .build(); byte[] result = remoteTaskExecutor.runTask(request); diff --git a/cdap-app-fabric/src/main/java/io/cdap/cdap/internal/app/worker/sidecar/GcpMetadataHttpHandlerInternal.java b/cdap-app-fabric/src/main/java/io/cdap/cdap/internal/app/worker/sidecar/GcpMetadataHttpHandlerInternal.java index 002019feca5a..cb860ffeb750 100644 --- a/cdap-app-fabric/src/main/java/io/cdap/cdap/internal/app/worker/sidecar/GcpMetadataHttpHandlerInternal.java +++ b/cdap-app-fabric/src/main/java/io/cdap/cdap/internal/app/worker/sidecar/GcpMetadataHttpHandlerInternal.java @@ -16,11 +16,13 @@ package io.cdap.cdap.internal.app.worker.sidecar; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonSyntaxException; import com.google.inject.Singleton; -import io.cdap.cdap.api.common.HttpErrorStatusProvider; import io.cdap.cdap.common.BadRequestException; import io.cdap.cdap.common.ForbiddenException; import io.cdap.cdap.common.conf.CConfiguration; @@ -32,7 +34,6 @@ import io.cdap.cdap.internal.namespace.credential.RemoteNamespaceCredentialProvider; import io.cdap.cdap.proto.BasicThrowable; import io.cdap.cdap.proto.codec.BasicThrowableCodec; -import io.cdap.cdap.proto.credential.CredentialProvisioningException; import io.cdap.cdap.proto.credential.NamespaceCredentialProvider; import io.cdap.cdap.proto.credential.NotFoundException; import io.cdap.cdap.proto.credential.ProvisionedCredential; @@ -49,6 +50,8 @@ import java.net.URL; import java.time.Duration; import java.time.Instant; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.PUT; @@ -74,6 +77,8 @@ public class GcpMetadataHttpHandlerInternal extends AbstractAppFabricHttpHandler private final NamespaceCredentialProvider credentialProvider; private final GcpWorkloadIdentityInternalAuthenticator gcpWorkloadIdentityInternalAuthenticator; private GcpMetadataTaskContext gcpMetadataTaskContext; + private final LoadingCache credentialLoadingCache; /** * Constructs the {@link GcpMetadataHttpHandlerInternal}. @@ -89,6 +94,18 @@ public GcpMetadataHttpHandlerInternal(CConfiguration cConf, new GcpWorkloadIdentityInternalAuthenticator(gcpMetadataTaskContext); this.credentialProvider = new RemoteNamespaceCredentialProvider(remoteClientFactory, this.gcpWorkloadIdentityInternalAuthenticator); + this.credentialLoadingCache = CacheBuilder.newBuilder() + // Provisioned credential expire after 60mins, assuming 20% buffer in cache exp (0.8*60). + .expireAfterWrite(48, TimeUnit.MINUTES) + .build(new CacheLoader() { + @Override + public ProvisionedCredential load(ProvisionedCredentialCacheKey + provisionedCredentialCacheKey) throws Exception { + return fetchTokenFromCredentialProvider( + provisionedCredentialCacheKey.getGcpMetadataTaskContext(), + provisionedCredentialCacheKey.getScopes()); + } + }); } /** @@ -139,6 +156,7 @@ public void token(HttpRequest request, HttpResponder responder, // needed when initializing // io.cdap.cdap.common.guice.DFSLocationModule$LocationFactoryProvider#get // in io.cdap.cdap.internal.app.worker.TaskWorkerTwillRunnable. + LOG.warn("The GCP Metadata Task Context has been identified as null."); GcpTokenResponse gcpTokenResponse = new GcpTokenResponse("Bearer", "invalidToken", 3599); responder.sendJson(HttpResponseStatus.OK, GSON.toJson(gcpTokenResponse)); return; @@ -146,24 +164,21 @@ public void token(HttpRequest request, HttpResponder responder, try { // fetch token from credential provider - GcpTokenResponse gcpTokenResponse = - Retries.callWithRetries(() -> fetchTokenFromCredentialProvider(scopes), - RetryStrategies.fromConfiguration(cConf, Constants.Service.TASK_WORKER + ".")); + ProvisionedCredential provisionedCredential = + credentialLoadingCache.get( + new ProvisionedCredentialCacheKey(this.gcpMetadataTaskContext, scopes)); + GcpTokenResponse gcpTokenResponse = new GcpTokenResponse("Bearer", + provisionedCredential.get(), + Duration.between(Instant.now(), provisionedCredential.getExpiration()).getSeconds()); responder.sendJson(HttpResponseStatus.OK, GSON.toJson(gcpTokenResponse)); return; - } catch (NotFoundException e) { + } catch (ExecutionException e) { + if (!(e.getCause() instanceof NotFoundException)) { + LOG.error("Failed to fetch token from credential provider", e.getCause()); + throw e; + } // if credential identity not found, // fallback to gcp metadata server for backward compatibility. - } catch (Exception ex) { - if (ex instanceof HttpErrorStatusProvider) { - HttpResponseStatus status = HttpResponseStatus.valueOf( - ((HttpErrorStatusProvider) ex).getStatusCode()); - responder.sendJson(status, exceptionToJson(ex)); - } else { - LOG.warn("Failed to fetch token from credential provider", ex); - responder.sendJson(HttpResponseStatus.INTERNAL_SERVER_ERROR, exceptionToJson(ex)); - } - return; } if (metadataServiceTokenEndpoint == null) { @@ -177,17 +192,16 @@ public void token(HttpRequest request, HttpResponder responder, responder.sendJson(HttpResponseStatus.OK, fetchTokenFromMetadataServer(scopes).getResponseBodyAsString()); } catch (Exception ex) { - LOG.warn("Failed to fetch token from metadata service", ex); + LOG.error("Failed to fetch token from metadata server", ex); responder.sendJson(HttpResponseStatus.INTERNAL_SERVER_ERROR, exceptionToJson(ex)); } } - private GcpTokenResponse fetchTokenFromCredentialProvider(String scopes) throws NotFoundException, - IOException, CredentialProvisioningException { - ProvisionedCredential provisionedCredential = - this.credentialProvider.provision(gcpMetadataTaskContext.getNamespace(), scopes); - return new GcpTokenResponse("Bearer", provisionedCredential.get(), - Duration.between(Instant.now(), provisionedCredential.getExpiration()).getSeconds()); + private ProvisionedCredential fetchTokenFromCredentialProvider( + GcpMetadataTaskContext gcpMetadataTaskContext, String scopes) throws Exception { + return Retries.callWithRetries(() -> + this.credentialProvider.provision(gcpMetadataTaskContext.getNamespace(), scopes), + RetryStrategies.fromConfiguration(cConf, Constants.Service.TASK_WORKER + ".")); } private HttpResponse fetchTokenFromMetadataServer(String scopes) throws IOException { @@ -229,6 +243,7 @@ public void setContext(FullHttpRequest request, HttpResponder responder) public void clearContext(HttpRequest request, HttpResponder responder) { this.gcpMetadataTaskContext = null; this.gcpWorkloadIdentityInternalAuthenticator.setGcpMetadataTaskContext(gcpMetadataTaskContext); + this.credentialLoadingCache.invalidateAll(); LOG.trace("Context cleared."); responder.sendStatus(HttpResponseStatus.OK); } diff --git a/cdap-app-fabric/src/main/java/io/cdap/cdap/internal/app/worker/sidecar/ProvisionedCredentialCacheKey.java b/cdap-app-fabric/src/main/java/io/cdap/cdap/internal/app/worker/sidecar/ProvisionedCredentialCacheKey.java new file mode 100644 index 000000000000..c8bf9d33118a --- /dev/null +++ b/cdap-app-fabric/src/main/java/io/cdap/cdap/internal/app/worker/sidecar/ProvisionedCredentialCacheKey.java @@ -0,0 +1,72 @@ +/* + * 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.internal.app.worker.sidecar; + +import io.cdap.cdap.proto.security.GcpMetadataTaskContext; +import java.util.Objects; + +/** + * Defines the contents of key used for + * caching {@link io.cdap.cdap.proto.credential.ProvisionedCredential}. + */ +public final class ProvisionedCredentialCacheKey { + private final GcpMetadataTaskContext gcpMetadataTaskContext; + private final String scopes; + private transient Integer hashCode; + + public ProvisionedCredentialCacheKey(GcpMetadataTaskContext gcpMetadataTaskContext, + String scopes) { + this.gcpMetadataTaskContext = gcpMetadataTaskContext; + this.scopes = scopes; + } + + public GcpMetadataTaskContext getGcpMetadataTaskContext() { + return gcpMetadataTaskContext; + } + + public String getScopes() { + return scopes; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof ProvisionedCredentialCacheKey)) { + return false; + } + ProvisionedCredentialCacheKey that = (ProvisionedCredentialCacheKey) o; + return Objects.equals(gcpMetadataTaskContext.getNamespace(), + that.gcpMetadataTaskContext.getNamespace()) + && Objects.equals(gcpMetadataTaskContext.getUserCredential().toString(), + that.gcpMetadataTaskContext.getUserCredential().toString()) + && Objects.equals(gcpMetadataTaskContext.getUserId(), + that.gcpMetadataTaskContext.getUserId()) + && Objects.equals(gcpMetadataTaskContext.getUserIp(), + that.gcpMetadataTaskContext.getUserIp()) + && Objects.equals(scopes, that.scopes); + } + + @Override + public int hashCode() { + Integer hashCode = this.hashCode; + if (hashCode == null) { + this.hashCode = hashCode = Objects.hash(gcpMetadataTaskContext.getNamespace(), + gcpMetadataTaskContext.getUserCredential().toString(), + gcpMetadataTaskContext.getUserId(), gcpMetadataTaskContext.getUserIp(), scopes); + } + return hashCode; + } +} diff --git a/cdap-app-fabric/src/test/java/io/cdap/cdap/internal/app/worker/TaskWorkerMetricsTest.java b/cdap-app-fabric/src/test/java/io/cdap/cdap/internal/app/worker/TaskWorkerMetricsTest.java index 073650a2cff6..53f40545da4b 100644 --- a/cdap-app-fabric/src/test/java/io/cdap/cdap/internal/app/worker/TaskWorkerMetricsTest.java +++ b/cdap-app-fabric/src/test/java/io/cdap/cdap/internal/app/worker/TaskWorkerMetricsTest.java @@ -105,8 +105,7 @@ public void afterTest() { public void testSimpleRequest() throws IOException { String taskClassName = TaskWorkerServiceTest.TestRunnableClass.class.getName(); RunnableTaskRequest req = RunnableTaskRequest.getBuilder(taskClassName) - .withParam("100") - .build(); + .withParam("100").withNamespace("testNamespace").build(); String reqBody = GSON.toJson(req); HttpResponse response = HttpRequests.execute( HttpRequest.post(uri.resolve("/v3Internal/worker/run").toURL()) diff --git a/cdap-app-fabric/src/test/java/io/cdap/cdap/internal/app/worker/TaskWorkerServiceTest.java b/cdap-app-fabric/src/test/java/io/cdap/cdap/internal/app/worker/TaskWorkerServiceTest.java index 60de9d2270a8..3f77f29f3d97 100644 --- a/cdap-app-fabric/src/test/java/io/cdap/cdap/internal/app/worker/TaskWorkerServiceTest.java +++ b/cdap-app-fabric/src/test/java/io/cdap/cdap/internal/app/worker/TaskWorkerServiceTest.java @@ -145,8 +145,8 @@ public void testPeriodicRestartWithInflightRequest() throws IOException { // Post valid request String want = "5000"; - RunnableTaskRequest req = RunnableTaskRequest.getBuilder(TestRunnableClass.class.getName()).withParam(want) - .build(); + RunnableTaskRequest req = RunnableTaskRequest.getBuilder(TestRunnableClass.class.getName()) + .withParam(want).withNamespace("testNamespace").build(); String reqBody = GSON.toJson(req); HttpResponse response = HttpRequests.execute( HttpRequest.post(uri.resolve("/v3Internal/worker/run").toURL()) @@ -224,8 +224,8 @@ public void testRestartAfterMultipleExecutions() throws IOException { // Post valid request String want = "100"; - RunnableTaskRequest req = RunnableTaskRequest.getBuilder(TestRunnableClass.class.getName()).withParam(want) - .build(); + RunnableTaskRequest req = RunnableTaskRequest.getBuilder(TestRunnableClass.class.getName()) + .withParam(want).withNamespace("testNamespace").build(); String reqBody = GSON.toJson(req); HttpResponse response = HttpRequests.execute( HttpRequest.post(uri.resolve("/v3Internal/worker/run").toURL()) @@ -248,8 +248,8 @@ public void testStartAndStopWithValidRequest() throws IOException { // Post valid request String want = "100"; - RunnableTaskRequest req = RunnableTaskRequest.getBuilder(TestRunnableClass.class.getName()).withParam(want) - .build(); + RunnableTaskRequest req = RunnableTaskRequest.getBuilder(TestRunnableClass.class.getName()) + .withParam(want).withNamespace("testNamespace").build(); String reqBody = GSON.toJson(req); HttpResponse response = HttpRequests.execute( HttpRequest.post(uri.resolve("/v3Internal/worker/run").toURL()) @@ -267,7 +267,8 @@ public void testStartAndStopWithInvalidRequest() throws Exception { URI uri = URI.create(String.format("http://%s:%s", addr.getHostName(), addr.getPort())); // Post invalid request - RunnableTaskRequest noClassReq = RunnableTaskRequest.getBuilder("NoClass").build(); + RunnableTaskRequest noClassReq = RunnableTaskRequest.getBuilder("NoClass") + .withNamespace("testNamespace").withParam("100").build(); String reqBody = GSON.toJson(noClassReq); HttpResponse response = HttpRequests.execute( HttpRequest.post(uri.resolve("/v3Internal/worker/run").toURL()) @@ -287,8 +288,8 @@ public void testConcurrentRequests() throws Exception { InetSocketAddress addr = taskWorkerService.getBindAddress(); URI uri = URI.create(String.format("http://%s:%s", addr.getHostName(), addr.getPort())); - RunnableTaskRequest request = RunnableTaskRequest.getBuilder(TestRunnableClass.class.getName()). - withParam("1000").build(); + RunnableTaskRequest request = RunnableTaskRequest.getBuilder(TestRunnableClass.class.getName()) + .withParam("1000").withNamespace("testNamespace").build(); String reqBody = GSON.toJson(request); List> calls = new ArrayList<>(); diff --git a/cdap-common/src/main/java/io/cdap/cdap/common/internal/remote/TaskWorkerHttpHandlerInternal.java b/cdap-common/src/main/java/io/cdap/cdap/common/internal/remote/TaskWorkerHttpHandlerInternal.java index bb2c1a604fc6..a04039d22864 100644 --- a/cdap-common/src/main/java/io/cdap/cdap/common/internal/remote/TaskWorkerHttpHandlerInternal.java +++ b/cdap-common/src/main/java/io/cdap/cdap/common/internal/remote/TaskWorkerHttpHandlerInternal.java @@ -207,13 +207,16 @@ public void run(FullHttpRequest request, HttpResponder responder) { RunnableTaskContext runnableTaskContext = new RunnableTaskContext( runnableTaskRequest); try { - if (runnableTaskRequest.getParam().getEmbeddedTaskRequest() != null - && runnableTaskRequest.getParam().getEmbeddedTaskRequest().getNamespace() != null) { - // set the GcpMetadataTaskContext before running the task. - NamespaceId namespaceId = new NamespaceId( + NamespaceId namespaceId; + if (runnableTaskRequest.getParam().getEmbeddedTaskRequest() != null) { + // For system app tasks + namespaceId = new NamespaceId( runnableTaskRequest.getParam().getEmbeddedTaskRequest().getNamespace()); - GcpMetadataTaskContextUtil.setGcpMetadataTaskContext(namespaceId, cConf); + } else { + namespaceId = new NamespaceId(runnableTaskRequest.getNamespace()); } + // set the GcpMetadataTaskContext before running the task. + GcpMetadataTaskContextUtil.setGcpMetadataTaskContext(namespaceId, cConf); runnableTaskLauncher.launchRunnableTask(runnableTaskContext); TaskDetails taskDetails = new TaskDetails(metricsCollectionService, startTime, diff --git a/cdap-common/src/test/java/io/cdap/cdap/common/internal/remote/RemoteTaskExecutorTest.java b/cdap-common/src/test/java/io/cdap/cdap/common/internal/remote/RemoteTaskExecutorTest.java index 77eac87fce1e..28325746931a 100644 --- a/cdap-common/src/test/java/io/cdap/cdap/common/internal/remote/RemoteTaskExecutorTest.java +++ b/cdap-common/src/test/java/io/cdap/cdap/common/internal/remote/RemoteTaskExecutorTest.java @@ -179,7 +179,7 @@ public void testFailedMetrics() throws Exception { RemoteTaskExecutor remoteTaskExecutor = new RemoteTaskExecutor(cConf, mockMetricsCollector, remoteClientFactory, RemoteTaskExecutor.Type.TASK_WORKER); RunnableTaskRequest runnableTaskRequest = RunnableTaskRequest.getBuilder(InValidRunnableClass.class.getName()). - withParam("param").build(); + withParam("param").withNamespace("testNamespace").build(); try { remoteTaskExecutor.runTask(runnableTaskRequest); } catch (RemoteExecutionException e) { @@ -203,7 +203,7 @@ public void testSuccessMetrics() throws Exception { RemoteTaskExecutor remoteTaskExecutor = new RemoteTaskExecutor(cConf, mockMetricsCollector, remoteClientFactory, RemoteTaskExecutor.Type.TASK_WORKER); RunnableTaskRequest runnableTaskRequest = RunnableTaskRequest.getBuilder(ValidRunnableClass.class.getName()). - withParam("param").build(); + withParam("param").withNamespace("testNamespace").build(); remoteTaskExecutor.runTask(runnableTaskRequest); mockMetricsCollector.stopAndWait(); Assert.assertSame(1, metricCollectors.size()); @@ -224,7 +224,7 @@ public void testRetryMetrics() throws Exception { RemoteTaskExecutor remoteTaskExecutor = new RemoteTaskExecutor(cConf, mockMetricsCollector, remoteClientFactory, RemoteTaskExecutor.Type.TASK_WORKER); RunnableTaskRequest runnableTaskRequest = RunnableTaskRequest.getBuilder(ValidRunnableClass.class.getName()). - withParam("param").build(); + withParam("param").withNamespace("testNamespace").build(); try { remoteTaskExecutor.runTask(runnableTaskRequest); } catch (Exception e) { diff --git a/cdap-credential-ext-gcp-wi/src/main/java/io/cdap/cdap/security/spi/credential/GcpWorkloadIdentityCredentialProvider.java b/cdap-credential-ext-gcp-wi/src/main/java/io/cdap/cdap/security/spi/credential/GcpWorkloadIdentityCredentialProvider.java index 8b22ccb10b2b..aec8224f8db9 100644 --- a/cdap-credential-ext-gcp-wi/src/main/java/io/cdap/cdap/security/spi/credential/GcpWorkloadIdentityCredentialProvider.java +++ b/cdap-credential-ext-gcp-wi/src/main/java/io/cdap/cdap/security/spi/credential/GcpWorkloadIdentityCredentialProvider.java @@ -283,6 +283,7 @@ private String exchangeTokenViaSts(String token, String scopes, String audience) throws IOException { // replace comma with space, see: + // https://cloud.google.com/functions/docs/securing/function-identity#access_tokens // https://cloud.google.com/iam/docs/reference/sts/rest/v1/TopLevel/token#request-body scopes = Arrays.stream(scopes.split(",")).map(String::trim) .filter(s -> !s.isEmpty()).distinct().collect(Collectors.joining(" ")); diff --git a/cdap-kubernetes/src/main/java/io/cdap/cdap/master/environment/k8s/KubeMasterEnvironment.java b/cdap-kubernetes/src/main/java/io/cdap/cdap/master/environment/k8s/KubeMasterEnvironment.java index 2139f3924e91..21195f91328f 100644 --- a/cdap-kubernetes/src/main/java/io/cdap/cdap/master/environment/k8s/KubeMasterEnvironment.java +++ b/cdap-kubernetes/src/main/java/io/cdap/cdap/master/environment/k8s/KubeMasterEnvironment.java @@ -68,6 +68,7 @@ import java.io.FileWriter; import java.io.IOException; import java.io.OutputStream; +import java.net.HttpURLConnection; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -549,32 +550,35 @@ public void onNamespaceCreation(String cdapNamespace, Map proper @Override public void createIdentity(String k8sNamespace, String identity) throws ApiException { - if (identity.equals("default")) { - // skip creating default service account as it already exists. - return; - } - KubeUtil.validateRFC1123LabelName(identity); - LOG.info("Creating credential identity: {}", identity); - V1ObjectMeta serviceAccountMetadata = new V1ObjectMeta(); - serviceAccountMetadata.setName(identity); - V1ServiceAccount serviceAccount = new V1ServiceAccount(); - serviceAccount.setMetadata(serviceAccountMetadata); try { - coreV1Api.createNamespacedServiceAccount(k8sNamespace, serviceAccount, - null, null, null, null); + LOG.info("Creating credential identity: {}", identity); + createK8sSaIfNotExists(k8sNamespace, identity); } catch (ApiException e) { - if (e.getCode() == 409) { - // ignore, the SA already exists. - return; - } LOG.error( String.format("Unable to create the service account %s with status %s and body: %s", - serviceAccount.getMetadata().getName(), e.getCode(), e.getResponseBody()), e); + identity, e.getCode(), e.getResponseBody()), e); throw e; } } + private void createK8sSaIfNotExists(String k8sNamespace, String serviceAccountName) + throws ApiException { + try { + coreV1Api.readNamespacedServiceAccount(serviceAccountName, k8sNamespace, null); + } catch (ApiException e) { + if (e.getCode() != HttpURLConnection.HTTP_NOT_FOUND) { + throw e; + } + V1ObjectMeta serviceAccountMetadata = new V1ObjectMeta(); + serviceAccountMetadata.setName(serviceAccountName); + V1ServiceAccount serviceAccount = new V1ServiceAccount(); + serviceAccount.setMetadata(serviceAccountMetadata); + coreV1Api.createNamespacedServiceAccount(k8sNamespace, serviceAccount, + null, null, null, null); + } + } + @Override public void deleteIdentity(String k8sNamespace, @Nullable String identity) throws ApiException { if (identity == null || identity.equals("default")) { diff --git a/cdap-source-control/src/main/java/io/cdap/cdap/sourcecontrol/operationrunner/RemoteSourceControlOperationRunner.java b/cdap-source-control/src/main/java/io/cdap/cdap/sourcecontrol/operationrunner/RemoteSourceControlOperationRunner.java index 3c493caceba6..2c24f9a93b47 100644 --- a/cdap-source-control/src/main/java/io/cdap/cdap/sourcecontrol/operationrunner/RemoteSourceControlOperationRunner.java +++ b/cdap-source-control/src/main/java/io/cdap/cdap/sourcecontrol/operationrunner/RemoteSourceControlOperationRunner.java @@ -78,7 +78,9 @@ public PushAppResponse push(PushAppOperationRequest pushAppOperationRequest) thr AuthenticationConfigException { try { RunnableTaskRequest request = RunnableTaskRequest.getBuilder(PushAppTask.class.getName()) - .withParam(GSON.toJson(pushAppOperationRequest)).build(); + .withParam(GSON.toJson(pushAppOperationRequest)) + .withNamespace(pushAppOperationRequest.getNamespaceId().getNamespace()) + .build(); LOG.trace("Pushing application {} to linked repository", pushAppOperationRequest.getApp()); byte[] result = remoteTaskExecutor.runTask(request); @@ -95,7 +97,9 @@ public PullAppResponse pull(PulAppOperationRequest pulAppOperationRequest) th AuthenticationConfigException { try { RunnableTaskRequest request = RunnableTaskRequest.getBuilder(PullAppTask.class.getName()) - .withParam(GSON.toJson(pulAppOperationRequest)).build(); + .withParam(GSON.toJson(pulAppOperationRequest)) + .withNamespace(pulAppOperationRequest.getApp().getNamespace()) + .build(); LOG.trace("Pulling application {} from linked repository", pulAppOperationRequest.getApp()); byte[] result = remoteTaskExecutor.runTask(request); @@ -112,7 +116,9 @@ public RepositoryAppsResponse list(NamespaceRepository nameSpaceRepository) throws AuthenticationConfigException, NotFoundException { try { RunnableTaskRequest request = RunnableTaskRequest.getBuilder(ListAppsTask.class.getName()) - .withParam(GSON.toJson(nameSpaceRepository)).build(); + .withParam(GSON.toJson(nameSpaceRepository)) + .withNamespace(nameSpaceRepository.getNamespaceId().getNamespace()) + .build(); LOG.trace("Listing applications for namespace {} in linked repository", nameSpaceRepository.getNamespaceId()); byte[] result = remoteTaskExecutor.runTask(request); return GSON.fromJson(new String(result, StandardCharsets.UTF_8), RepositoryAppsResponse.class);