This is an automated email from the ASF dual-hosted git repository. dimuthuupe pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/airavata-mft.git
commit 013ed494eb958990d0a6f90186a53103e1237bcd Author: Dimuthu Wannipurage <[email protected]> AuthorDate: Thu Apr 16 00:20:31 2020 -0400 Adding azure blob transfer support --- agent/pom.xml | 16 +++ .../airavata/mft/core/ConnectorResolver.java | 10 ++ .../mft/core/MetadataCollectorResolver.java | 3 + .../resource/server/backend/ResourceBackend.java | 5 + .../backend/airavata/AiravataResourceBackend.java | 20 ++++ .../backend/file/FileBasedResourceBackend.java | 44 +++++++- .../server/backend/sql/SQLResourceBackend.java | 20 ++++ .../server/handler/ResourceServiceHandler.java | 91 +++++++++++++---- .../resources/distribution/conf/resources.json | 6 ++ .../server/src/main/resources/resources.json | 6 ++ .../stub/src/main/proto/ResourceService.proto | 70 +++++++++++++ .../mft/secret/server/backend/SecretBackend.java | 5 + .../backend/airavata/AiravataSecretBackend.java | 20 ++++ .../backend/file/FileBasedSecretBackend.java | 40 ++++++++ .../server/backend/sql/SQLSecretBackend.java | 20 ++++ .../server/handler/SecretServiceHandler.java | 57 +++++++++++ .../main/resources/distribution/conf/secrets.json | 5 + .../server/src/main/resources/secrets.json | 5 + .../stub/src/main/proto/SecretService.proto | 70 +++++++++++++ transport/{ => azure-transport}/pom.xml | 21 ++-- .../transport/azure/AzureMetadataCollector.java | 112 +++++++++++++++++++++ .../mft/transport/azure/AzureReceiver.java | 111 ++++++++++++++++++++ .../airavata/mft/transport/azure/AzureSender.java | 77 ++++++++++++++ transport/pom.xml | 1 + 24 files changed, 801 insertions(+), 34 deletions(-) diff --git a/agent/pom.xml b/agent/pom.xml index cd00e66..80cab0c 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -50,6 +50,11 @@ </dependency> <dependency> <groupId>org.apache.airavata</groupId> + <artifactId>mft-azure-transport</artifactId> + <version>0.01-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.apache.airavata</groupId> <artifactId>mft-admin</artifactId> <version>0.01-SNAPSHOT</version> </dependency> @@ -57,6 +62,17 @@ <groupId>com.orbitz.consul</groupId> <artifactId>consul-client</artifactId> <version>${consul.client}</version> + <exclusions> + <exclusion> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + <version>2.10.0</version> </dependency> <dependency> <groupId>org.slf4j</groupId> diff --git a/core/src/main/java/org/apache/airavata/mft/core/ConnectorResolver.java b/core/src/main/java/org/apache/airavata/mft/core/ConnectorResolver.java index 3f76d07..5c49678 100644 --- a/core/src/main/java/org/apache/airavata/mft/core/ConnectorResolver.java +++ b/core/src/main/java/org/apache/airavata/mft/core/ConnectorResolver.java @@ -57,6 +57,16 @@ public final class ConnectorResolver { break; } break; + case "AZURE": + switch (direction) { + case "IN": + className = "org.apache.airavata.mft.transport.azure.AzureReceiver"; + break; + case "OUT": + className = "org.apache.airavata.mft.transport.azure.AzureSender"; + break; + } + break; } if (className != null) { diff --git a/core/src/main/java/org/apache/airavata/mft/core/MetadataCollectorResolver.java b/core/src/main/java/org/apache/airavata/mft/core/MetadataCollectorResolver.java index 653e56d..e8ca689 100644 --- a/core/src/main/java/org/apache/airavata/mft/core/MetadataCollectorResolver.java +++ b/core/src/main/java/org/apache/airavata/mft/core/MetadataCollectorResolver.java @@ -36,6 +36,9 @@ public final class MetadataCollectorResolver { case "S3": className = "org.apache.airavata.mft.transport.s3.S3MetadataCollector"; break; + case "AZURE": + className = "org.apache.airavata.mft.transport.azure.AzureMetadataCollector"; + break; } if (className != null) { diff --git a/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/ResourceBackend.java b/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/ResourceBackend.java index 6857bde..231c738 100644 --- a/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/ResourceBackend.java +++ b/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/ResourceBackend.java @@ -46,4 +46,9 @@ public interface ResourceBackend { public S3Resource createS3Resource(S3ResourceCreateRequest request) throws Exception; public boolean updateS3Resource(S3ResourceUpdateRequest request) throws Exception; public boolean deleteS3Resource(S3ResourceDeleteRequest request) throws Exception; + + public Optional<AzureResource> getAzureResource(AzureResourceGetRequest request) throws Exception; + public AzureResource createAzureResource(AzureResourceCreateRequest request) throws Exception; + public boolean updateAzureResource(AzureResourceUpdateRequest request) throws Exception; + public boolean deleteAzureResource(AzureResourceDeleteRequest request) throws Exception; } diff --git a/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/airavata/AiravataResourceBackend.java b/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/airavata/AiravataResourceBackend.java index f888134..09de364 100644 --- a/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/airavata/AiravataResourceBackend.java +++ b/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/airavata/AiravataResourceBackend.java @@ -191,4 +191,24 @@ public class AiravataResourceBackend implements ResourceBackend { throw new UnsupportedOperationException("Operation is not supported in backend"); } + + @Override + public Optional<AzureResource> getAzureResource(AzureResourceGetRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public AzureResource createAzureResource(AzureResourceCreateRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public boolean updateAzureResource(AzureResourceUpdateRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public boolean deleteAzureResource(AzureResourceDeleteRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } } diff --git a/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/file/FileBasedResourceBackend.java b/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/file/FileBasedResourceBackend.java index cb504b3..c8d1d4c 100644 --- a/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/file/FileBasedResourceBackend.java +++ b/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/file/FileBasedResourceBackend.java @@ -136,7 +136,6 @@ public class FileBasedResourceBackend implements ResourceBackend { JSONArray resourceList = (JSONArray) obj; - System.out.println("All resources "); List<LocalResource> localResources = (List<LocalResource>) resourceList.stream() .filter(resource -> "LOCAL".equals(((JSONObject) resource).get("type").toString())) .map(resource -> { @@ -180,7 +179,6 @@ public class FileBasedResourceBackend implements ResourceBackend { JSONArray resourceList = (JSONArray) obj; - System.out.println("All resources "); List<S3Resource> s3Resources = (List<S3Resource>) resourceList.stream() .filter(resource -> "S3".equals(((JSONObject) resource).get("type").toString())) .map(resource -> { @@ -217,4 +215,46 @@ public class FileBasedResourceBackend implements ResourceBackend { throw new UnsupportedOperationException("Operation is not supported in backend"); } + + @Override + public Optional<AzureResource> getAzureResource(AzureResourceGetRequest request) throws Exception { + JSONParser jsonParser = new JSONParser(); + InputStream inputStream = FileBasedResourceBackend.class.getClassLoader().getResourceAsStream(resourceFile); + + try (InputStreamReader reader = new InputStreamReader(inputStream)) { + Object obj = jsonParser.parse(reader); + + JSONArray resourceList = (JSONArray) obj; + + List<AzureResource> azureResources = (List<AzureResource>) resourceList.stream() + .filter(resource -> "AZURE".equals(((JSONObject) resource).get("type").toString())) + .map(resource -> { + JSONObject r = (JSONObject) resource; + + AzureResource azureResource = AzureResource.newBuilder() + .setBlobName(r.get("blobName").toString()) + .setContainer(r.get("container").toString()) + .setResourceId(r.get("resourceId").toString()) + .build(); + + return azureResource; + }).collect(Collectors.toList()); + return azureResources.stream().filter(r -> request.getResourceId().equals(r.getResourceId())).findFirst(); + } + } + + @Override + public AzureResource createAzureResource(AzureResourceCreateRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public boolean updateAzureResource(AzureResourceUpdateRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public boolean deleteAzureResource(AzureResourceDeleteRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } } diff --git a/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/sql/SQLResourceBackend.java b/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/sql/SQLResourceBackend.java index a02d5a9..b02f9e2 100644 --- a/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/sql/SQLResourceBackend.java +++ b/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/backend/sql/SQLResourceBackend.java @@ -155,4 +155,24 @@ public class SQLResourceBackend implements ResourceBackend { throw new UnsupportedOperationException("Operation is not supported in backend"); } + + @Override + public Optional<AzureResource> getAzureResource(AzureResourceGetRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public AzureResource createAzureResource(AzureResourceCreateRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public boolean updateAzureResource(AzureResourceUpdateRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public boolean deleteAzureResource(AzureResourceDeleteRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } } diff --git a/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/handler/ResourceServiceHandler.java b/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/handler/ResourceServiceHandler.java index 16bd850..edcc7c9 100644 --- a/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/handler/ResourceServiceHandler.java +++ b/services/resource-service/server/src/main/java/org/apache/airavata/mft/resource/server/handler/ResourceServiceHandler.java @@ -14,22 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.airavata.mft.resource.server.handler; @@ -294,7 +278,8 @@ public class ResourceServiceHandler extends ResourceServiceGrpc.ResourceServiceI responseObserver.onError(Status.INTERNAL.withCause(e) .withDescription("Failed in creating the S3 resource") .asRuntimeException()); - } } + } + } @Override public void updateS3Resource(S3ResourceUpdateRequest request, StreamObserver<Empty> responseObserver) { @@ -307,7 +292,8 @@ public class ResourceServiceHandler extends ResourceServiceGrpc.ResourceServiceI responseObserver.onError(Status.INTERNAL.withCause(e) .withDescription("Failed in updating the S3 resource with id " + request.getResourceId()) .asRuntimeException()); - } } + } + } @Override public void deleteS3Resource(S3ResourceDeleteRequest request, StreamObserver<Empty> responseObserver) { @@ -324,5 +310,72 @@ public class ResourceServiceHandler extends ResourceServiceGrpc.ResourceServiceI responseObserver.onError(Status.INTERNAL.withCause(e) .withDescription("Failed in deleting the S3 resource with id " + request.getResourceId()) .asRuntimeException()); - } } + } + } + + @Override + public void getAzureResource(AzureResourceGetRequest request, StreamObserver<AzureResource> responseObserver) { + try { + this.backend.getAzureResource(request).ifPresentOrElse(resource -> { + responseObserver.onNext(resource); + responseObserver.onCompleted(); + }, () -> { + responseObserver.onError(Status.INTERNAL + .withDescription("No Azure Resource with id " + request.getResourceId()) + .asRuntimeException()); + }); + } catch (Exception e) { + logger.error("Failed in retrieving Azure resource with id {}", request.getResourceId(), e); + + responseObserver.onError(Status.INTERNAL.withCause(e) + .withDescription("Failed in retrieving Azure resource with id " + request.getResourceId()) + .asRuntimeException()); + } + } + + @Override + public void createAzureResource(AzureResourceCreateRequest request, StreamObserver<AzureResource> responseObserver) { + try { + responseObserver.onNext(this.backend.createAzureResource(request)); + responseObserver.onCompleted(); + } catch (Exception e) { + logger.error("Failed in creating the Azure resource", e); + + responseObserver.onError(Status.INTERNAL.withCause(e) + .withDescription("Failed in creating the Azure resource") + .asRuntimeException()); + } + } + + @Override + public void updateAzureResource(AzureResourceUpdateRequest request, StreamObserver<Empty> responseObserver) { + try { + this.backend.updateAzureResource(request); + responseObserver.onCompleted(); + } catch (Exception e) { + logger.error("Failed in updating the Azure resource {}", request.getResourceId(), e); + + responseObserver.onError(Status.INTERNAL.withCause(e) + .withDescription("Failed in updating the Azure resource with id " + request.getResourceId()) + .asRuntimeException()); + } + } + + @Override + public void deleteAzureResource(AzureResourceDeleteRequest request, StreamObserver<Empty> responseObserver) { + try { + boolean res = this.backend.deleteAzureResource(request); + if (res) { + responseObserver.onCompleted(); + } else { + responseObserver.onError(new Exception("Failed to delete Azure Resource with id " + request.getResourceId())); + } + } catch (Exception e) { + logger.error("Failed in deleting the Azure resource {}", request.getResourceId(), e); + + responseObserver.onError(Status.INTERNAL.withCause(e) + .withDescription("Failed in deleting the Azure resource with id " + request.getResourceId()) + .asRuntimeException()); + } + } } diff --git a/services/resource-service/server/src/main/resources/distribution/conf/resources.json b/services/resource-service/server/src/main/resources/distribution/conf/resources.json index 0791f2c..6b1f9c3 100644 --- a/services/resource-service/server/src/main/resources/distribution/conf/resources.json +++ b/services/resource-service/server/src/main/resources/distribution/conf/resources.json @@ -32,5 +32,11 @@ "resourcePath": "new-file.txt", "region": "us-east-2", "bucketName": "s3-bucket" + }, + { + "type": "AZURE", + "resourceId": "azure-blob", + "container": "sample-container", + "blobName": "sample.blob" } ] \ No newline at end of file diff --git a/services/resource-service/server/src/main/resources/resources.json b/services/resource-service/server/src/main/resources/resources.json index 0791f2c..6b1f9c3 100644 --- a/services/resource-service/server/src/main/resources/resources.json +++ b/services/resource-service/server/src/main/resources/resources.json @@ -32,5 +32,11 @@ "resourcePath": "new-file.txt", "region": "us-east-2", "bucketName": "s3-bucket" + }, + { + "type": "AZURE", + "resourceId": "azure-blob", + "container": "sample-container", + "blobName": "sample.blob" } ] \ No newline at end of file diff --git a/services/resource-service/stub/src/main/proto/ResourceService.proto b/services/resource-service/stub/src/main/proto/ResourceService.proto index 42af6b9..4b757b5 100644 --- a/services/resource-service/stub/src/main/proto/ResourceService.proto +++ b/services/resource-service/stub/src/main/proto/ResourceService.proto @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + syntax = "proto3"; option java_multiple_files = true; @@ -117,6 +134,33 @@ message S3ResourceDeleteRequest { string resourceId = 1; } +// Azure Resource + +message AzureResource { + string resourceId = 1; + string container = 2; + string blobName = 3; +} + +message AzureResourceGetRequest { + string resourceId = 1; +} + +message AzureResourceCreateRequest { + string container = 1; + string blobName = 2; +} + +message AzureResourceUpdateRequest { + string resourceId = 1; + string container = 2; + string blobName = 3; +} + +message AzureResourceDeleteRequest { + string resourceId = 1; +} + service ResourceService { // SCP Storage @@ -221,4 +265,30 @@ service ResourceService { delete: "/v1.0/resource/s3" }; } + + // Azure Resource + + rpc getAzureResource (AzureResourceGetRequest) returns (AzureResource) { + option (google.api.http) = { + get: "/v1.0/resource/azure" + }; + } + + rpc createAzureResource (AzureResourceCreateRequest) returns (AzureResource) { + option (google.api.http) = { + post: "/v1.0/resource/azure" + }; + } + + rpc updateAzureResource (AzureResourceUpdateRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + put: "/v1.0/resource/azure" + }; + } + + rpc deleteAzureResource (AzureResourceDeleteRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1.0/resource/azure" + }; + } } \ No newline at end of file diff --git a/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/SecretBackend.java b/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/SecretBackend.java index 3172387..e187beb 100644 --- a/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/SecretBackend.java +++ b/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/SecretBackend.java @@ -35,4 +35,9 @@ public interface SecretBackend { public S3Secret createS3Secret(S3SecretCreateRequest request) throws Exception; public boolean updateS3Secret(S3SecretUpdateRequest request) throws Exception; public boolean deleteS3Secret(S3SecretDeleteRequest request) throws Exception; + + public Optional<AzureSecret> getAzureSecret(AzureSecretGetRequest request) throws Exception; + public AzureSecret createAzureSecret(AzureSecretCreateRequest request) throws Exception; + public boolean updateAzureSecret(AzureSecretUpdateRequest request) throws Exception; + public boolean deleteAzureSecret(AzureSecretDeleteRequest request) throws Exception; } diff --git a/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/airavata/AiravataSecretBackend.java b/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/airavata/AiravataSecretBackend.java index a224c0a..9c52eb1 100644 --- a/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/airavata/AiravataSecretBackend.java +++ b/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/airavata/AiravataSecretBackend.java @@ -99,4 +99,24 @@ public class AiravataSecretBackend implements SecretBackend { public boolean deleteS3Secret(S3SecretDeleteRequest request) throws Exception { throw new UnsupportedOperationException("Operation is not supported in backend"); } + + @Override + public Optional<AzureSecret> getAzureSecret(AzureSecretGetRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public AzureSecret createAzureSecret(AzureSecretCreateRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public boolean updateAzureSecret(AzureSecretUpdateRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public boolean deleteAzureSecret(AzureSecretDeleteRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } } diff --git a/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/file/FileBasedSecretBackend.java b/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/file/FileBasedSecretBackend.java index d2d97ad..4203929 100644 --- a/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/file/FileBasedSecretBackend.java +++ b/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/file/FileBasedSecretBackend.java @@ -130,4 +130,44 @@ public class FileBasedSecretBackend implements SecretBackend { public boolean deleteS3Secret(S3SecretDeleteRequest request) throws Exception { throw new UnsupportedOperationException("Operation is not supported in backend"); } + + @Override + public Optional<AzureSecret> getAzureSecret(AzureSecretGetRequest request) throws Exception { + JSONParser jsonParser = new JSONParser(); + InputStream inputStream = FileBasedSecretBackend.class.getClassLoader().getResourceAsStream(secretFile); + + try (InputStreamReader reader = new InputStreamReader(inputStream)) { + Object obj = jsonParser.parse(reader); + + JSONArray resourceList = (JSONArray) obj; + + List<AzureSecret> azureSecrets = (List<AzureSecret>) resourceList.stream() + .filter(resource -> "AZURE".equals(((JSONObject) resource).get("type").toString())) + .map(resource -> { + JSONObject r = (JSONObject) resource; + + AzureSecret azureSecret = AzureSecret.newBuilder() + .setSecretId(r.get("secretId").toString()) + .setConnectionString(r.get("connectionString").toString()).build(); + + return azureSecret; + }).collect(Collectors.toList()); + return azureSecrets.stream().filter(r -> request.getSecretId().equals(r.getSecretId())).findFirst(); + } + } + + @Override + public AzureSecret createAzureSecret(AzureSecretCreateRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public boolean updateAzureSecret(AzureSecretUpdateRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public boolean deleteAzureSecret(AzureSecretDeleteRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } } diff --git a/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/sql/SQLSecretBackend.java b/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/sql/SQLSecretBackend.java index 79da582..e9c1e40 100644 --- a/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/sql/SQLSecretBackend.java +++ b/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/backend/sql/SQLSecretBackend.java @@ -90,4 +90,24 @@ public class SQLSecretBackend implements SecretBackend { public boolean deleteS3Secret(S3SecretDeleteRequest request) throws Exception { throw new UnsupportedOperationException("Operation is not supported in backend"); } + + @Override + public Optional<AzureSecret> getAzureSecret(AzureSecretGetRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public AzureSecret createAzureSecret(AzureSecretCreateRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public boolean updateAzureSecret(AzureSecretUpdateRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } + + @Override + public boolean deleteAzureSecret(AzureSecretDeleteRequest request) throws Exception { + throw new UnsupportedOperationException("Operation is not supported in backend"); + } } diff --git a/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/handler/SecretServiceHandler.java b/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/handler/SecretServiceHandler.java index 72a9706..65ee4f8 100644 --- a/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/handler/SecretServiceHandler.java +++ b/services/secret-service/server/src/main/java/org/apache/airavata/mft/secret/server/handler/SecretServiceHandler.java @@ -137,4 +137,61 @@ public class SecretServiceHandler extends SecretServiceGrpc.SecretServiceImplBas .asRuntimeException()); } } + + @Override + public void getAzureSecret(AzureSecretGetRequest request, StreamObserver<AzureSecret> responseObserver) { + try { + this.backend.getAzureSecret(request).ifPresentOrElse(secret -> { + responseObserver.onNext(secret); + responseObserver.onCompleted(); + }, () -> { + responseObserver.onError(Status.INTERNAL + .withDescription("No Azure Secret with id " + request.getSecretId()) + .asRuntimeException()); + }); + + } catch (Exception e) { + logger.error("Error in retrieving Azure Secret with id " + request.getSecretId(), e); + responseObserver.onError(Status.INTERNAL.withCause(e) + .withDescription("Error in retrieving Azure Secret with id " + request.getSecretId()) + .asRuntimeException()); + } + super.getAzureSecret(request, responseObserver); + } + + @Override + public void createAzureSecret(AzureSecretCreateRequest request, StreamObserver<AzureSecret> responseObserver) { + try { + this.backend.createAzureSecret(request); + } catch (Exception e) { + logger.error("Error in creating Azure Secret", e); + responseObserver.onError(Status.INTERNAL.withCause(e) + .withDescription("Error in creating Azure Secret") + .asRuntimeException()); + } + } + + @Override + public void updateAzureSecret(AzureSecretUpdateRequest request, StreamObserver<Empty> responseObserver) { + try { + this.backend.updateAzureSecret(request); + } catch (Exception e) { + logger.error("Error in updating Azure Secret with id {}", request.getSecretId(), e); + responseObserver.onError(Status.INTERNAL.withCause(e) + .withDescription("Error in updating Azure Secret with id " + request.getSecretId()) + .asRuntimeException()); + } + } + + @Override + public void deleteAzureSecret(AzureSecretDeleteRequest request, StreamObserver<Empty> responseObserver) { + try { + this.backend.deleteAzureSecret(request); + } catch (Exception e) { + logger.error("Error in deleting Azure Secret with id {}", request.getSecretId(), e); + responseObserver.onError(Status.INTERNAL.withCause(e) + .withDescription("Error in deleting Azure Secret with id " + request.getSecretId()) + .asRuntimeException()); + } + } } diff --git a/services/secret-service/server/src/main/resources/distribution/conf/secrets.json b/services/secret-service/server/src/main/resources/distribution/conf/secrets.json index 042efaf..49cfc5d 100644 --- a/services/secret-service/server/src/main/resources/distribution/conf/secrets.json +++ b/services/secret-service/server/src/main/resources/distribution/conf/secrets.json @@ -11,5 +11,10 @@ "secretId": "s3-cred", "accessKey": "", "secretKey": "" + }, + { + "type": "AZURE", + "secretId": "azure-cred", + "connectionString": "azure-con" } ] \ No newline at end of file diff --git a/services/secret-service/server/src/main/resources/secrets.json b/services/secret-service/server/src/main/resources/secrets.json index fd5af0e..8262246 100644 --- a/services/secret-service/server/src/main/resources/secrets.json +++ b/services/secret-service/server/src/main/resources/secrets.json @@ -12,5 +12,10 @@ "secretId": "s3-cred", "accessKey": "", "secretKey": "" + }, + { + "type": "AZURE", + "secretId": "azure-cred", + "connectionString": "azure-con" } ] \ No newline at end of file diff --git a/services/secret-service/stub/src/main/proto/SecretService.proto b/services/secret-service/stub/src/main/proto/SecretService.proto index 93dfe3e..1fa8187 100644 --- a/services/secret-service/stub/src/main/proto/SecretService.proto +++ b/services/secret-service/stub/src/main/proto/SecretService.proto @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + syntax = "proto3"; option java_multiple_files = true; @@ -71,6 +88,33 @@ message S3SecretDeleteRequest { AuthToken authzToken = 2; } +// Azure + +message AzureSecret { + string secretId = 1; + string connectionString = 2; +} + +message AzureSecretGetRequest { + string secretId = 1; + AuthToken authzToken = 2; +} + +message AzureSecretCreateRequest { + string connectionString = 1; + AuthToken authzToken = 2; +} + +message AzureSecretUpdateRequest { + string secretId = 1; + string connectionString = 2; + AuthToken authzToken = 3; +} + +message AzureSecretDeleteRequest { + string secretId = 1; + AuthToken authzToken = 2; +} service SecretService { rpc getSCPSecret (SCPSecretGetRequest) returns (SCPSecret) { @@ -120,4 +164,30 @@ service SecretService { delete: "/v1.0/secret/s3" }; } + + // Azure + + rpc getAzureSecret (AzureSecretGetRequest) returns (AzureSecret) { + option (google.api.http) = { + get: "/v1.0/secret/azure" + }; + } + + rpc createAzureSecret (AzureSecretCreateRequest) returns (AzureSecret) { + option (google.api.http) = { + post: "/v1.0/secret/azure" + }; + } + + rpc updateAzureSecret (AzureSecretUpdateRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + put: "/v1.0/secret/azure" + }; + } + + rpc deleteAzureSecret (AzureSecretDeleteRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1.0/secret/azure" + }; + } } \ No newline at end of file diff --git a/transport/pom.xml b/transport/azure-transport/pom.xml old mode 100755 new mode 100644 similarity index 74% copy from transport/pom.xml copy to transport/azure-transport/pom.xml index e1373da..d2d8d5f --- a/transport/pom.xml +++ b/transport/azure-transport/pom.xml @@ -24,31 +24,26 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> - <artifactId>airavata-mft</artifactId> + <artifactId>mft-transport</artifactId> <groupId>org.apache.airavata</groupId> <version>0.01-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> - <artifactId>mft-transport</artifactId> - <packaging>pom</packaging> - <modules> - <module>scp-transport</module> - <module>local-transport</module> - <module>s3-transport</module> - </modules> + <artifactId>mft-azure-transport</artifactId> + <dependencies> <dependency> - <groupId>org.apache.airavata</groupId> - <artifactId>mft-resource-service-client</artifactId> - <version>0.01-SNAPSHOT</version> + <groupId>com.azure</groupId> + <artifactId>azure-storage-blob</artifactId> + <version>12.0.0</version> </dependency> <dependency> <groupId>org.apache.airavata</groupId> - <artifactId>mft-secret-service-client</artifactId> + <artifactId>mft-core</artifactId> <version>0.01-SNAPSHOT</version> </dependency> - </dependencies> + </dependencies> </project> \ No newline at end of file diff --git a/transport/azure-transport/src/main/java/org/apache/airavata/mft/transport/azure/AzureMetadataCollector.java b/transport/azure-transport/src/main/java/org/apache/airavata/mft/transport/azure/AzureMetadataCollector.java new file mode 100644 index 0000000..cc6e669 --- /dev/null +++ b/transport/azure-transport/src/main/java/org/apache/airavata/mft/transport/azure/AzureMetadataCollector.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.airavata.mft.transport.azure; + +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.BlobContainerClient; +import com.azure.storage.blob.BlobServiceClient; +import com.azure.storage.blob.BlobServiceClientBuilder; +import com.azure.storage.blob.models.BlobProperties; +import org.apache.airavata.mft.core.ResourceMetadata; +import org.apache.airavata.mft.core.api.MetadataCollector; +import org.apache.airavata.mft.resource.client.ResourceServiceClient; +import org.apache.airavata.mft.resource.service.AzureResource; +import org.apache.airavata.mft.resource.service.AzureResourceGetRequest; +import org.apache.airavata.mft.resource.service.ResourceServiceGrpc; +import org.apache.airavata.mft.secret.client.SecretServiceClient; +import org.apache.airavata.mft.secret.service.AzureSecret; +import org.apache.airavata.mft.secret.service.AzureSecretGetRequest; +import org.apache.airavata.mft.secret.service.SecretServiceGrpc; + +public class AzureMetadataCollector implements MetadataCollector { + + private String resourceServiceHost; + private int resourceServicePort; + private String secretServiceHost; + private int secretServicePort; + boolean initialized = false; + + @Override + public void init(String resourceServiceHost, int resourceServicePort, String secretServiceHost, int secretServicePort) { + this.resourceServiceHost = resourceServiceHost; + this.resourceServicePort = resourceServicePort; + this.secretServiceHost = secretServiceHost; + this.secretServicePort = secretServicePort; + this.initialized = true; + } + + private void checkInitialized() { + if (!initialized) { + throw new IllegalStateException("Azure Metadata Collector is not initialized"); + } + } + + @Override + public ResourceMetadata getGetResourceMetadata(String resourceId, String credentialToken) throws Exception { + checkInitialized(); + + if (!isAvailable(resourceId, credentialToken)) { + throw new Exception("Azure blob can not find for resource id " + resourceId); + } + + ResourceServiceGrpc.ResourceServiceBlockingStub resourceClient = ResourceServiceClient.buildClient(resourceServiceHost, resourceServicePort); + AzureResource azureResource = resourceClient.getAzureResource(AzureResourceGetRequest.newBuilder().setResourceId(resourceId).build()); + + SecretServiceGrpc.SecretServiceBlockingStub secretClient = SecretServiceClient.buildClient(secretServiceHost, secretServicePort); + AzureSecret azureSecret = secretClient.getAzureSecret(AzureSecretGetRequest.newBuilder().setSecretId(credentialToken).build()); + + BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().connectionString(azureSecret.getConnectionString()).buildClient(); + + BlobClient blobClient = blobServiceClient.getBlobContainerClient(azureResource.getContainer()).getBlobClient(azureResource.getBlobName()); + + BlobProperties properties = blobClient.getBlockBlobClient().getProperties(); + ResourceMetadata metadata = new ResourceMetadata(); + metadata.setResourceSize(properties.getBlobSize()); + metadata.setCreatedTime(properties.getCreationTime().toEpochSecond()); + metadata.setUpdateTime(properties.getCreationTime().toEpochSecond()); + + byte[] contentMd5 = properties.getContentMd5(); + StringBuilder md5sb = new StringBuilder(); + for (byte aByte : contentMd5) { + md5sb.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1)); + } + + metadata.setMd5sum(md5sb.toString()); + + return metadata; + } + + @Override + public Boolean isAvailable(String resourceId, String credentialToken) throws Exception { + checkInitialized(); + + ResourceServiceGrpc.ResourceServiceBlockingStub resourceClient = ResourceServiceClient.buildClient(resourceServiceHost, resourceServicePort); + AzureResource azureResource = resourceClient.getAzureResource(AzureResourceGetRequest.newBuilder().setResourceId(resourceId).build()); + + SecretServiceGrpc.SecretServiceBlockingStub secretClient = SecretServiceClient.buildClient(secretServiceHost, secretServicePort); + AzureSecret azureSecret = secretClient.getAzureSecret(AzureSecretGetRequest.newBuilder().setSecretId(credentialToken).build()); + + BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().connectionString(azureSecret.getConnectionString()).buildClient(); + BlobContainerClient containerClient = blobServiceClient.getBlobContainerClient(azureResource.getContainer()); + boolean containerExists = containerClient.exists(); + if (!containerExists) { + return false; + } + return containerClient.getBlobClient(azureResource.getBlobName()).exists(); + } +} diff --git a/transport/azure-transport/src/main/java/org/apache/airavata/mft/transport/azure/AzureReceiver.java b/transport/azure-transport/src/main/java/org/apache/airavata/mft/transport/azure/AzureReceiver.java new file mode 100644 index 0000000..6685bd5 --- /dev/null +++ b/transport/azure-transport/src/main/java/org/apache/airavata/mft/transport/azure/AzureReceiver.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.airavata.mft.transport.azure; + +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.BlobContainerClient; +import com.azure.storage.blob.BlobServiceClient; +import com.azure.storage.blob.BlobServiceClientBuilder; +import com.azure.storage.blob.specialized.BlobInputStream; +import com.azure.storage.blob.specialized.BlockBlobClient; +import org.apache.airavata.mft.core.ConnectorContext; +import org.apache.airavata.mft.core.api.Connector; +import org.apache.airavata.mft.resource.client.ResourceServiceClient; +import org.apache.airavata.mft.resource.service.AzureResource; +import org.apache.airavata.mft.resource.service.AzureResourceGetRequest; +import org.apache.airavata.mft.resource.service.ResourceServiceGrpc; +import org.apache.airavata.mft.secret.client.SecretServiceClient; +import org.apache.airavata.mft.secret.service.AzureSecret; +import org.apache.airavata.mft.secret.service.AzureSecretGetRequest; +import org.apache.airavata.mft.secret.service.SecretServiceGrpc; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.OutputStream; + +public class AzureReceiver implements Connector { + + private static final Logger logger = LoggerFactory.getLogger(AzureReceiver.class); + + private boolean initialized = false; + private AzureResource azureResource; + BlobContainerClient containerClient; + + @Override + public void init(String resourceId, String credentialToken, String resourceServiceHost, int resourceServicePort, String secretServiceHost, int secretServicePort) throws Exception { + this.initialized = true; + + ResourceServiceGrpc.ResourceServiceBlockingStub resourceClient = ResourceServiceClient.buildClient(resourceServiceHost, resourceServicePort); + this.azureResource = resourceClient.getAzureResource(AzureResourceGetRequest.newBuilder().setResourceId(resourceId).build()); + + SecretServiceGrpc.SecretServiceBlockingStub secretClient = SecretServiceClient.buildClient(secretServiceHost, secretServicePort); + AzureSecret azureSecret = secretClient.getAzureSecret(AzureSecretGetRequest.newBuilder().setSecretId(credentialToken).build()); + + BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().connectionString(azureSecret.getConnectionString()).buildClient(); + this.containerClient = blobServiceClient.getBlobContainerClient(azureResource.getContainer()); + } + + @Override + public void destroy() { + + } + + private void checkInitialized() { + if (!initialized) { + throw new IllegalStateException("Azure Receiver is not initialized"); + } + } + + @Override + public void startStream(ConnectorContext context) throws Exception { + logger.info("Starting azure receive for remote server for transfer {}", context.getTransferId()); + checkInitialized(); + BlobClient blobClient = containerClient.getBlobClient(azureResource.getBlobName()); + BlobInputStream blobInputStream = blobClient.openInputStream(); + + OutputStream streamOs = context.getStreamBuffer().getOutputStream(); + + long fileSize = context.getMetadata().getResourceSize(); + + byte[] buf = new byte[1024]; + while (true) { + int bufSize = 0; + + if (buf.length < fileSize) { + bufSize = buf.length; + } else { + bufSize = (int) fileSize; + } + bufSize = blobInputStream.read(buf, 0, bufSize); + + if (bufSize < 0) { + break; + } + + streamOs.write(buf, 0, bufSize); + streamOs.flush(); + + fileSize -= bufSize; + if (fileSize == 0L) + break; + } + + streamOs.close(); + logger.info("Completed azure receive for remote server for transfer {}", context.getTransferId()); + } +} diff --git a/transport/azure-transport/src/main/java/org/apache/airavata/mft/transport/azure/AzureSender.java b/transport/azure-transport/src/main/java/org/apache/airavata/mft/transport/azure/AzureSender.java new file mode 100644 index 0000000..cbca14e --- /dev/null +++ b/transport/azure-transport/src/main/java/org/apache/airavata/mft/transport/azure/AzureSender.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.airavata.mft.transport.azure; + +import com.azure.storage.blob.BlobContainerClient; +import com.azure.storage.blob.BlobServiceClient; +import com.azure.storage.blob.BlobServiceClientBuilder; +import com.azure.storage.blob.specialized.BlockBlobClient; +import org.apache.airavata.mft.core.ConnectorContext; +import org.apache.airavata.mft.core.api.Connector; +import org.apache.airavata.mft.resource.client.ResourceServiceClient; +import org.apache.airavata.mft.resource.service.AzureResource; +import org.apache.airavata.mft.resource.service.AzureResourceGetRequest; +import org.apache.airavata.mft.resource.service.ResourceServiceGrpc; +import org.apache.airavata.mft.secret.client.SecretServiceClient; +import org.apache.airavata.mft.secret.service.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AzureSender implements Connector { + + private static final Logger logger = LoggerFactory.getLogger(AzureSender.class); + + private boolean initialized = false; + private AzureResource azureResource; + BlobContainerClient containerClient; + + @Override + public void init(String resourceId, String credentialToken, String resourceServiceHost, int resourceServicePort, String secretServiceHost, int secretServicePort) throws Exception { + this.initialized = true; + + ResourceServiceGrpc.ResourceServiceBlockingStub resourceClient = ResourceServiceClient.buildClient(resourceServiceHost, resourceServicePort); + this.azureResource = resourceClient.getAzureResource(AzureResourceGetRequest.newBuilder().setResourceId(resourceId).build()); + + SecretServiceGrpc.SecretServiceBlockingStub secretClient = SecretServiceClient.buildClient(secretServiceHost, secretServicePort); + AzureSecret azureSecret = secretClient.getAzureSecret(AzureSecretGetRequest.newBuilder().setSecretId(credentialToken).build()); + + BlobServiceClient blobServiceClient = new BlobServiceClientBuilder().connectionString(azureSecret.getConnectionString()).buildClient(); + this.containerClient = blobServiceClient.getBlobContainerClient(azureResource.getContainer()); + } + + @Override + public void destroy() { + + } + + private void checkInitialized() { + if (!initialized) { + throw new IllegalStateException("Azure Sender is not initialized"); + } + } + + @Override + public void startStream(ConnectorContext context) throws Exception { + logger.info("Starting scp send for remote server for transfer {}", context.getTransferId()); + checkInitialized(); + BlockBlobClient blockBlobClient = containerClient.getBlobClient(azureResource.getBlobName()).getBlockBlobClient(); + blockBlobClient.upload(context.getStreamBuffer().getInputStream(), context.getMetadata().getResourceSize(), true); + logger.info("Completed scp send for remote server for transfer {}", context.getTransferId()); + + } +} diff --git a/transport/pom.xml b/transport/pom.xml index e1373da..9a68166 100755 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -36,6 +36,7 @@ <module>scp-transport</module> <module>local-transport</module> <module>s3-transport</module> + <module>azure-transport</module> </modules> <dependencies> <dependency>
