haifa-reminder/node_modules/gcp-metadata/build/src/index.js

324 lines
12 KiB
JavaScript
Raw Normal View History

2023-07-26 11:01:58 +00:00
"use strict";
/**
* Copyright 2018 Google LLC
*
* Distributed under MIT license.
* See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.requestTimeout = exports.setGCPResidency = exports.getGCPResidency = exports.gcpResidencyCache = exports.resetIsAvailableCache = exports.isAvailable = exports.project = exports.instance = exports.METADATA_SERVER_DETECTION = exports.HEADERS = exports.HEADER_VALUE = exports.HEADER_NAME = exports.SECONDARY_HOST_ADDRESS = exports.HOST_ADDRESS = exports.BASE_PATH = void 0;
const gaxios_1 = require("gaxios");
const jsonBigint = require("json-bigint");
const gcp_residency_1 = require("./gcp-residency");
exports.BASE_PATH = '/computeMetadata/v1';
exports.HOST_ADDRESS = 'http://169.254.169.254';
exports.SECONDARY_HOST_ADDRESS = 'http://metadata.google.internal.';
exports.HEADER_NAME = 'Metadata-Flavor';
exports.HEADER_VALUE = 'Google';
exports.HEADERS = Object.freeze({ [exports.HEADER_NAME]: exports.HEADER_VALUE });
/**
* Metadata server detection override options.
*
* Available via `process.env.METADATA_SERVER_DETECTION`.
*/
exports.METADATA_SERVER_DETECTION = Object.freeze({
'assume-present': "don't try to ping the metadata server, but assume it's present",
none: "don't try to ping the metadata server, but don't try to use it either",
'bios-only': "treat the result of a BIOS probe as canonical (don't fall back to pinging)",
'ping-only': 'skip the BIOS probe, and go straight to pinging',
});
/**
* Returns the base URL while taking into account the GCE_METADATA_HOST
* environment variable if it exists.
*
* @returns The base URL, e.g., http://169.254.169.254/computeMetadata/v1.
*/
function getBaseUrl(baseUrl) {
if (!baseUrl) {
baseUrl =
process.env.GCE_METADATA_IP ||
process.env.GCE_METADATA_HOST ||
exports.HOST_ADDRESS;
}
// If no scheme is provided default to HTTP:
if (!/^https?:\/\//.test(baseUrl)) {
baseUrl = `http://${baseUrl}`;
}
return new URL(exports.BASE_PATH, baseUrl).href;
}
// Accepts an options object passed from the user to the API. In previous
// versions of the API, it referred to a `Request` or an `Axios` request
// options object. Now it refers to an object with very limited property
// names. This is here to help ensure users don't pass invalid options when
// they upgrade from 0.4 to 0.5 to 0.8.
function validate(options) {
Object.keys(options).forEach(key => {
switch (key) {
case 'params':
case 'property':
case 'headers':
break;
case 'qs':
throw new Error("'qs' is not a valid configuration option. Please use 'params' instead.");
default:
throw new Error(`'${key}' is not a valid configuration option.`);
}
});
}
async function metadataAccessor(type, options, noResponseRetries = 3, fastFail = false) {
options = options || {};
if (typeof options === 'string') {
options = { property: options };
}
let property = '';
if (typeof options === 'object' && options.property) {
property = '/' + options.property;
}
validate(options);
try {
const requestMethod = fastFail ? fastFailMetadataRequest : gaxios_1.request;
const res = await requestMethod({
url: `${getBaseUrl()}/${type}${property}`,
headers: Object.assign({}, exports.HEADERS, options.headers),
retryConfig: { noResponseRetries },
params: options.params,
responseType: 'text',
timeout: requestTimeout(),
});
// NOTE: node.js converts all incoming headers to lower case.
if (res.headers[exports.HEADER_NAME.toLowerCase()] !== exports.HEADER_VALUE) {
throw new Error(`Invalid response from metadata service: incorrect ${exports.HEADER_NAME} header.`);
}
else if (!res.data) {
throw new Error('Invalid response from the metadata service');
}
if (typeof res.data === 'string') {
try {
return jsonBigint.parse(res.data);
}
catch (_a) {
/* ignore */
}
}
return res.data;
}
catch (e) {
const err = e;
if (err.response && err.response.status !== 200) {
err.message = `Unsuccessful response status code. ${err.message}`;
}
throw e;
}
}
async function fastFailMetadataRequest(options) {
const secondaryOptions = {
...options,
url: options.url.replace(getBaseUrl(), getBaseUrl(exports.SECONDARY_HOST_ADDRESS)),
};
// We race a connection between DNS/IP to metadata server. There are a couple
// reasons for this:
//
// 1. the DNS is slow in some GCP environments; by checking both, we might
// detect the runtime environment signficantly faster.
// 2. we can't just check the IP, which is tarpitted and slow to respond
// on a user's local machine.
//
// Additional logic has been added to make sure that we don't create an
// unhandled rejection in scenarios where a failure happens sometime
// after a success.
//
// Note, however, if a failure happens prior to a success, a rejection should
// occur, this is for folks running locally.
//
let responded = false;
const r1 = (0, gaxios_1.request)(options)
.then(res => {
responded = true;
return res;
})
.catch(err => {
if (responded) {
return r2;
}
else {
responded = true;
throw err;
}
});
const r2 = (0, gaxios_1.request)(secondaryOptions)
.then(res => {
responded = true;
return res;
})
.catch(err => {
if (responded) {
return r1;
}
else {
responded = true;
throw err;
}
});
return Promise.race([r1, r2]);
}
/**
* Obtain metadata for the current GCE instance
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function instance(options) {
return metadataAccessor('instance', options);
}
exports.instance = instance;
/**
* Obtain metadata for the current GCP Project.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function project(options) {
return metadataAccessor('project', options);
}
exports.project = project;
/*
* How many times should we retry detecting GCP environment.
*/
function detectGCPAvailableRetries() {
return process.env.DETECT_GCP_RETRIES
? Number(process.env.DETECT_GCP_RETRIES)
: 0;
}
let cachedIsAvailableResponse;
/**
* Determine if the metadata server is currently available.
*/
async function isAvailable() {
if (process.env.METADATA_SERVER_DETECTION) {
const value = process.env.METADATA_SERVER_DETECTION.trim().toLocaleLowerCase();
if (!(value in exports.METADATA_SERVER_DETECTION)) {
throw new RangeError(`Unknown \`METADATA_SERVER_DETECTION\` env variable. Got \`${value}\`, but it should be \`${Object.keys(exports.METADATA_SERVER_DETECTION).join('`, `')}\`, or unset`);
}
switch (value) {
case 'assume-present':
return true;
case 'none':
return false;
case 'bios-only':
return getGCPResidency();
case 'ping-only':
// continue, we want to ping the server
}
}
try {
// If a user is instantiating several GCP libraries at the same time,
// this may result in multiple calls to isAvailable(), to detect the
// runtime environment. We use the same promise for each of these calls
// to reduce the network load.
if (cachedIsAvailableResponse === undefined) {
cachedIsAvailableResponse = metadataAccessor('instance', undefined, detectGCPAvailableRetries(),
// If the default HOST_ADDRESS has been overridden, we should not
// make an effort to try SECONDARY_HOST_ADDRESS (as we are likely in
// a non-GCP environment):
!(process.env.GCE_METADATA_IP || process.env.GCE_METADATA_HOST));
}
await cachedIsAvailableResponse;
return true;
}
catch (e) {
const err = e;
if (process.env.DEBUG_AUTH) {
console.info(err);
}
if (err.type === 'request-timeout') {
// If running in a GCP environment, metadata endpoint should return
// within ms.
return false;
}
if (err.response && err.response.status === 404) {
return false;
}
else {
if (!(err.response && err.response.status === 404) &&
// A warning is emitted if we see an unexpected err.code, or err.code
// is not populated:
(!err.code ||
![
'EHOSTDOWN',
'EHOSTUNREACH',
'ENETUNREACH',
'ENOENT',
'ENOTFOUND',
'ECONNREFUSED',
].includes(err.code))) {
let code = 'UNKNOWN';
if (err.code)
code = err.code;
process.emitWarning(`received unexpected error = ${err.message} code = ${code}`, 'MetadataLookupWarning');
}
// Failure to resolve the metadata service means that it is not available.
return false;
}
}
}
exports.isAvailable = isAvailable;
/**
* reset the memoized isAvailable() lookup.
*/
function resetIsAvailableCache() {
cachedIsAvailableResponse = undefined;
}
exports.resetIsAvailableCache = resetIsAvailableCache;
/**
* A cache for the detected GCP Residency.
*/
exports.gcpResidencyCache = null;
/**
* Detects GCP Residency.
* Caches results to reduce costs for subsequent calls.
*
* @see setGCPResidency for setting
*/
function getGCPResidency() {
if (exports.gcpResidencyCache === null) {
setGCPResidency();
}
return exports.gcpResidencyCache;
}
exports.getGCPResidency = getGCPResidency;
/**
* Sets the detected GCP Residency.
* Useful for forcing metadata server detection behavior.
*
* Set `null` to autodetect the environment (default behavior).
* @see getGCPResidency for getting
*/
function setGCPResidency(value = null) {
exports.gcpResidencyCache = value !== null ? value : (0, gcp_residency_1.detectGCPResidency)();
}
exports.setGCPResidency = setGCPResidency;
/**
* Obtain the timeout for requests to the metadata server.
*
* In certain environments and conditions requests can take longer than
* the default timeout to complete. This function will determine the
* appropriate timeout based on the environment.
*
* @returns {number} a request timeout duration in milliseconds.
*/
function requestTimeout() {
return getGCPResidency() ? 0 : 3000;
}
exports.requestTimeout = requestTimeout;
__exportStar(require("./gcp-residency"), exports);
//# sourceMappingURL=index.js.map