"use strict";
/*!
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs");
const gcpMetadata = require("gcp-metadata");
const google_auth_library_1 = require("google-auth-library");
const util_1 = require("util");
const readFile = util_1.promisify(fs.readFile);
function zoneFromQualifiedZone(qualified) {
// Some parsing is necessary. Metadata service returns a fully
// qualified zone name: 'projects/{projectId}/zones/{zone}'. Logging
// wants just the zone part.
//
return qualified.split('/').pop();
}
/**
* Create a descriptor for Cloud Functions.
*
* @returns {object}
*/
function getCloudFunctionDescriptor() {
/**
* In GCF versions after Node 8, K_SERVICE is the preferred way to
* get the function name and GOOGLE_CLOUD_REGION is the preferred way
* to get the region.
*/
return {
type: 'cloud_function',
labels: {
function_name: process.env.K_SERVICE || process.env.FUNCTION_NAME,
region: process.env.GOOGLE_CLOUD_REGION || process.env.FUNCTION_REGION,
},
};
}
exports.getCloudFunctionDescriptor = getCloudFunctionDescriptor;
/**
* Create a descriptor for Google App Engine.
*
* @returns {object}
*/
async function getGAEDescriptor() {
const qualifiedZone = await gcpMetadata.instance('zone');
const zone = zoneFromQualifiedZone(qualifiedZone);
return {
type: 'gae_app',
labels: {
module_id: process.env.GAE_SERVICE || process.env.GAE_MODULE_NAME,
version_id: process.env.GAE_VERSION,
zone,
},
};
}
exports.getGAEDescriptor = getGAEDescriptor;
/**
* Create a descriptor for Google Compute Engine.
* @return {object}
*/
async function getGCEDescriptor() {
const idResponse = await gcpMetadata.instance('id');
const zoneResponse = await gcpMetadata.instance('zone');
// Some parsing is necessary. Metadata service returns a fully
// qualified zone name: 'projects/{projectId}/zones/{zone}'. Logging
// wants just the zone part.
//
const zone = zoneFromQualifiedZone(zoneResponse);
return {
type: 'gce_instance',
labels: {
// idResponse can be BigNumber when the id too large for JavaScript
// numbers. Use a toString() to uniformly convert to a string.
instance_id: idResponse.toString(),
zone,
},
};
}
exports.getGCEDescriptor = getGCEDescriptor;
exports.KUBERNETES_NAMESPACE_ID_PATH = '/var/run/secrets/kubernetes.io/serviceaccount/namespace';
/**
* Create a descriptor for Google Container Engine.
*
* @return {object}
*/
async function getGKEDescriptor() {
// Stackdriver Logging Monitored Resource for 'container' requires
// cluster_name and namespace_id fields. Note that these *need* to be
// snake_case. The namespace_id is not easily available from inside the
// container, but we can get the namespace_name. Logging has been using the
// namespace_name in place of namespace_id for a while now. Log correlation
// with metrics may not necessarily work however.
//
const resp = await gcpMetadata.instance('attributes/cluster-name');
let namespace;
try {
namespace = await readFile(exports.KUBERNETES_NAMESPACE_ID_PATH, 'utf8');
}
catch (err) {
throw new Error(`Error reading ${exports.KUBERNETES_NAMESPACE_ID_PATH}: ${err.message}`);
}
return {
type: 'container',
labels: {
cluster_name: resp,
namespace_id: namespace,
},
};
}
exports.getGKEDescriptor = getGKEDescriptor;
/**
* Create a global descriptor.
*
* @returns {object}
*/
function getGlobalDescriptor() {
return {
type: 'global',
};
}
exports.getGlobalDescriptor = getGlobalDescriptor;
/**
* Attempt to contact the metadata service and determine,
* based on request success and environment variables, what type of resource
* the library is operating on.
*/
async function getDefaultResource(auth) {
const env = await auth.getEnv();
switch (env) {
case google_auth_library_1.GCPEnv.KUBERNETES_ENGINE:
return getGKEDescriptor().catch(() => getGlobalDescriptor());
case google_auth_library_1.GCPEnv.APP_ENGINE:
return getGAEDescriptor().catch(() => getGlobalDescriptor());
case google_auth_library_1.GCPEnv.CLOUD_FUNCTIONS:
return getCloudFunctionDescriptor();
case google_auth_library_1.GCPEnv.COMPUTE_ENGINE:
// Test for compute engine should be done after all the rest -
// everything runs on top of compute engine.
return getGCEDescriptor().catch(() => getGlobalDescriptor());
default:
return getGlobalDescriptor();
}
}
exports.getDefaultResource = getDefaultResource;
/**
* For logged errors, users can provide a service context. This enables errors
* to be picked up Stackdriver Error Reporting. For more information see
* [this guide]{@link
* https://cloud.google.com/error-reporting/docs/formatting-error-messages} and
* the [official documentation]{@link
* https://cloud.google.com/error-reporting/reference/rest/v1beta1/ServiceContext}.
*/
async function detectServiceContext(auth) {
const env = await auth.getEnv();
switch (env) {
case google_auth_library_1.GCPEnv.APP_ENGINE:
return {
service: process.env.GAE_SERVICE || process.env.GAE_MODULE_NAME,
version: process.env.GAE_VERSION || process.env.GAE_MODULE_VERSION,
};
case google_auth_library_1.GCPEnv.CLOUD_FUNCTIONS:
return {
service: process.env.FUNCTION_NAME,
};
// One Kubernetes we probably need to use the pod-name to describe the
// service. Currently there isn't a universal way to acquire the pod
// name from within the pod.
case google_auth_library_1.GCPEnv.KUBERNETES_ENGINE:
case google_auth_library_1.GCPEnv.COMPUTE_ENGINE:
default:
return null;
}
}
exports.detectServiceContext = detectServiceContext;
//# sourceMappingURL=metadata.js.map