Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add nio entry to user-agent #774

Merged
merged 9 commits into from Dec 21, 2021
Expand Up @@ -21,6 +21,7 @@
import static com.google.common.base.Strings.isNullOrEmpty;

import com.google.api.gax.paging.Page;
import com.google.api.gax.rpc.FixedHeaderProvider;
import com.google.cloud.storage.Acl;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.BlobId;
Expand All @@ -35,6 +36,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.UrlEscapers;
import com.google.common.primitives.Ints;
import java.io.BufferedInputStream;
Expand Down Expand Up @@ -70,6 +72,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
Expand All @@ -86,6 +89,7 @@
@Singleton
@ThreadSafe
public final class CloudStorageFileSystemProvider extends FileSystemProvider {
static final String VERSION = getVersion();

private Storage storage;
private final StorageOptions storageOptions;
Expand Down Expand Up @@ -205,13 +209,8 @@ public CloudStorageFileSystemProvider() {
// Initialize this.storage, once. This may throw an exception if default authentication
// credentials are not available (hence not doing it in the ctor).
private void initStorage() {
if (this.storage != null) {
return;
}
if (storageOptions == null) {
this.storage = StorageOptions.getDefaultInstance().getService();
} else {
this.storage = storageOptions.getService();
if (this.storage == null) {
doInitStorage();
}
}

Expand Down Expand Up @@ -1037,4 +1036,53 @@ private IOException asIoException(StorageException oops) {
}
return new IOException(oops.getMessage(), oops);
}

/**
* Resolve the version of google-cloud-nio for inclusion in request meta-data
*/
private static String getVersion() {
// attempt to read the library's version from a properties file generated during the build
// this value should be read and cached for later use
String version = "";
try (InputStream inputStream = CloudStorageFileSystemProvider.class.getResourceAsStream("/META-INF/maven/com.google.cloud/google-cloud-nio/pom.properties")) {
if (inputStream != null) {
final Properties properties = new Properties();
properties.load(inputStream);
version = properties.getProperty("version");
}
} catch (IOException e) {
// ignore
}
return version;
}

@VisibleForTesting
void doInitStorage() {
final StorageOptions so = storageOptions != null ? storageOptions : StorageOptions.getDefaultInstance();
final StorageOptions.Builder builder = so.toBuilder();
this.storage = builder
.setHeaderProvider(getHeaderProvider())
.build()
.getService();
}

@VisibleForTesting
NioUserAgentEntryHeaderProvider getHeaderProvider() {
return new NioUserAgentEntryHeaderProvider();
}

@VisibleForTesting
private static class NioUserAgentEntryHeaderProvider extends FixedHeaderProvider {

private final ImmutableMap<String, String> userAgent = ImmutableMap
.of("user-agent", String.format("nio/%s", VERSION));
BenWhitehead marked this conversation as resolved.
Show resolved Hide resolved

@Nullable
@Override
public Map<String, String> getHeaders() {
return userAgent;
}

}

}
Expand Up @@ -26,6 +26,7 @@
import static java.nio.file.StandardOpenOption.CREATE_NEW;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static java.nio.file.StandardOpenOption.WRITE;
import static org.junit.Assert.assertTrue;

import com.google.cloud.storage.contrib.nio.testing.LocalStorageHelper;
import com.google.cloud.testing.junit4.MultipleAttemptsRule;
Expand Down Expand Up @@ -795,6 +796,22 @@ public void testFromSpace() throws Exception {
assertThat(path4.toString()).isEqualTo("/with/a%20percent");
}

@Test
public void testVersion_matchesAcceptablePatterns() {
String acceptableVersionPattern =
"|(?:\\d+\\.\\d+\\.\\d+(?:-.*?)?(?:-SNAPSHOT)?)";
String version = CloudStorageFileSystemProvider.VERSION;
assertTrue(
String.format("the loaded version '%s' did not match the acceptable pattern", version),
version.matches(acceptableVersionPattern)
);
}

@Test
public void getUserAgentStartsWithCorrectToken() {
assertThat(String.format("nio/%s", CloudStorageFileSystemProvider.VERSION)).startsWith("nio/");
BenWhitehead marked this conversation as resolved.
Show resolved Hide resolved
}

private static CloudStorageConfiguration permitEmptyPathComponents(boolean value) {
return CloudStorageConfiguration.builder().permitEmptyPathComponents(value).build();
}
Expand Down
Expand Up @@ -16,56 +16,49 @@

package com.google.cloud.storage.contrib.nio;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageOptions;
import com.google.cloud.testing.junit4.MultipleAttemptsRule;
import java.net.URI;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;

/** Unit tests for {@link CloudStorageFileSystemProvider} late initialization. */
@RunWith(JUnit4.class)
@RunWith(MockitoJUnitRunner.class)
public class CloudStorageLateInitializationTest {
@Rule public final MultipleAttemptsRule multipleAttemptsRule = new MultipleAttemptsRule(3);

private StorageOptions mockOptions;

@Before
public void before() {
mockOptions = mock(StorageOptions.class);
Storage mockStorage = mock(Storage.class);
when(mockOptions.getService()).thenReturn(mockStorage);
CloudStorageFileSystemProvider.setStorageOptions(mockOptions);
}
@Spy
private final CloudStorageFileSystemProvider provider = new CloudStorageFileSystemProvider();

@Test
public void ctorDoesNotCreateStorage() {
new CloudStorageFileSystemProvider();
verify(mockOptions, never()).getService();
verify(provider, never()).doInitStorage();
}

@Test
public void getPathCreatesStorageOnce() {
CloudStorageFileSystemProvider provider = new CloudStorageFileSystemProvider();
provider.getPath(URI.create("gs://bucket1/wat"));
provider.getPath(URI.create("gs://bucket2/wat"));
verify(mockOptions, times(1)).getService();
verify(provider, times(1)).doInitStorage();
}

@Test
public void getFileSystemCreatesStorageOnce() {
CloudStorageFileSystemProvider provider = new CloudStorageFileSystemProvider();
provider.getFileSystem(URI.create("gs://bucket1"));
provider.getFileSystem(URI.create("gs://bucket2"));
verify(mockOptions, times(1)).getService();
verify(provider, times(1)).doInitStorage();
}

@Test
public void headerProviderRegistered() {
provider.getProject();
verify(provider, times(1)).doInitStorage();
verify(provider, times(1)).getHeaderProvider();
}
}
@@ -0,0 +1 @@
mock-maker-inline