blob: 9955a5f80b7f73f5caf408de9a75c842b04367ad [file] [log] [blame]
/*
* 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.gcp;
import com.google.api.gax.paging.Page;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.Bucket;
import com.google.cloud.storage.Storage;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.storage.StorageOptions;
import org.apache.airavata.mft.agent.stub.*;
import org.apache.airavata.mft.core.api.MetadataCollector;
import org.apache.airavata.mft.credential.stubs.gcs.GCSSecret;
import org.apache.airavata.mft.resource.stubs.gcs.storage.GCSStorage;
import java.io.File;
import java.security.PrivateKey;
import java.time.temporal.ChronoField;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GCSMetadataCollector implements MetadataCollector {
boolean initialized = false;
private GCSStorage gcsStorage;
private GCSSecret gcsSecret;
@Override
public void init(StorageWrapper storage, SecretWrapper secret) {
this.gcsStorage = storage.getGcs();
this.gcsSecret = secret.getGcs();
this.initialized = true;
}
private void checkInitialized() {
if (!initialized) {
throw new IllegalStateException("GCS Metadata Collector is not initialized");
}
}
@Override
public ResourceMetadata getResourceMetadata(String resourcePath, boolean recursiveSearch) throws Exception {
checkInitialized();
PrivateKey privKey = GCSUtil.getPrivateKey(gcsSecret.getPrivateKey());
try (Storage storage = StorageOptions.newBuilder().setCredentials(ServiceAccountCredentials.newBuilder()
.setProjectId(gcsSecret.getProjectId())
.setPrivateKey(privKey)
.setClientEmail(gcsSecret.getClientEmail())
.build()).build().getService()) {
ResourceMetadata.Builder resourceBuilder = ResourceMetadata.newBuilder();
if (gcsStorage.getBucketName().isEmpty() && resourcePath.isEmpty()) // Load bucket list
{
Page<Bucket> buckets = storage.list();
DirectoryMetadata.Builder parentDir = DirectoryMetadata.newBuilder();
parentDir.setResourcePath("");
parentDir.setFriendlyName("");
for (Bucket b : buckets.getValues()) {
DirectoryMetadata.Builder bucketDir = DirectoryMetadata.newBuilder();
bucketDir.setFriendlyName(b.getName());
bucketDir.setResourcePath(b.getName());
bucketDir.setCreatedTime(b.getCreateTimeOffsetDateTime().getLong(ChronoField.INSTANT_SECONDS));
bucketDir.setUpdateTime(b.getUpdateTimeOffsetDateTime().getLong(ChronoField.INSTANT_SECONDS));
parentDir.addDirectories(bucketDir);
}
resourceBuilder.setDirectory(parentDir);
return resourceBuilder.build();
}
// If directory path or top level bucket path
final String dirPath = resourcePath.endsWith("/") ? resourcePath : resourcePath + "/";
if (resourcePath.endsWith("/") || resourcePath.isEmpty()) { // Directory
Page<Blob> blob = storage.list(gcsStorage.getBucketName(), Storage.BlobListOption.prefix(resourcePath), Storage.BlobListOption.currentDirectory());
resourceBuilder.setDirectory(processDirectory(resourcePath, blob));
} else {
try {
Blob blob = storage.get(gcsStorage.getBucketName(), resourcePath, Storage.BlobGetOption.fields(Storage.BlobField.values()));
if (blob != null) {
FileMetadata.Builder fileBuilder = FileMetadata.newBuilder();
fileBuilder.setFriendlyName(blob.getName());
fileBuilder.setResourcePath(resourcePath);
fileBuilder.setCreatedTime(blob.getCreateTimeOffsetDateTime().getLong(ChronoField.INSTANT_SECONDS));
fileBuilder.setUpdateTime(blob.getUpdateTimeOffsetDateTime().getLong(ChronoField.INSTANT_SECONDS));
fileBuilder.setResourceSize(blob.getSize());
fileBuilder.setMd5Sum(blob.getMd5());
resourceBuilder.setFile(fileBuilder);
} else {
Page<Blob> blobs = storage.list(gcsStorage.getBucketName(), Storage.BlobListOption.prefix(dirPath), Storage.BlobListOption.currentDirectory());
resourceBuilder.setDirectory(processDirectory(resourcePath + "/", blobs));
}
} catch (Exception e) {
resourceBuilder.setError(MetadataFetchError.NOT_FOUND);
}
}
return resourceBuilder.build();
}
}
@Override
public Boolean isAvailable(String resourcePath) throws Exception {
checkInitialized();
PrivateKey privKey = GCSUtil.getPrivateKey(gcsSecret.getPrivateKey());
try (Storage storage = StorageOptions.newBuilder().setCredentials(ServiceAccountCredentials.newBuilder()
.setProjectId(gcsSecret.getProjectId())
.setPrivateKey(privKey)
.setClientEmail(gcsSecret.getClientEmail())
.build()).build().getService()) {
Blob blob = storage.get(gcsStorage.getBucketName(), resourcePath, Storage.BlobGetOption.fields(Storage.BlobField.values()));
if (blob != null) {
return true;
} else {
final String dirPath = resourcePath.endsWith("/") ? resourcePath : resourcePath + "/";
try {
Page<Blob> blobs = storage.list(gcsStorage.getBucketName(), Storage.BlobListOption.currentDirectory(), Storage.BlobListOption.prefix(dirPath));
return true;
} catch (Exception e) {
return false;
}
}
}
}
private DirectoryMetadata.Builder processDirectory(String resourcePath, Page<Blob> objectListing) {
Iterable<Blob> objectSummaries = objectListing.getValues();
Map<String, DirectoryMetadata.Builder> subDirCache = new HashMap<>();
Map<String, List<String>> childTree = new HashMap<>();
childTree.put(resourcePath, new ArrayList<>());
DirectoryMetadata.Builder dirBuilder = DirectoryMetadata.newBuilder();
subDirCache.put(resourcePath, dirBuilder);
for (Blob summary : objectSummaries) {
buildStructureRecursively(resourcePath, summary.getName(), summary, subDirCache, childTree);
}
registerChildren(resourcePath, subDirCache, childTree);
return dirBuilder;
}
private void registerChildren(String parentPath, Map<String, DirectoryMetadata.Builder> directoryStore, Map<String, List<String>> childTree) {
for (String childDir : childTree.get(parentPath)) {
registerChildren(childDir, directoryStore, childTree);
directoryStore.get(parentPath).addDirectories(directoryStore.get(childDir));
}
}
private void buildStructureRecursively(String basePath, String filePath, Blob summary, Map<String, DirectoryMetadata.Builder> directoryStore, Map<String, List<String>> childTree) {
String relativePath = filePath.substring(basePath.length());
if (relativePath.contains("/")) { // A Directory
String[] pathSections = relativePath.split("/");
String thisDirKey = basePath + pathSections[0] + "/";
if (!directoryStore.containsKey(thisDirKey)) {
DirectoryMetadata.Builder subDirBuilder = DirectoryMetadata.newBuilder();
subDirBuilder.setResourcePath(thisDirKey);
subDirBuilder.setFriendlyName(pathSections[0]);
directoryStore.put(thisDirKey, subDirBuilder);
childTree.get(basePath).add(thisDirKey);
childTree.put(thisDirKey, new ArrayList<>());
}
} else if (!basePath.equals(summary.getName())) { // A File
FileMetadata.Builder fileBuilder = FileMetadata.newBuilder();
fileBuilder.setUpdateTime(summary.getUpdateTimeOffsetDateTime().getLong(ChronoField.INSTANT_SECONDS));
fileBuilder.setCreatedTime(summary.getCreateTimeOffsetDateTime().getLong(ChronoField.INSTANT_SECONDS));
fileBuilder.setResourcePath(summary.getName());
fileBuilder.setFriendlyName(new File(summary.getName()).getName());
fileBuilder.setResourceSize(summary.getSize());
directoryStore.get(basePath).addFiles(fileBuilder);
}
}
}