Skip to content

Commit 1a36004

Browse files
committed
region hacking generalization
1 parent 8838d5b commit 1a36004

File tree

6 files changed

+152
-170
lines changed

6 files changed

+152
-170
lines changed

gcp/lib/gcp.dart

+1-6
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,13 @@ export 'src/gcp_project.dart'
2222
gcpProjectIdEnvironmentVariables,
2323
projectIdFromEnvironment,
2424
projectIdFromMetadataServer;
25-
export 'src/gpc_region.dart'
26-
show
27-
computeRegion,
28-
gcpRegionEnvironmentVariables,
29-
regionFromEnvironment,
30-
regionFromMetadataServer;
3125
export 'src/log_severity.dart' show LogSeverity, RequestLogger;
3226
export 'src/logging.dart'
3327
show
3428
badRequestMiddleware,
3529
cloudLoggingMiddleware,
3630
createLoggingMiddleware,
3731
currentLogger;
32+
export 'src/metadata.dart' show MetadataValue;
3833
export 'src/serve.dart' show listenPort, serveHandler;
3934
export 'src/terminate.dart' show waitForTerminate;

gcp/lib/src/gcp_project.dart

+8-57
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,16 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import 'dart:io';
16-
17-
import 'package:http/http.dart' as http;
18-
1915
import 'bad_configuration_exception.dart';
16+
import 'metadata.dart';
2017

2118
/// A convenience wrapper that first tries [projectIdFromEnvironment]
2219
/// then (if the value is `null`) tries [projectIdFromMetadataServer]
2320
///
2421
/// Like [projectIdFromMetadataServer], if no value is found, a
2522
/// [BadConfigurationException] is thrown.
26-
Future<String> computeProjectId() async {
27-
final localValue = projectIdFromEnvironment();
28-
if (localValue != null) {
29-
return localValue;
30-
}
31-
final result = await projectIdFromMetadataServer();
32-
33-
return result;
34-
}
23+
Future<String> computeProjectId() =>
24+
MetadataValue.project.fromEnvironmentOrMetadata();
3525

3626
/// Returns the
3727
/// [Project ID](https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects)
@@ -41,14 +31,7 @@ Future<String> computeProjectId() async {
4131
/// The list is checked in order. This is useful for local development.
4232
///
4333
/// If no matching variable is found, `null` is returned.
44-
String? projectIdFromEnvironment() {
45-
for (var envKey in gcpProjectIdEnvironmentVariables) {
46-
final value = Platform.environment[envKey];
47-
if (value != null) return value;
48-
}
49-
50-
return null;
51-
}
34+
String? projectIdFromEnvironment() => MetadataValue.project.fromEnvironment();
5235

5336
/// Returns a [Future] that completes with the
5437
/// [Project ID](https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects)
@@ -57,36 +40,8 @@ String? projectIdFromEnvironment() {
5740
///
5841
/// If the metadata server cannot be contacted, a [BadConfigurationException] is
5942
/// thrown.
60-
Future<String> projectIdFromMetadataServer() async {
61-
const host = 'http://metadata.google.internal/';
62-
final url = Uri.parse('$host/computeMetadata/v1/project/project-id');
63-
64-
try {
65-
final response = await http.get(
66-
url,
67-
headers: {'Metadata-Flavor': 'Google'},
68-
);
69-
70-
if (response.statusCode != 200) {
71-
throw HttpException(
72-
'${response.body} (${response.statusCode})',
73-
uri: url,
74-
);
75-
}
76-
77-
return response.body;
78-
} on SocketException catch (e) {
79-
throw BadConfigurationException(
80-
'''
81-
Could not connect to $host.
82-
If not running on Google Cloud, one of these environment variables must be set
83-
to the target Google Project ID:
84-
${gcpProjectIdEnvironmentVariables.join('\n')}
85-
''',
86-
details: e.toString(),
87-
);
88-
}
89-
}
43+
Future<String> projectIdFromMetadataServer() =>
44+
MetadataValue.project.fromMetadataServer();
9045

9146
/// A set of typical environment variables that are likely to represent the
9247
/// current Google Cloud project ID.
@@ -98,9 +53,5 @@ ${gcpProjectIdEnvironmentVariables.join('\n')}
9853
///
9954
/// Note: these are ordered starting from the most current/canonical to least.
10055
/// (At least as could be determined at the time of writing.)
101-
const gcpProjectIdEnvironmentVariables = {
102-
'GCP_PROJECT',
103-
'GCLOUD_PROJECT',
104-
'CLOUDSDK_CORE_PROJECT',
105-
'GOOGLE_CLOUD_PROJECT',
106-
};
56+
Set<String> get gcpProjectIdEnvironmentVariables =>
57+
MetadataValue.project.environmentValues;

gcp/lib/src/gpc_region.dart

-104
This file was deleted.

gcp/lib/src/metadata.dart

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import 'dart:io';
16+
17+
import 'package:http/http.dart' as http;
18+
19+
import 'bad_configuration_exception.dart';
20+
21+
enum MetadataValue {
22+
/// A set of typical environment variables that are likely to represent the
23+
/// current Google Cloud instance region.
24+
///
25+
/// For context, see:
26+
/// * https://cloud.google.com/functions/docs/env-var
27+
/// * https://cloud.google.com/compute/docs/gcloud-compute#default_project
28+
/// * https://github.com/GoogleContainerTools/gcp-auth-webhook/blob/08136ca171fe5713cc70ef822c911fbd3a1707f5/server.go#L38-L44
29+
///
30+
/// Note: these are ordered starting from the most current/canonical to least.
31+
/// (At least as could be determined at the time of writing.)
32+
project(
33+
path: 'project/project-id',
34+
environmentValues: {
35+
'GCP_PROJECT',
36+
'GCLOUD_PROJECT',
37+
'CLOUDSDK_CORE_PROJECT',
38+
'GOOGLE_CLOUD_PROJECT',
39+
},
40+
),
41+
42+
/// A set of typical environment variables that are likely to represent the
43+
/// current Google Cloud instance region.
44+
///
45+
/// For context, see:
46+
/// * https://cloud.google.com/functions/docs/env-var
47+
/// * https://cloud.google.com/compute/docs/gcloud-compute#default_project
48+
/// * https://github.com/GoogleContainerTools/gcp-auth-webhook/blob/08136ca171fe5713cc70ef822c911fbd3a1707f5/server.go#L38-L44
49+
///
50+
/// Note: these are ordered starting from the most current/canonical to least.
51+
/// (At least as could be determined at the time of writing.)
52+
region(
53+
path: 'instance/region',
54+
environmentValues: {
55+
'FUNCTION_REGION',
56+
'CLOUDSDK_COMPUTE_REGION',
57+
},
58+
);
59+
60+
const MetadataValue({
61+
required this.path,
62+
required this.environmentValues,
63+
});
64+
65+
final String path;
66+
67+
final Set<String> environmentValues;
68+
69+
/// A convenience wrapper that first tries [fromEnvironment]
70+
/// then (if the value is `null`) tries [fromMetadataServer]
71+
///
72+
/// Like [fromMetadataServer], if no value is found, a
73+
/// [BadConfigurationException] is thrown.
74+
Future<String> fromEnvironmentOrMetadata() async {
75+
final localValue = fromEnvironment();
76+
if (localValue != null) {
77+
return localValue;
78+
}
79+
final result = await fromMetadataServer();
80+
81+
return result;
82+
}
83+
84+
/// Returns the
85+
/// [Region](https://cloud.google.com/compute/docs/regions-zones#identifying_a_region_or_zone)
86+
/// for the current instance by checking the environment variables in
87+
/// [environmentValues].
88+
///
89+
/// The list is checked in order. This is useful for local development.
90+
///
91+
/// If no matching variable is found, `null` is returned.
92+
String? fromEnvironment() {
93+
for (var envKey in environmentValues) {
94+
final value = Platform.environment[envKey];
95+
if (value != null) return value;
96+
}
97+
98+
return null;
99+
}
100+
101+
/// Returns a [Future] that completes with the
102+
/// [Region](https://cloud.google.com/compute/docs/regions-zones#identifying_a_region_or_zone)
103+
/// for the current instance by checking
104+
/// [instance metadata](https://cloud.google.com/compute/docs/metadata/default-metadata-values#vm_instance_metadata).
105+
///
106+
/// If the metadata server cannot be contacted, a [BadConfigurationException]
107+
/// is thrown.
108+
Future<String> fromMetadataServer() async {
109+
const host = 'http://metadata.google.internal/';
110+
final url = Uri.parse('$host/computeMetadata/v1/$path');
111+
112+
try {
113+
final response = await http.get(
114+
url,
115+
headers: {'Metadata-Flavor': 'Google'},
116+
);
117+
118+
if (response.statusCode != 200) {
119+
throw HttpException(
120+
'${response.body} (${response.statusCode})',
121+
uri: url,
122+
);
123+
}
124+
125+
return response.body;
126+
} on SocketException catch (e) {
127+
throw BadConfigurationException(
128+
'''
129+
Could not connect to $host.
130+
If not running on Google Cloud, one of these environment variables must be set
131+
to the target region:
132+
${environmentValues.join('\n')}
133+
''',
134+
details: e.toString(),
135+
);
136+
}
137+
}
138+
}

gcp/test/gcp_test.dart

+4-2
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ void main() {
142142

143143
await expectLater(
144144
errorOut,
145-
containsAll(gcpRegionEnvironmentVariables),
145+
containsAll(MetadataValue.region.environmentValues),
146146
);
147147
await expectLater(proc.stdout, emitsDone);
148148

@@ -152,7 +152,9 @@ void main() {
152152
test('environment set', () async {
153153
final proc = await _run(
154154
regionPrint,
155-
environment: {gcpRegionEnvironmentVariables.first: 'us-central1'},
155+
environment: {
156+
MetadataValue.region.environmentValues.first: 'us-central1',
157+
},
156158
);
157159

158160
await expectLater(proc.stdout, emits('us-central1'));

gcp/test/src/region_print.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
import 'package:gcp/gcp.dart';
1515

1616
Future<void> main() async {
17-
print(await computeRegion());
17+
print(await MetadataValue.region.fromEnvironmentOrMetadata());
1818
}

0 commit comments

Comments
 (0)