Skip to content

Commit

Permalink
more refactor fun!
Browse files Browse the repository at this point in the history
  • Loading branch information
kevmoo committed Oct 1, 2021
1 parent ae4041f commit c1c66eb
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 33 deletions.
41 changes: 12 additions & 29 deletions functions_framework/lib/src/function_config.dart
Expand Up @@ -17,8 +17,8 @@ import 'dart:io';
import 'package:args/args.dart';

import 'bad_configuration.dart';
import 'helpers.dart';

const defaultPort = 8080;
const defaultFunctionType = FunctionType.http;
const defaultFunctionTarget = 'function';

Expand All @@ -40,39 +40,22 @@ class FunctionConfig {
final String target;

FunctionConfig({
this.port = defaultPort,
this.port = defaultListenPort,
this.functionType = defaultFunctionType,
this.target = defaultFunctionTarget,
});

// Required per spec:
// https://github.com/GoogleCloudPlatform/functions-framework#specification-summary
factory FunctionConfig.fromEnv() {
int port;

if (Platform.environment.containsKey('PORT')) {
try {
port = int.parse(Platform.environment['PORT']!);
} on FormatException catch (e) {
throw BadConfigurationException(
'Bad value for environment variable "PORT" – '
'"${Platform.environment['PORT']}" – ${e.message}.',
);
}
} else {
port = defaultPort;
}

return FunctionConfig(
port: port,
target: Platform.environment[environmentKeyFunctionTarget] ??
defaultFunctionTarget,
functionType: _parseFunctionType(
Platform.environment['FUNCTION_SIGNATURE_TYPE'] ??
_enumValue(FunctionType.http),
),
);
}
factory FunctionConfig.fromEnv() => FunctionConfig(
port: listenPort(),
target: Platform.environment[environmentKeyFunctionTarget] ??
defaultFunctionTarget,
functionType: _parseFunctionType(
Platform.environment['FUNCTION_SIGNATURE_TYPE'] ??
_enumValue(FunctionType.http),
),
);

// Optional per spec:
// https://github.com/GoogleCloudPlatform/functions-framework#specification-summary
Expand Down Expand Up @@ -121,7 +104,7 @@ class FunctionConfig {
);
}
} else {
port = defaults?.port ?? defaultPort;
port = defaults?.port ?? defaultListenPort;
}

final functionTypeOptionValue = options[_functionTypeOpt] as String?;
Expand Down
97 changes: 93 additions & 4 deletions functions_framework/lib/src/helpers.dart
Expand Up @@ -12,19 +12,43 @@
// See the License for the specific language governing permissions and
// limitations under the License.

/// Utilities to consider pulling into a separate package.
library functions_framework.src.helpers;

import 'dart:async';
import 'dart:io';

import 'package:http/http.dart' as http;

import 'bad_configuration.dart';

const defaultListenPort = 8080;

/// Returns the port to listen on from environment variable "PORT" or uses
/// [defaultPort] if none is defined.
///
/// If the "PORT" environment variable is defined, but it cannot be parsed as a
/// an [int], a [BadConfigurationException] is thrown.
///
/// See https://cloud.google.com/run/docs/reference/container-contract#port
int listenPort({int defaultPort = defaultListenPort}) {
if (Platform.environment.containsKey('PORT')) {
try {
return int.parse(Platform.environment['PORT']!);
} on FormatException catch (e) {
throw BadConfigurationException(
'Bad value for environment variable "PORT" – '
'"${Platform.environment['PORT']}" – ${e.message}.',
);
}
} else {
return defaultPort;
}
}

/// Returns a [Future] that completes when the process receives a
/// [ProcessSignal] requesting a shutdown.
///
/// [ProcessSignal.sigint] is listened to on all platforms.
///
/// [ProcessSignal.sigterm] is listened to on all platforms except Windows.
// Copied from https://github.com/dart-lang/samples/blob/091c89c984/server/google_apis/lib/helpers.dart#L44
Future<void> terminateRequestFuture() {
final completer = Completer<bool>.sync();

Expand Down Expand Up @@ -58,3 +82,68 @@ Future<void> terminateRequestFuture() {

return completer.future;
}

/// Returns a [Future] that completes with the
/// [Project ID](https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects)
/// for the current Google Cloud Project.
///
/// First, if an environment variable with a name in
/// [gcpProjectIdEnvironmentVariables] exists, that is returned.
/// (The list is checked in order.) This is useful for local development.
///
/// If no such environment variable exists, then we assume the code is running
/// on Google Cloud and
/// [Project metadata](https://cloud.google.com/compute/docs/metadata/default-metadata-values#project_metadata)
/// is queried for the Project ID.
Future<String> currentProjectId() async {
for (var envKey in gcpProjectIdEnvironmentVariables) {
final value = Platform.environment[envKey];
if (value != null) return value;
}

const host = 'http://metadata.google.internal/';
final url = Uri.parse('$host/computeMetadata/v1/project/project-id');

try {
final response = await http.get(
url,
headers: {'Metadata-Flavor': 'Google'},
);

if (response.statusCode != 200) {
throw HttpException(
'${response.body} (${response.statusCode})',
uri: url,
);
}

return response.body;
} on SocketException {
stderr.writeln(
'''
Could not connect to $host.
If not running on Google Cloud, one of these environment variables must be set
to the target Google Project ID:
${gcpProjectIdEnvironmentVariables.join('\n')}
''',
);
rethrow;
}
}

/// A set of typical environment variables that are likely to represent the
/// current Google Cloud project ID.
///
/// For context, see:
/// * https://cloud.google.com/functions/docs/env-var
/// * https://cloud.google.com/compute/docs/gcloud-compute#default_project
/// * https://github.com/GoogleContainerTools/gcp-auth-webhook/blob/08136ca171fe5713cc70ef822c911fbd3a1707f5/server.go#L38-L44
///
/// Note: these are ordered starting from the most current/canonical to least.
/// (At least as could be determined at the time of writing.)
const gcpProjectIdEnvironmentVariables = {
'GCP_PROJECT',
'GCLOUD_PROJECT',
'CLOUDSDK_CORE_PROJECT',
'GOOGLE_CLOUD_PROJECT',
};

0 comments on commit c1c66eb

Please sign in to comment.