From 5cd1864a511a358639a7ad5d308b87575507342a Mon Sep 17 00:00:00 2001 From: Jon Edvald Date: Wed, 19 Feb 2020 19:20:25 +0100 Subject: [PATCH] fix: issues with running HTTPS requests through HTTP proxy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds and configured `global-agent` (https://github.com/gajus/global-agent) and replaces Axios with `got`  (https://github.com/sindresorhus/got) which, since Axios has a long-standing issue with routing HTTPS requests through HTTPS proxies. --- garden-service/package-lock.json | 310 +++++++++++++++++- garden-service/package.json | 5 + garden-service/src/cli/helpers.ts | 8 +- garden-service/src/commands/call.ts | 54 ++- .../commands/cleanup-cluster-registry.ts | 14 +- .../src/plugins/kubernetes/container/util.ts | 11 +- garden-service/src/util/ext-tools.ts | 17 +- garden-service/src/util/http.ts | 23 ++ garden-service/test/unit/src/commands/call.ts | 50 +-- 9 files changed, 422 insertions(+), 70 deletions(-) create mode 100644 garden-service/src/util/http.ts diff --git a/garden-service/package-lock.json b/garden-service/package-lock.json index d0364cbc68..4c329b5116 100644 --- a/garden-service/package-lock.json +++ b/garden-service/package-lock.json @@ -772,6 +772,14 @@ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==" }, + "@szmarczak/http-timer": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", + "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, "@types/accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", @@ -809,6 +817,17 @@ "@types/node": "*" } }, + "@types/cacheable-request": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, "@types/caseless": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz", @@ -939,6 +958,12 @@ "@types/node": "*" } }, + "@types/global-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/global-agent/-/global-agent-2.1.0.tgz", + "integrity": "sha512-xBOerse4Agekl7VZJclA9bfuA9aa3u9T24TDkBiMQrZgu4qe5HMBPzVGzAt2k4dx/v3uIFI6CzG0Z9X894LHrg==", + "dev": true + }, "@types/google-cloud__kms": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@types/google-cloud__kms/-/google-cloud__kms-1.5.0.tgz", @@ -955,6 +980,30 @@ "integrity": "sha512-ifFemzjNchFBCtHS6bZNhSZCBu7tbtOe0e8qY0z2J4HtFXmPJjm6fXSaQsTG7yhShBEZtt2oP/bkwu5k+emlkQ==", "dev": true }, + "@types/got": { + "version": "9.6.9", + "resolved": "https://registry.npmjs.org/@types/got/-/got-9.6.9.tgz", + "integrity": "sha512-w+ZE+Ovp6fM+1sHwJB7RN3f3pTJHZkyABuULqbtknqezQyWadFEp5BzOXaZzRqAw2md6/d3ybxQJt+BNgpvzOg==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, "@types/hapi__joi": { "version": "16.0.5", "resolved": "https://registry.npmjs.org/@types/hapi__joi/-/hapi__joi-16.0.5.tgz", @@ -973,6 +1022,11 @@ "integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==", "dev": true }, + "@types/http-cache-semantics": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==" + }, "@types/inquirer": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-6.5.0.tgz", @@ -1033,6 +1087,14 @@ "integrity": "sha1-/1QEYtL7TQqIRBzq8n0oewHD2Hg=", "dev": true }, + "@types/keyv": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", + "requires": { + "@types/node": "*" + } + }, "@types/klaw": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/klaw/-/klaw-3.0.0.tgz", @@ -1258,6 +1320,14 @@ "@types/request": "*" } }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "requires": { + "@types/node": "*" + } + }, "@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", @@ -2206,6 +2276,11 @@ "integrity": "sha512-zo+HIdIhzojv6F1siQPqPFROyVy7C50KzHv/k/Iz+BtvtVzSHXiMXOpq2wCfNkeBqdCv+V8XOV96tsEt2W/3rQ==", "dev": true }, + "boolean": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz", + "integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2310,6 +2385,29 @@ "ylru": "^1.2.0" } }, + "cacheable-lookup": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.0.tgz", + "integrity": "sha512-s2piO6LvA7xnL1AR03wuEdSx3BZT3tIJpZ56/lcJwzO/6DTJZlTs7X3lrvPxk6d1PlDe6PrVe2TjlUIZNFglAQ==", + "requires": { + "keyv": "^4.0.0" + }, + "dependencies": { + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "keyv": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.0.tgz", + "integrity": "sha512-U7ioE8AimvRVLfw4LffyOIRhL2xVgmE8T22L6i0BucSnBUyv4w+I7VN/zVZwRKHOI6ZRUcdMdWHQ8KSUvGpEog==", + "requires": { + "json-buffer": "3.0.1" + } + } + } + }, "cacheable-request": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", @@ -3568,11 +3666,15 @@ "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", "dev": true }, + "defer-to-connect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", + "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==" + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -3995,6 +4097,11 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" + }, "diagnostics": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", @@ -4311,8 +4418,7 @@ "es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" }, "es6-iterator": { "version": "2.0.3", @@ -5980,6 +6086,32 @@ } } }, + "global-agent": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.8.tgz", + "integrity": "sha512-VpBe/rhY6Rw2VDOTszAMNambg+4Qv8j0yiTNDYEXXXxkUNGWLHp8A3ztK4YDBbFNcWF4rgsec6/5gPyryya/+A==", + "requires": { + "boolean": "^3.0.0", + "core-js": "^3.6.4", + "es6-error": "^4.1.1", + "matcher": "^2.1.0", + "roarr": "^2.15.2", + "semver": "^7.1.2", + "serialize-error": "^5.0.0" + }, + "dependencies": { + "core-js": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", + "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==" + }, + "semver": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", + "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==" + } + } + }, "global-dirs": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", @@ -6019,6 +6151,14 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, + "globalthis": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.1.tgz", + "integrity": "sha512-mJPRTc/P39NH/iNG4mXa9aIhNymaQikTrnspeCa2ZuJ+mH2QN/rXwtX3XwKrHqWgUQFbNZKtHM105aHzJalElw==", + "requires": { + "define-properties": "^1.1.3" + } + }, "globby": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", @@ -6181,6 +6321,111 @@ "node-forge": "^0.9.0" } }, + "got": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-10.6.0.tgz", + "integrity": "sha512-3LIdJNTdCFbbJc+h/EH0V5lpNpbJ6Bfwykk21lcQvQsEcrzdi/ltCyQehFHLzJ/ka0UMH4Slg0hkYvAZN9qUDg==", + "requires": { + "@sindresorhus/is": "^2.0.0", + "@szmarczak/http-timer": "^4.0.0", + "@types/cacheable-request": "^6.0.1", + "cacheable-lookup": "^2.0.0", + "cacheable-request": "^7.0.1", + "decompress-response": "^5.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^5.0.0", + "lowercase-keys": "^2.0.0", + "mimic-response": "^2.1.0", + "p-cancelable": "^2.0.0", + "p-event": "^4.0.0", + "responselike": "^2.0.0", + "to-readable-stream": "^2.0.0", + "type-fest": "^0.10.0" + }, + "dependencies": { + "@sindresorhus/is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.0.tgz", + "integrity": "sha512-lXKXfypKo644k4Da4yXkPCrwcvn6SlUW2X2zFbuflKHNjf0w9htru01bo26uMhleMXsDmnZ12eJLdrAZa9MANg==" + }, + "cacheable-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", + "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^2.0.0" + } + }, + "decompress-response": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz", + "integrity": "sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==", + "requires": { + "mimic-response": "^2.0.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "requires": { + "pump": "^3.0.0" + } + }, + "http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-Z2EICWNJou7Tr9Bd2M2UqDJq3A9F2ePG9w3lIpjoyuSyXFP9QbniJVu3XQYytuw5ebmG7dXSXO9PgAjJG8DDKA==" + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "keyv": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.0.tgz", + "integrity": "sha512-U7ioE8AimvRVLfw4LffyOIRhL2xVgmE8T22L6i0BucSnBUyv4w+I7VN/zVZwRKHOI6ZRUcdMdWHQ8KSUvGpEog==", + "requires": { + "json-buffer": "3.0.1" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==" + }, + "p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==" + }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "type-fest": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz", + "integrity": "sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==" + } + } + }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", @@ -6755,6 +7000,11 @@ "sshpk": "^1.7.0" } }, + "http-status-codes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-1.4.0.tgz", + "integrity": "sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ==" + }, "https-proxy-agent": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", @@ -8172,6 +8422,14 @@ } } }, + "matcher": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-2.1.0.tgz", + "integrity": "sha512-o+nZr+vtJtgPNklyeUKkkH42OsK8WAfdgaJE2FNxcjLPg+5QbeEoT6vRj8Xq/iv18JlQ9cmKsEu0b94ixWf1YQ==", + "requires": { + "escape-string-regexp": "^2.0.0" + } + }, "md5": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", @@ -9206,8 +9464,7 @@ "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object-visit": { "version": "1.0.1", @@ -10702,6 +10959,26 @@ } } }, + "roarr": { + "version": "2.15.2", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.2.tgz", + "integrity": "sha512-jmaDhK9CO4YbQAV8zzCnq9vjAqeO489MS5ehZ+rXmFiPFFE6B+S9KYO6prjmLJ5A0zY3QxVlQdrIya7E/azz/Q==", + "requires": { + "boolean": "^3.0.0", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + } + } + }, "run-async": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", @@ -10771,6 +11048,11 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==" }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" + }, "semver-greatest-satisfied-range": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", @@ -10780,6 +11062,14 @@ "sver-compat": "^1.5.0" } }, + "serialize-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-5.0.0.tgz", + "integrity": "sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA==", + "requires": { + "type-fest": "^0.8.0" + } + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -11813,6 +12103,11 @@ } } }, + "to-readable-stream": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz", + "integrity": "sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w==" + }, "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", @@ -12068,6 +12363,11 @@ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", diff --git a/garden-service/package.json b/garden-service/package.json index bada8acb01..79b34e8e57 100644 --- a/garden-service/package.json +++ b/garden-service/package.json @@ -64,9 +64,12 @@ "fs-extra": "^8.1.0", "get-port": "^5.1.0", "glob": "^7.1.6", + "global-agent": "^2.1.8", + "got": "^10.5.7", "gray-matter": "^4.0.2", "has-ansi": "^4.0.0", "hasha": "^5.1.0", + "http-status-codes": "^1.4.0", "humanize-string": "^2.1.0", "indent-string": "^4.0.0", "inquirer": "^7.0.1", @@ -143,7 +146,9 @@ "@types/dockerode": "^2.5.21", "@types/fs-extra": "^8.0.1", "@types/glob": "^7.1.1", + "@types/global-agent": "^2.1.0", "@types/google-cloud__kms": "^1.5.0", + "@types/got": "^9.6.9", "@types/hapi__joi": "^16.0.5", "@types/has-ansi": "^3.0.0", "@types/inquirer": "6.5.0", diff --git a/garden-service/src/cli/helpers.ts b/garden-service/src/cli/helpers.ts index 0aa431ba70..43f38c8d20 100644 --- a/garden-service/src/cli/helpers.ts +++ b/garden-service/src/cli/helpers.ts @@ -6,7 +6,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import axios from "axios" import chalk from "chalk" import ci = require("ci-info") import { pathExists } from "fs-extra" @@ -23,6 +22,7 @@ import { LogEntry } from "../logger/log-entry" import { STATIC_DIR, VERSION_CHECK_URL } from "../constants" import { printWarningMessage } from "../logger/util" import { GlobalConfigStore, globalConfigKeys } from "../config-store" +import { got, GotResponse } from "../util/http" // Parameter types T which map between the Parameter class and the Sywac cli library. // In case we add types that aren't supported natively by Sywac, see: http://sywac.io/docs/sync-config.html#custom @@ -236,7 +236,7 @@ export async function checkForUpdates(config: GlobalConfigStore, logger: LogEntr headers["X-ci-name"] = ci.name } - const res = await axios.get(`${VERSION_CHECK_URL}?${qs.stringify(query)}`, { headers }) + const res = await got(`${VERSION_CHECK_URL}?${qs.stringify(query)}`, { headers }).json>() const configObj = await config.get() const showMessage = configObj.lastVersionCheck && @@ -246,8 +246,8 @@ export async function checkForUpdates(config: GlobalConfigStore, logger: LogEntr // we check again for lastVersionCheck because in the first run it doesn't exist if (showMessage || !configObj.lastVersionCheck) { - if (res.data.status === "OUTDATED") { - printWarningMessage(logger, res.data.message) + if (res.body.status === "OUTDATED") { + printWarningMessage(logger, res.body.message) await config.set([globalConfigKeys.lastVersionCheck], { lastRun: new Date() }) } } diff --git a/garden-service/src/commands/call.ts b/garden-service/src/commands/call.ts index 1dc0990c02..2158d5e258 100644 --- a/garden-service/src/commands/call.ts +++ b/garden-service/src/commands/call.ts @@ -7,17 +7,17 @@ */ import { parse, resolve } from "url" -import Axios from "axios" import chalk from "chalk" -import { isObject } from "util" +import { getStatusText } from "http-status-codes" import { Command, CommandResult, CommandParams, StringParameter } from "./base" import { splitFirst } from "../util/util" import { ParameterError, RuntimeError } from "../exceptions" -import { find, includes, pick } from "lodash" +import { find, includes } from "lodash" import { ServiceIngress, getIngressUrl } from "../types/service" -import dedent = require("dedent") +import { dedent } from "../util/string" import { printHeader } from "../logger/util" import { emptyRuntimeContext } from "../runtime-context" +import { got, GotResponse } from "../util/http" const callArgs = { serviceAndPath: new StringParameter({ @@ -28,6 +28,18 @@ const callArgs = { type Args = typeof callArgs +interface CallResult { + serviceName: string + path: string + url: string + response: { + status: number + statusText: string + headers: GotResponse["headers"] + data: string | object + } +} + export class CallCommand extends Command { name = "call" help = "Call a service ingress endpoint." @@ -46,7 +58,7 @@ export class CallCommand extends Command { arguments = callArgs - async action({ garden, log, headerLog, args }: CommandParams): Promise { + async action({ garden, log, headerLog, args }: CommandParams): Promise> { printHeader(headerLog, "Call", "telephone_receiver") let [serviceName, path] = splitFirst(args.serviceAndPath, "/") @@ -147,37 +159,55 @@ export class CallCommand extends Command { // this is to accept self-signed certs process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" - const req = Axios({ + const req = got({ method, url, headers: { host }, }) // TODO: add verbose and debug logging (request/response headers etc.) - let res + let res: GotResponse + let statusText = "" try { res = await req entry.setSuccess() - log.info(chalk.green(`${res.status} ${res.statusText}\n`)) + statusText = getStatusText(res.statusCode) + log.info(chalk.green(`${res.statusCode} ${statusText}\n`)) } catch (err) { res = err.response entry.setError() - const error = res ? `${res.status} ${res.statusText}` : err.message + statusText = getStatusText(res.statusCode) + const error = res ? `${res.statusCode} ${statusText}` : err.message log.info(chalk.red(error + "\n")) return {} } - const resStr = isObject(res.data) ? JSON.stringify(res.data, null, 2) : res.data + let output: string | object = res.body + + if (res.headers["content-type"] === "application/json") { + try { + output = JSON.parse(res.body) + } catch (err) { + throw new RuntimeError(`Got content-type=application/json but could not parse output as JSON`, { + response: res, + }) + } + } - res.data && log.info(chalk.white(resStr)) + res.body && log.info(chalk.white(res.body)) return { result: { serviceName, path, url, - response: pick(res, ["status", "statusText", "headers", "data"]), + response: { + data: output, + headers: res.headers, + status: res.statusCode, + statusText, + }, }, } } diff --git a/garden-service/src/plugins/kubernetes/commands/cleanup-cluster-registry.ts b/garden-service/src/plugins/kubernetes/commands/cleanup-cluster-registry.ts index 971d9d6c86..cd5f586a58 100644 --- a/garden-service/src/plugins/kubernetes/commands/cleanup-cluster-registry.ts +++ b/garden-service/src/plugins/kubernetes/commands/cleanup-cluster-registry.ts @@ -126,12 +126,12 @@ async function getImagesInRegistry(ctx: KubernetesPluginContext, log: LogEntry) while (nextUrl) { const res = await queryRegistry(ctx, log, nextUrl) - repositories.push(...res.data.repositories) + repositories.push(...res.body.repositories) // Paginate - const linkHeader = res.headers["Link"] + const linkHeader = res.headers["Link"] if (linkHeader) { - nextUrl = linkHeader.match(/<(.*)>/)[1] + nextUrl = linkHeader.match(/<(.*)>/)![1] } else { nextUrl = "" } @@ -144,13 +144,13 @@ async function getImagesInRegistry(ctx: KubernetesPluginContext, log: LogEntry) while (nextUrl) { const res = await queryRegistry(ctx, log, nextUrl) - if (res.data.tags) { - images.push(...res.data.tags.map((tag: string) => `${repo}:${tag}`)) + if (res.body.tags) { + images.push(...res.body.tags.map((tag: string) => `${repo}:${tag}`)) } // Paginate - const linkHeader = res.headers["link"] + const linkHeader = res.headers["link"] if (linkHeader) { - nextUrl = linkHeader.match(/<(.*)>/)[1] + nextUrl = linkHeader.match(/<(.*)>/)![1] } else { nextUrl = "" } diff --git a/garden-service/src/plugins/kubernetes/container/util.ts b/garden-service/src/plugins/kubernetes/container/util.ts index 29c091d722..d5c7bdff70 100644 --- a/garden-service/src/plugins/kubernetes/container/util.ts +++ b/garden-service/src/plugins/kubernetes/container/util.ts @@ -11,20 +11,15 @@ import { getPortForward } from "../port-forward" import { CLUSTER_REGISTRY_DEPLOYMENT_NAME, CLUSTER_REGISTRY_PORT } from "../constants" import { LogEntry } from "../../../logger/log-entry" import { KubernetesPluginContext } from "../config" -import axios, { AxiosRequestConfig } from "axios" import { getSystemNamespace } from "../namespace" +import { got, GotOptions, GotResponse } from "../../../util/http" -export async function queryRegistry( - ctx: KubernetesPluginContext, - log: LogEntry, - path: string, - opts: AxiosRequestConfig = {} -) { +export async function queryRegistry(ctx: KubernetesPluginContext, log: LogEntry, path: string, opts?: GotOptions) { const registryFwd = await getRegistryPortForward(ctx, log) const baseUrl = `http://localhost:${registryFwd.localPort}/v2/` const url = resolve(baseUrl, path) - return axios({ url, ...opts }) + return got(url, opts).json>() } export async function getRegistryPortForward(ctx: KubernetesPluginContext, log: LogEntry) { diff --git a/garden-service/src/util/ext-tools.ts b/garden-service/src/util/ext-tools.ts index 291c830f63..9fa0e9164e 100644 --- a/garden-service/src/util/ext-tools.ts +++ b/garden-service/src/util/ext-tools.ts @@ -11,7 +11,6 @@ import { pathExists, createWriteStream, ensureDir, chmod, remove, move } from "f import { ConfigurationError, ParameterError, GardenBaseError } from "../exceptions" import { join, dirname, basename, sep } from "path" import { hashString, exec } from "./util" -import Axios from "axios" import tar from "tar" import { SupportedPlatform, GARDEN_GLOBAL_PATH } from "../constants" import { LogEntry } from "../logger/log-entry" @@ -21,6 +20,7 @@ import uuid from "uuid" import crossSpawn from "cross-spawn" import { spawn } from "./util" import { Writable } from "stream" +import got from "got/dist/source" const AsyncLock = require("async-lock") const toolsPath = join(GARDEN_GLOBAL_PATH, "tools") @@ -137,20 +137,19 @@ export class Library { } protected async fetch(tmpPath: string, log: LogEntry) { - const response = await Axios({ + const response = got.stream({ method: "GET", url: this.spec.url, - responseType: "stream", }) // compute the sha256 checksum const hash = createHash("sha256") hash.setEncoding("hex") - response.data.pipe(hash) + response.pipe(hash) return new Promise((resolve, reject) => { - response.data.on("error", (err) => { - log.setError(`Failed fetching ${response.request.url}`) + response.on("error", (err) => { + log.setError(`Failed fetching ${this.spec.url}`) reject(err) }) @@ -176,8 +175,8 @@ export class Library { if (!this.spec.extract) { const targetExecutable = join(tmpPath, ...this.targetSubpath) - response.data.pipe(createWriteStream(targetExecutable)) - response.data.on("end", () => resolve()) + response.pipe(createWriteStream(targetExecutable)) + response.on("end", () => resolve()) } else { const format = this.spec.extract.format let extractor: Writable @@ -201,7 +200,7 @@ export class Library { return } - response.data.pipe(extractor) + response.pipe(extractor) extractor.on("error", (err) => { log.setError(`Failed extracting ${format} archive ${this.spec.url}`) diff --git a/garden-service/src/util/http.ts b/garden-service/src/util/http.ts new file mode 100644 index 0000000000..6290e96778 --- /dev/null +++ b/garden-service/src/util/http.ts @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2018-2020 Garden Technologies, Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +import _got, { Response } from "got" +import { bootstrap } from "global-agent" +import { OptionsOfDefaultResponseBody } from "got/dist/source/create" + +// Handle proxy environment settings +// (see https://github.com/gajus/global-agent#what-is-the-reason-global-agentbootstrap-does-not-use-http_proxy) +bootstrap({ + environmentVariableNamespace: "", + forceGlobalAgent: true, +}) + +// Exporting from here to make sure the global-agent bootstrap is executed, and for convenience as well +export const got = _got +export type GotOptions = OptionsOfDefaultResponseBody +export type GotResponse = Response diff --git a/garden-service/test/unit/src/commands/call.ts b/garden-service/test/unit/src/commands/call.ts index d3fe698271..0122d6762b 100644 --- a/garden-service/test/unit/src/commands/call.ts +++ b/garden-service/test/unit/src/commands/call.ts @@ -129,11 +129,11 @@ describe("commands.call", () => { opts: withDefaultGlobalOpts({}), }) - expect(result.url).to.equal("http://service-a.test-project-b.local.app.garden:32000/path-a") - expect(result.serviceName).to.equal("service-a") - expect(result.path).to.equal("/path-a") - expect(result.response.status).to.equal(200) - expect(result.response.data).to.equal("bla") + expect(result!.url).to.equal("http://service-a.test-project-b.local.app.garden:32000/path-a") + expect(result!.serviceName).to.equal("service-a") + expect(result!.path).to.equal("/path-a") + expect(result!.response.status).to.equal(200) + expect(result!.response.data).to.equal("bla") }) it("should default to the path '/' if that is exposed if no path is requested", async () => { @@ -154,11 +154,11 @@ describe("commands.call", () => { opts: withDefaultGlobalOpts({}), }) - expect(result.url).to.equal("http://service-a.test-project-b.local.app.garden:32000/path-a") - expect(result.serviceName).to.equal("service-a") - expect(result.path).to.equal("/path-a") - expect(result.response.status).to.equal(200) - expect(result.response.data).to.equal("bla") + expect(result!.url).to.equal("http://service-a.test-project-b.local.app.garden:32000/path-a") + expect(result!.serviceName).to.equal("service-a") + expect(result!.path).to.equal("/path-a") + expect(result!.response.status).to.equal(200) + expect(result!.response.data).to.equal("bla") }) it("should otherwise use the first defined ingress if no path is requested", async () => { @@ -179,11 +179,11 @@ describe("commands.call", () => { opts: withDefaultGlobalOpts({}), }) - expect(result.url).to.equal("http://service-b.test-project-b.local.app.garden:32000/") - expect(result.serviceName).to.equal("service-b") - expect(result.path).to.equal("/") - expect(result.response.status).to.equal(200) - expect(result.response.data).to.equal("bla") + expect(result!.url).to.equal("http://service-b.test-project-b.local.app.garden:32000/") + expect(result!.serviceName).to.equal("service-b") + expect(result!.path).to.equal("/") + expect(result!.response.status).to.equal(200) + expect(result!.response.data).to.equal("bla") }) it("should use the linkUrl if provided", async () => { @@ -204,11 +204,11 @@ describe("commands.call", () => { opts: withDefaultGlobalOpts({}), }) - expect(result.url).to.equal("https://www.example.com") - expect(result.serviceName).to.equal("service-a") - expect(result.path).to.equal("/") - expect(result.response.status).to.equal(200) - expect(result.response.data).to.equal("bla") + expect(result!.url).to.equal("https://www.example.com") + expect(result!.serviceName).to.equal("service-a") + expect(result!.path).to.equal("/") + expect(result!.response.status).to.equal(200) + expect(result!.response.data).to.equal("bla") }) it("should return the path for linkUrl", async () => { @@ -229,11 +229,11 @@ describe("commands.call", () => { opts: withDefaultGlobalOpts({}), }) - expect(result.url).to.equal("https://www.example.com/hello") - expect(result.serviceName).to.equal("service-b") - expect(result.path).to.equal("/hello") - expect(result.response.status).to.equal(200) - expect(result.response.data).to.equal("bla") + expect(result!.url).to.equal("https://www.example.com/hello") + expect(result!.serviceName).to.equal("service-b") + expect(result!.path).to.equal("/hello") + expect(result!.response.status).to.equal(200) + expect(result!.response.data).to.equal("bla") }) it("should error if service isn't running", async () => {