package io.anemos.metastore.provider; import com.google.cloud.ServiceOptions; import com.google.cloud.storage.BlobId; import com.google.cloud.storage.BlobInfo; import com.google.cloud.storage.Storage; import com.google.cloud.storage.StorageOptions; import com.google.protobuf.ByteString; import com.google.protobuf.Descriptors; import io.opencensus.common.Scope; import io.opencensus.trace.Tracer; import io.opencensus.trace.Tracing; import java.io.IOException; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class GoogleCloudStorage implements StorageProvider, BindProvider { private static final Logger LOG = LoggerFactory.getLogger(GoogleCloudStorage.class); private static final Tracer TRACER = Tracing.getTracer(); private Storage storage; private String bucket; private String project; private String fileName; private BindDatabase bindDatabase; public void init(RegistryInfo registryInfo, Map<String, String> config, String extension) { this.storage = StorageOptions.getDefaultInstance().getService(); String project = ServiceOptions.getDefaultProjectId(); if (config.get("project") == null && project == null) { throw new RuntimeException("project variable not set"); } if (config.get("bucket") == null) { throw new RuntimeException("bucket variable not set"); } if (config.get("path") == null) { throw new RuntimeException("path variable not set"); } if (config.get("project") != null) { this.project = config.get("project"); } this.bucket = config.get("bucket"); if (config.get("path").endsWith("/")) { this.fileName = config.get("path") + registryInfo.getName() + "." + extension; } else { this.fileName = config.get("path") + "/" + registryInfo.getName() + "." + extension; } } @Override public void initForStorage( RegistryInfo registryInfo, Map<String, String> config, boolean writeOnly) { init(registryInfo, config, "pb"); } @Override public void initForBind( RegistryInfo registryInfo, Map<String, String> config, boolean writeOnly) { bindDatabase = new BindDatabase(); init(registryInfo, config, "bind"); try { loadBind(); } catch (IOException e) { e.printStackTrace(); } } @Override public ByteString read() { BlobId blobId = BlobId.of(bucket, fileName); if (storage.get(blobId) != null && storage.get(blobId).exists()) { byte[] buffer = storage.readAllBytes(blobId, Storage.BlobSourceOption.userProject(project)); return ByteString.copyFrom(buffer); } else { return null; } } @Override public void write(ByteString payload) { try (Scope scope = TRACER.spanBuilder("GoogleCloudStorage.write").setRecordEvents(true).startScopedSpan()) { storage.create(BlobInfo.newBuilder(bucket, fileName).build(), payload.toByteArray()); } } @Override public void createResourceBinding(String resourceUrn, Descriptors.Descriptor descriptor) { try (Scope scope = TRACER .spanBuilder("GoogleCloudStorage.createResourceBinding") .setRecordEvents(true) .startScopedSpan()) { bindDatabase.bindMessage(resourceUrn, descriptor.getFullName()); saveBind(); } } @Override public void updateResourceBinding(String resourceUrn, Descriptors.Descriptor descriptor) { try (Scope scope = TRACER .spanBuilder("GoogleCloudStorage.updateResourceBinding") .setRecordEvents(true) .startScopedSpan()) { bindDatabase.bindMessage(resourceUrn, descriptor.getFullName()); saveBind(); } } @Override public void createServiceBinding(String resourceUrn, Descriptors.ServiceDescriptor descriptor) { try (Scope scope = TRACER .spanBuilder("GoogleCloudStorage.createServiceBinding") .setRecordEvents(true) .startScopedSpan()) { bindDatabase.bindService(resourceUrn, descriptor.getFullName()); saveBind(); } } @Override public void updateServiceBinding(String resourceUrn, Descriptors.ServiceDescriptor descriptor) { try (Scope scope = TRACER .spanBuilder("GoogleCloudStorage.updateServiceBinding") .setRecordEvents(true) .startScopedSpan()) { bindDatabase.bindService(resourceUrn, descriptor.getFullName()); saveBind(); } } @Override public void deleteResourceBinding(String resourceUrn) { try (Scope scope = TRACER .spanBuilder("GoogleCloudStorage.deleteResourceBinding") .setRecordEvents(true) .startScopedSpan()) { bindDatabase.remove(resourceUrn); saveBind(); } } @Override public List<BindResult> listResourceBindings(String next_page_token) { try (Scope scope = TRACER .spanBuilder("GoogleCloudStorage.listResourceBindings") .setRecordEvents(true) .startScopedSpan()) { return bindDatabase.list(next_page_token); } } @Override public boolean isWriteOnly() { return false; } @Override public BindResult getResourceBinding(String resourceUrn) { try (Scope scope = TRACER .spanBuilder("GoogleCloudStorage.getResourceBinding") .setRecordEvents(true) .startScopedSpan()) { return bindDatabase.get(resourceUrn); } } private void saveBind() { storage.create(BlobInfo.newBuilder(bucket, fileName).build(), bindDatabase.toByteArray()); } private void loadBind() throws IOException { BlobId blobId = BlobId.of(bucket, fileName); if (storage.get(blobId) != null && storage.get(blobId).exists()) { byte[] buffer = storage.readAllBytes(blobId, Storage.BlobSourceOption.userProject(project)); bindDatabase.parse(buffer); } } }