33
33
package org .opensearch .repositories .gcs ;
34
34
35
35
import com .google .auth .Credentials ;
36
+ import com .google .auth .oauth2 .GoogleCredentials ;
36
37
import com .google .cloud .http .HttpTransportOptions ;
37
38
import com .google .cloud .storage .Storage ;
38
39
39
40
import org .opensearch .common .bytes .BytesReference ;
41
+ import com .google .cloud .storage .StorageOptions ;
40
42
import org .opensearch .common .settings .MockSecureSettings ;
41
43
import org .opensearch .common .settings .Setting ;
42
44
import org .opensearch .common .settings .Settings ;
43
45
import org .opensearch .common .unit .TimeValue ;
44
46
import org .opensearch .common .xcontent .XContentBuilder ;
45
47
import org .opensearch .test .OpenSearchTestCase ;
48
+ import org .hamcrest .MatcherAssert ;
46
49
import org .hamcrest .Matchers ;
47
50
51
+ import java .io .IOException ;
52
+ import java .net .Proxy ;
53
+ import java .net .URI ;
54
+ import java .net .URISyntaxException ;
48
55
import java .security .KeyPair ;
49
56
import java .security .KeyPairGenerator ;
50
57
import java .util .Base64 ;
51
58
import java .util .Locale ;
52
59
import java .util .UUID ;
53
60
61
+ import org .mockito .Mockito ;
62
+
54
63
import static org .opensearch .common .xcontent .XContentFactory .jsonBuilder ;
55
64
import static org .hamcrest .Matchers .equalTo ;
56
65
import static org .hamcrest .Matchers .containsString ;
57
66
58
67
public class GoogleCloudStorageServiceTests extends OpenSearchTestCase {
59
68
69
+ final TimeValue connectTimeValue = TimeValue .timeValueNanos (randomIntBetween (0 , 2000000 ));
70
+ final TimeValue readTimeValue = TimeValue .timeValueNanos (randomIntBetween (0 , 2000000 ));
71
+ final String applicationName = randomAlphaOfLength (randomIntBetween (1 , 10 )).toLowerCase (Locale .ROOT );
72
+ final String endpoint = randomFrom ("http://" , "https://" )
73
+ + randomFrom ("www.opensearch.org" , "www.googleapis.com" , "localhost/api" , "google.com/oauth" )
74
+ + ":"
75
+ + randomIntBetween (1 , 65535 );
76
+ final String projectIdName = randomAlphaOfLength (randomIntBetween (1 , 10 )).toLowerCase (Locale .ROOT );
77
+
60
78
public void testClientInitializer () throws Exception {
61
79
final String clientName = randomAlphaOfLength (randomIntBetween (1 , 10 )).toLowerCase (Locale .ROOT );
62
- final TimeValue connectTimeValue = TimeValue .timeValueNanos (randomIntBetween (0 , 2000000 ));
63
- final TimeValue readTimeValue = TimeValue .timeValueNanos (randomIntBetween (0 , 2000000 ));
64
- final String applicationName = randomAlphaOfLength (randomIntBetween (1 , 10 )).toLowerCase (Locale .ROOT );
65
- final String endpoint = randomFrom ("http://" , "https://" )
66
- + randomFrom ("www.opensearch.org" , "www.googleapis.com" , "localhost/api" , "google.com/oauth" )
67
- + ":"
68
- + randomIntBetween (1 , 65535 );
69
- final String projectIdName = randomAlphaOfLength (randomIntBetween (1 , 10 )).toLowerCase (Locale .ROOT );
70
80
final Settings settings = Settings .builder ()
71
81
.put (
72
82
GoogleCloudStorageClientSettings .CONNECT_TIMEOUT_SETTING .getConcreteSettingForNamespace (clientName ).getKey (),
@@ -83,31 +93,35 @@ public void testClientInitializer() throws Exception {
83
93
.put (GoogleCloudStorageClientSettings .ENDPOINT_SETTING .getConcreteSettingForNamespace (clientName ).getKey (), endpoint )
84
94
.put (GoogleCloudStorageClientSettings .PROJECT_ID_SETTING .getConcreteSettingForNamespace (clientName ).getKey (), projectIdName )
85
95
.build ();
86
- final GoogleCloudStorageService service = new GoogleCloudStorageService ();
96
+ GoogleCredentials mockGoogleCredentials = Mockito .mock (GoogleCredentials .class );
97
+ GoogleApplicationDefaultCredentials mockDefaultCredentials = Mockito .mock (GoogleApplicationDefaultCredentials .class );
98
+ Mockito .when (mockDefaultCredentials .get ()).thenReturn (mockGoogleCredentials );
99
+
100
+ final GoogleCloudStorageService service = new GoogleCloudStorageService (mockDefaultCredentials );
87
101
service .refreshAndClearCache (GoogleCloudStorageClientSettings .load (settings ));
88
102
GoogleCloudStorageOperationsStats statsCollector = new GoogleCloudStorageOperationsStats ("bucket" );
89
103
final IllegalArgumentException e = expectThrows (
90
104
IllegalArgumentException .class ,
91
105
() -> service .client ("another_client" , "repo" , statsCollector )
92
106
);
93
- assertThat (e .getMessage (), Matchers .startsWith ("Unknown client name" ));
107
+ MatcherAssert . assertThat (e .getMessage (), Matchers .startsWith ("Unknown client name" ));
94
108
assertSettingDeprecationsAndWarnings (
95
109
new Setting <?>[] { GoogleCloudStorageClientSettings .APPLICATION_NAME_SETTING .getConcreteSettingForNamespace (clientName ) }
96
110
);
97
111
final Storage storage = service .client (clientName , "repo" , statsCollector );
98
- assertThat (storage .getOptions ().getApplicationName (), Matchers .containsString (applicationName ));
99
- assertThat (storage .getOptions ().getHost (), Matchers .is (endpoint ));
100
- assertThat (storage .getOptions ().getProjectId (), Matchers .is (projectIdName ));
101
- assertThat (storage .getOptions ().getTransportOptions (), Matchers .instanceOf (HttpTransportOptions .class ));
102
- assertThat (
112
+ MatcherAssert . assertThat (storage .getOptions ().getApplicationName (), Matchers .containsString (applicationName ));
113
+ MatcherAssert . assertThat (storage .getOptions ().getHost (), Matchers .is (endpoint ));
114
+ MatcherAssert . assertThat (storage .getOptions ().getProjectId (), Matchers .is (projectIdName ));
115
+ MatcherAssert . assertThat (storage .getOptions ().getTransportOptions (), Matchers .instanceOf (HttpTransportOptions .class ));
116
+ MatcherAssert . assertThat (
103
117
((HttpTransportOptions ) storage .getOptions ().getTransportOptions ()).getConnectTimeout (),
104
118
Matchers .is ((int ) connectTimeValue .millis ())
105
119
);
106
- assertThat (
120
+ MatcherAssert . assertThat (
107
121
((HttpTransportOptions ) storage .getOptions ().getTransportOptions ()).getReadTimeout (),
108
122
Matchers .is ((int ) readTimeValue .millis ())
109
123
);
110
- assertThat (storage .getOptions ().getCredentials (), Matchers .nullValue (Credentials .class ));
124
+ MatcherAssert . assertThat (storage .getOptions ().getCredentials (), Matchers .instanceOf (Credentials .class ));
111
125
}
112
126
113
127
public void testReinitClientSettings () throws Exception {
@@ -123,33 +137,33 @@ public void testReinitClientSettings() throws Exception {
123
137
final GoogleCloudStorageService storageService = plugin .storageService ;
124
138
GoogleCloudStorageOperationsStats statsCollector = new GoogleCloudStorageOperationsStats ("bucket" );
125
139
final Storage client11 = storageService .client ("gcs1" , "repo1" , statsCollector );
126
- assertThat (client11 .getOptions ().getProjectId (), equalTo ("project_gcs11" ));
140
+ MatcherAssert . assertThat (client11 .getOptions ().getProjectId (), equalTo ("project_gcs11" ));
127
141
final Storage client12 = storageService .client ("gcs2" , "repo2" , statsCollector );
128
- assertThat (client12 .getOptions ().getProjectId (), equalTo ("project_gcs12" ));
142
+ MatcherAssert . assertThat (client12 .getOptions ().getProjectId (), equalTo ("project_gcs12" ));
129
143
// client 3 is missing
130
144
final IllegalArgumentException e1 = expectThrows (
131
145
IllegalArgumentException .class ,
132
146
() -> storageService .client ("gcs3" , "repo3" , statsCollector )
133
147
);
134
- assertThat (e1 .getMessage (), containsString ("Unknown client name [gcs3]." ));
148
+ MatcherAssert . assertThat (e1 .getMessage (), containsString ("Unknown client name [gcs3]." ));
135
149
// update client settings
136
150
plugin .reload (settings2 );
137
151
// old client 1 not changed
138
- assertThat (client11 .getOptions ().getProjectId (), equalTo ("project_gcs11" ));
152
+ MatcherAssert . assertThat (client11 .getOptions ().getProjectId (), equalTo ("project_gcs11" ));
139
153
// new client 1 is changed
140
154
final Storage client21 = storageService .client ("gcs1" , "repo1" , statsCollector );
141
- assertThat (client21 .getOptions ().getProjectId (), equalTo ("project_gcs21" ));
155
+ MatcherAssert . assertThat (client21 .getOptions ().getProjectId (), equalTo ("project_gcs21" ));
142
156
// old client 2 not changed
143
- assertThat (client12 .getOptions ().getProjectId (), equalTo ("project_gcs12" ));
157
+ MatcherAssert . assertThat (client12 .getOptions ().getProjectId (), equalTo ("project_gcs12" ));
144
158
// new client2 is gone
145
159
final IllegalArgumentException e2 = expectThrows (
146
160
IllegalArgumentException .class ,
147
161
() -> storageService .client ("gcs2" , "repo2" , statsCollector )
148
162
);
149
- assertThat (e2 .getMessage (), containsString ("Unknown client name [gcs2]." ));
163
+ MatcherAssert . assertThat (e2 .getMessage (), containsString ("Unknown client name [gcs2]." ));
150
164
// client 3 emerged
151
165
final Storage client23 = storageService .client ("gcs3" , "repo3" , statsCollector );
152
- assertThat (client23 .getOptions ().getProjectId (), equalTo ("project_gcs23" ));
166
+ MatcherAssert . assertThat (client23 .getOptions ().getProjectId (), equalTo ("project_gcs23" ));
153
167
}
154
168
}
155
169
@@ -194,4 +208,72 @@ public void testToTimeout() {
194
208
assertEquals (-1 , GoogleCloudStorageService .toTimeout (TimeValue .ZERO ).intValue ());
195
209
assertEquals (0 , GoogleCloudStorageService .toTimeout (TimeValue .MINUS_ONE ).intValue ());
196
210
}
211
+
212
+ /**
213
+ * The following method test the Google Application Default Credential instead of
214
+ * using service account file.
215
+ * Considered use of JUnit Mocking due to static method GoogleCredentials.getApplicationDefault
216
+ * and avoiding environment variables to set which later use GCE.
217
+ * @throws Exception
218
+ */
219
+ public void testApplicationDefaultCredential () throws Exception {
220
+ GoogleCloudStorageClientSettings settings = getGCSClientSettingsWithoutCredentials ();
221
+ GoogleCredentials mockGoogleCredentials = Mockito .mock (GoogleCredentials .class );
222
+ HttpTransportOptions mockHttpTransportOptions = Mockito .mock (HttpTransportOptions .class );
223
+ GoogleApplicationDefaultCredentials mockDefaultCredentials = Mockito .mock (GoogleApplicationDefaultCredentials .class );
224
+ Mockito .when (mockDefaultCredentials .get ()).thenReturn (mockGoogleCredentials );
225
+
226
+ GoogleCloudStorageService service = new GoogleCloudStorageService (mockDefaultCredentials );
227
+ StorageOptions storageOptions = service .createStorageOptions (settings , mockHttpTransportOptions );
228
+ assertNotNull (storageOptions );
229
+ assertEquals (storageOptions .getCredentials ().toString (), mockGoogleCredentials .toString ());
230
+ }
231
+
232
+ /**
233
+ * The application default credential throws exception when there are
234
+ * no Environment Variables provided or Google Compute Engine is not running
235
+ * @throws Exception
236
+ */
237
+ public void testApplicationDefaultCredentialsWhenNoSettingProvided () throws Exception {
238
+ GoogleCloudStorageClientSettings settings = getGCSClientSettingsWithoutCredentials ();
239
+ HttpTransportOptions mockHttpTransportOptions = Mockito .mock (HttpTransportOptions .class );
240
+ GoogleCloudStorageService service = new GoogleCloudStorageService ();
241
+ StorageOptions storageOptions = service .createStorageOptions (settings , mockHttpTransportOptions );
242
+
243
+ Exception exception = assertThrows (IOException .class , GoogleCredentials ::getApplicationDefault );
244
+ assertNotNull (storageOptions );
245
+ assertNull (storageOptions .getCredentials ());
246
+ MatcherAssert .assertThat (exception .getMessage (), containsString ("The Application Default Credentials are not available" ));
247
+ }
248
+
249
+ /**
250
+ * The application default credential throws IOException when it is
251
+ * used without GoogleCloudStorageService
252
+ */
253
+ public void testDefaultCredentialsThrowsExceptionWithoutGCStorageService () {
254
+ GoogleApplicationDefaultCredentials googleApplicationDefaultCredentials = new GoogleApplicationDefaultCredentials ();
255
+ GoogleCredentials credentials = googleApplicationDefaultCredentials .get ();
256
+ assertNull (credentials );
257
+ Exception exception = assertThrows (IOException .class , GoogleCredentials ::getApplicationDefault );
258
+ MatcherAssert .assertThat (exception .getMessage (), containsString ("The Application Default Credentials are not available" ));
259
+ }
260
+
261
+ /**
262
+ * This is a helper method to provide GCS Client settings without credentials
263
+ * @return GoogleCloudStorageClientSettings
264
+ * @throws URISyntaxException
265
+ */
266
+ private GoogleCloudStorageClientSettings getGCSClientSettingsWithoutCredentials () throws URISyntaxException {
267
+ return new GoogleCloudStorageClientSettings (
268
+ null ,
269
+ endpoint ,
270
+ projectIdName ,
271
+ connectTimeValue ,
272
+ readTimeValue ,
273
+ applicationName ,
274
+ new URI ("" ),
275
+ new ProxySettings (Proxy .Type .DIRECT , null , 0 , null , null )
276
+ );
277
+ }
278
+
197
279
}
0 commit comments