UNPKG

9 kBJavaScriptView Raw
1"use strict";
2/*!
3 * Copyright 2016 Google Inc. All Rights Reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.detectServiceContext = exports.getDefaultResource = exports.getGlobalDescriptor = exports.getGKEDescriptor = exports.KUBERNETES_NAMESPACE_ID_PATH = exports.getGCEDescriptor = exports.getGAEDescriptor = exports.getCloudRunDescriptor = exports.getCloudFunctionDescriptor = void 0;
19const fs = require("fs");
20const gcpMetadata = require("gcp-metadata");
21const google_auth_library_1 = require("google-auth-library");
22const util_1 = require("util");
23const readFile = util_1.promisify(fs.readFile);
24function zoneFromQualifiedZone(qualified) {
25 // Some parsing is necessary. Metadata service returns a fully
26 // qualified zone name: 'projects/{projectId}/zones/{zone}'. Logging
27 // wants just the zone part.
28 //
29 return qualified.split('/').pop();
30}
31function regionFromQualifiedZone(qualified) {
32 // Parses the region from the zone. Used for GCF and GCR which dynamically
33 // allocate zones.
34 const zone = zoneFromQualifiedZone(qualified);
35 const region = zone === undefined ? undefined : zone.slice(0, zone.lastIndexOf('-'));
36 return region;
37}
38/**
39 * Create a descriptor for Cloud Functions.
40 *
41 * @returns {object}
42 */
43async function getCloudFunctionDescriptor() {
44 // If the region is already available via an environment variable, don't delay the function by pinging metaserver.
45 let region = undefined;
46 if (!(process.env.GOOGLE_CLOUD_REGION || process.env.FUNCTION_REGION)) {
47 const qualifiedZone = await gcpMetadata.instance('zone');
48 region = regionFromQualifiedZone(qualifiedZone);
49 }
50 /**
51 * In GCF versions after Node 8, K_SERVICE is the preferred way to
52 * get the function name. We still check for GOOGLE_CLOUD_REGION and FUNCTION_REGION for backwards Node runtime compatibility.
53 */
54 return {
55 type: 'cloud_function',
56 labels: {
57 function_name: process.env.K_SERVICE || process.env.FUNCTION_NAME,
58 region: process.env.GOOGLE_CLOUD_REGION ||
59 process.env.FUNCTION_REGION ||
60 region,
61 },
62 };
63}
64exports.getCloudFunctionDescriptor = getCloudFunctionDescriptor;
65/**
66 * Create a descriptor for Cloud Run.
67 *
68 * @returns {object}
69 */
70async function getCloudRunDescriptor() {
71 const qualifiedZone = await gcpMetadata.instance('zone');
72 const location = regionFromQualifiedZone(qualifiedZone);
73 return {
74 type: 'cloud_run_revision',
75 labels: {
76 location,
77 service_name: process.env.K_SERVICE,
78 revision_name: process.env.K_REVISION,
79 configuration_name: process.env.K_CONFIGURATION,
80 },
81 };
82}
83exports.getCloudRunDescriptor = getCloudRunDescriptor;
84/**
85 * Create a descriptor for Google App Engine.
86 *
87 * @returns {object}
88 */
89async function getGAEDescriptor() {
90 const qualifiedZone = await gcpMetadata.instance('zone');
91 const zone = zoneFromQualifiedZone(qualifiedZone);
92 return {
93 type: 'gae_app',
94 labels: {
95 module_id: process.env.GAE_SERVICE || process.env.GAE_MODULE_NAME,
96 version_id: process.env.GAE_VERSION,
97 zone,
98 },
99 };
100}
101exports.getGAEDescriptor = getGAEDescriptor;
102/**
103 * Create a descriptor for Google Compute Engine.
104 * @return {object}
105 */
106async function getGCEDescriptor() {
107 const idResponse = await gcpMetadata.instance('id');
108 const zoneResponse = await gcpMetadata.instance('zone');
109 // Some parsing is necessary. Metadata service returns a fully
110 // qualified zone name: 'projects/{projectId}/zones/{zone}'. Logging
111 // wants just the zone part.
112 //
113 const zone = zoneFromQualifiedZone(zoneResponse);
114 return {
115 type: 'gce_instance',
116 labels: {
117 // idResponse can be BigNumber when the id too large for JavaScript
118 // numbers. Use a toString() to uniformly convert to a string.
119 instance_id: idResponse.toString(),
120 zone,
121 },
122 };
123}
124exports.getGCEDescriptor = getGCEDescriptor;
125exports.KUBERNETES_NAMESPACE_ID_PATH = '/var/run/secrets/kubernetes.io/serviceaccount/namespace';
126/**
127 * Create a descriptor for Google Container Engine.
128 *
129 * @return {object}
130 */
131async function getGKEDescriptor() {
132 // Cloud Logging Monitored Resource for 'container' requires
133 // cluster_name and namespace_id fields. Note that these *need* to be
134 // snake_case. The namespace_id is not easily available from inside the
135 // container, but we can get the namespace_name. Logging has been using the
136 // namespace_name in place of namespace_id for a while now. Log correlation
137 // with metrics may not necessarily work however.
138 //
139 const resp = await gcpMetadata.instance('attributes/cluster-name');
140 const qualifiedZone = await gcpMetadata.instance('zone');
141 const location = zoneFromQualifiedZone(qualifiedZone);
142 let namespace = '';
143 try {
144 namespace = await readFile(exports.KUBERNETES_NAMESPACE_ID_PATH, 'utf8');
145 }
146 catch (err) {
147 // Ignore errors (leave namespace as a nil string).
148 }
149 return {
150 type: 'k8s_container',
151 labels: {
152 location,
153 cluster_name: resp,
154 namespace_name: namespace,
155 pod_name: process.env.HOSTNAME,
156 // Users must manually supply container name for now.
157 // This may be autodetected in the future, pending b/145137070.
158 container_name: process.env.CONTAINER_NAME,
159 },
160 };
161}
162exports.getGKEDescriptor = getGKEDescriptor;
163/**
164 * Create a global descriptor.
165 *
166 * @returns {object}
167 */
168function getGlobalDescriptor() {
169 return {
170 type: 'global',
171 };
172}
173exports.getGlobalDescriptor = getGlobalDescriptor;
174/**
175 * Attempt to contact the metadata service and determine,
176 * based on request success and environment variables, what type of resource
177 * the library is operating on.
178 */
179async function getDefaultResource(auth) {
180 const env = await auth.getEnv();
181 switch (env) {
182 case google_auth_library_1.GCPEnv.KUBERNETES_ENGINE:
183 return getGKEDescriptor().catch(() => getGlobalDescriptor());
184 case google_auth_library_1.GCPEnv.APP_ENGINE:
185 return getGAEDescriptor().catch(() => getGlobalDescriptor());
186 case google_auth_library_1.GCPEnv.CLOUD_FUNCTIONS:
187 return getCloudFunctionDescriptor().catch(() => getGlobalDescriptor());
188 case google_auth_library_1.GCPEnv.CLOUD_RUN:
189 return getCloudRunDescriptor().catch(() => getGlobalDescriptor());
190 case google_auth_library_1.GCPEnv.COMPUTE_ENGINE:
191 return getGCEDescriptor().catch(() => getGlobalDescriptor());
192 default:
193 return getGlobalDescriptor();
194 }
195}
196exports.getDefaultResource = getDefaultResource;
197/**
198 * For logged errors, users can provide a service context. This enables errors
199 * to be picked up Cloud Error Reporting. For more information see
200 * [this guide]{@link
201 * https://cloud.google.com/error-reporting/docs/formatting-error-messages} and
202 * the [official documentation]{@link
203 * https://cloud.google.com/error-reporting/reference/rest/v1beta1/ServiceContext}.
204 */
205async function detectServiceContext(auth) {
206 const env = await auth.getEnv();
207 switch (env) {
208 case google_auth_library_1.GCPEnv.APP_ENGINE:
209 return {
210 service: process.env.GAE_SERVICE || process.env.GAE_MODULE_NAME,
211 version: process.env.GAE_VERSION || process.env.GAE_MODULE_VERSION,
212 };
213 case google_auth_library_1.GCPEnv.CLOUD_FUNCTIONS:
214 return {
215 service: process.env.FUNCTION_NAME,
216 };
217 // On Kubernetes we use the pod-name to describe the service. Currently,
218 // we acquire the pod-name from within the pod through env var `HOSTNAME`.
219 case google_auth_library_1.GCPEnv.KUBERNETES_ENGINE:
220 return {
221 service: process.env.HOSTNAME,
222 };
223 case google_auth_library_1.GCPEnv.CLOUD_RUN:
224 return {
225 service: process.env.K_SERVICE,
226 };
227 case google_auth_library_1.GCPEnv.COMPUTE_ENGINE:
228 return null;
229 default:
230 return null;
231 }
232}
233exports.detectServiceContext = detectServiceContext;
234//# sourceMappingURL=metadata.js.map
\No newline at end of file