-
Notifications
You must be signed in to change notification settings - Fork 375
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Feat: add traffic mesh proxy flag #1196
Changes from 11 commits
8b9f02c
c6036a2
81ca361
7fdc804
7c200f2
099f1d3
bb121d0
b4448f2
34a79ea
a2941b2
788e51d
6114da5
868f21b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
const fs = require('fs-extra') | ||
const path = require('path') | ||
const execa = require('execa') | ||
const { fetchLatest, updateAvailable } = require('gh-release-fetch') | ||
const { NETLIFYDEVWARN } = require('../utils/logo') | ||
|
||
const isWindows = () => { | ||
return process.platform === 'win32' | ||
} | ||
|
||
const getRepository = ({ packageName }) => `netlify/${packageName}` | ||
|
||
const getExecName = ({ execName }) => { | ||
return isWindows() ? `${execName}.exe` : execName | ||
} | ||
|
||
const isExe = (mode, gid, uid) => { | ||
if (isWindows()) { | ||
return true | ||
} | ||
|
||
const isGroup = gid ? process.getgid && gid === process.getgid() : true | ||
const isUser = uid ? process.getuid && uid === process.getuid() : true | ||
|
||
return Boolean(mode & 0o0001 || (mode & 0o0010 && isGroup) || (mode & 0o0100 && isUser)) | ||
} | ||
|
||
const execExist = async binPath => { | ||
const binExists = await fs.exists(binPath) | ||
if (!binExists) { | ||
return false | ||
} | ||
const stat = fs.statSync(binPath) | ||
return stat && stat.isFile() && isExe(stat.mode, stat.gid, stat.uid) | ||
} | ||
|
||
const isVersionOutdated = async ({ packageName, currentVersion }) => { | ||
const outdated = await updateAvailable(getRepository({ packageName }), currentVersion) | ||
return outdated | ||
} | ||
|
||
const shouldFetchLatestVersion = async ({ binPath, packageName, execName, execArgs, pattern, log }) => { | ||
const execPath = path.join(binPath, getExecName({ execName })) | ||
|
||
const exists = await execExist(execPath) | ||
if (!exists) { | ||
return true | ||
} | ||
|
||
const { stdout } = await execa(execPath, execArgs) | ||
|
||
if (!stdout) { | ||
return false | ||
} | ||
|
||
const match = stdout.match(new RegExp(pattern)) | ||
if (!match) { | ||
return false | ||
} | ||
|
||
try { | ||
const outdated = await isVersionOutdated({ | ||
packageName, | ||
currentVersion: match[1], | ||
}) | ||
return outdated | ||
} catch (e) { | ||
if (exists) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a small fix to use existing version if checking for updates failed. |
||
log(NETLIFYDEVWARN, `failed checking for new version of '${packageName}'. Using existing version`) | ||
return false | ||
} else { | ||
throw e | ||
} | ||
} | ||
} | ||
|
||
const fetchLatestVersion = async ({ packageName, execName, destination, extension }) => { | ||
const win = isWindows() | ||
const platform = win ? 'windows' : process.platform | ||
const release = { | ||
repository: getRepository({ packageName }), | ||
package: `${execName}-${platform}-amd64.${extension}`, | ||
destination, | ||
extract: true, | ||
} | ||
|
||
await fetchLatest(release) | ||
} | ||
|
||
module.exports = { getExecName, shouldFetchLatestVersion, fetchLatestVersion } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
const test = require('ava') | ||
const path = require('path') | ||
const fs = require('fs-extra') | ||
const tempDirectory = require('temp-dir') | ||
const { v4: uuid } = require('uuid') | ||
const { getExecName, shouldFetchLatestVersion, fetchLatestVersion } = require('./exec-fetcher') | ||
|
||
test.beforeEach(t => { | ||
const directory = path.join(tempDirectory, `netlify-cli-exec-fetcher`, uuid()) | ||
t.context.binPath = directory | ||
}) | ||
|
||
test(`should postix exec with .exe on windows`, t => { | ||
const execName = 'some-binary-file' | ||
if (process.platform === 'win32') { | ||
t.is(getExecName({ execName }), `${execName}.exe`) | ||
} else { | ||
t.is(getExecName({ execName }), execName) | ||
} | ||
}) | ||
|
||
const packages = [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feels a bit strange to have this here and not in a dedicated test file (one for the tunnel client and one for the traffic mesh). |
||
{ | ||
packageName: 'live-tunnel-client', | ||
execName: 'live-tunnel-client', | ||
execArgs: ['version'], | ||
pattern: 'live-tunnel-client\\/v?([^\\s]+)', | ||
extension: process.platform === 'win32' ? 'zip' : 'tar.gz', | ||
}, | ||
{ | ||
packageName: 'traffic-mesh-agent', | ||
execName: 'traffic-mesh', | ||
execArgs: ['--version'], | ||
pattern: '\\sv(.+)', | ||
extension: 'zip', | ||
}, | ||
] | ||
|
||
packages.forEach(({ packageName, execName, execArgs, pattern, extension }) => { | ||
const log = console.log | ||
|
||
test(`${packageName} - should return true on empty directory`, async t => { | ||
const { binPath } = t.context | ||
const actual = await shouldFetchLatestVersion({ binPath, packageName, execName, execArgs, pattern, log }) | ||
t.is(actual, true) | ||
}) | ||
|
||
test(`${packageName} - should return false after latest version is fetched`, async t => { | ||
const { binPath } = t.context | ||
|
||
await fetchLatestVersion({ packageName, execName, destination: binPath, extension }) | ||
|
||
const actual = await shouldFetchLatestVersion({ binPath, packageName, execName, execArgs, pattern, log }) | ||
t.is(actual, false) | ||
}) | ||
|
||
test(`${packageName} - should download latest version on empty directory`, async t => { | ||
const { binPath } = t.context | ||
|
||
await fetchLatestVersion({ packageName, execName, destination: binPath, extension }) | ||
|
||
const execPath = path.join(binPath, getExecName({ execName })) | ||
const stats = await fs.stat(execPath) | ||
t.is(stats.size >= 5000, true) | ||
}) | ||
}) | ||
|
||
test.afterEach(async t => { | ||
await fs.remove(t.context.binPath) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
const path = require('path') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've started a new concept of |
||
const os = require('os') | ||
|
||
const NETLIFY_HOME = '.netlify' | ||
|
||
const getHomeDirectory = () => { | ||
return path.join(os.homedir(), NETLIFY_HOME) | ||
} | ||
|
||
const getPathInHome = paths => { | ||
const pathInHome = path.join(getHomeDirectory(), ...paths) | ||
return pathInHome | ||
} | ||
|
||
module.exports = { getPathInHome } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,14 @@ | ||
const fetch = require('node-fetch') | ||
const fs = require('fs') | ||
const os = require('os') | ||
const path = require('path') | ||
const execa = require('execa') | ||
const chalk = require('chalk') | ||
const { fetchLatest, updateAvailable } = require('gh-release-fetch') | ||
const { | ||
NETLIFYDEVLOG, | ||
// NETLIFYDEVWARN, | ||
NETLIFYDEVERR, | ||
} = require('./logo') | ||
|
||
async function createTunnel(siteId, netlifyApiToken, log) { | ||
const { NETLIFYDEVLOG, NETLIFYDEVERR } = require('./logo') | ||
const { getPathInHome } = require('../lib/settings') | ||
const { shouldFetchLatestVersion, fetchLatestVersion } = require('../lib/exec-fetcher') | ||
|
||
const PACKAGE_NAME = 'live-tunnel-client' | ||
const EXEC_NAME = PACKAGE_NAME | ||
|
||
async function createTunnel({ siteId, netlifyApiToken, log }) { | ||
await installTunnelClient(log) | ||
|
||
if (!siteId) { | ||
|
@@ -43,8 +40,8 @@ async function createTunnel(siteId, netlifyApiToken, log) { | |
return data | ||
} | ||
|
||
async function connectTunnel(session, netlifyApiToken, localPort, log) { | ||
const execPath = path.join(os.homedir(), '.netlify', 'tunnel', 'bin', 'live-tunnel-client') | ||
async function connectTunnel({ session, netlifyApiToken, localPort, log }) { | ||
const execPath = getPathInHome(['tunnel', 'bin', EXEC_NAME]) | ||
const args = ['connect', '-s', session.id, '-t', netlifyApiToken, '-l', localPort] | ||
if (process.env.DEBUG) { | ||
args.push('-v') | ||
|
@@ -58,70 +55,38 @@ async function connectTunnel(session, netlifyApiToken, localPort, log) { | |
} | ||
|
||
async function installTunnelClient(log) { | ||
const win = isWindows() | ||
const binPath = path.join(os.homedir(), '.netlify', 'tunnel', 'bin') | ||
const execName = win ? 'live-tunnel-client.exe' : 'live-tunnel-client' | ||
const execPath = path.join(binPath, execName) | ||
const newVersion = await fetchTunnelClient(execPath) | ||
if (!newVersion) { | ||
const binPath = getPathInHome(['tunnel', 'bin']) | ||
const shouldFetch = await shouldFetchLatestVersion({ | ||
binPath, | ||
packageName: PACKAGE_NAME, | ||
execArgs: ['version'], | ||
pattern: `${PACKAGE_NAME}\\/v?([^\\s]+)`, | ||
execName: EXEC_NAME, | ||
}) | ||
if (!shouldFetch) { | ||
return | ||
} | ||
|
||
log(`${NETLIFYDEVLOG} Installing Live Tunnel Client`) | ||
|
||
const platform = win ? 'windows' : process.platform | ||
const extension = win ? 'zip' : 'tar.gz' | ||
const release = { | ||
repository: 'netlify/live-tunnel-client', | ||
package: `live-tunnel-client-${platform}-amd64.${extension}`, | ||
await fetchLatestVersion({ | ||
packageName: PACKAGE_NAME, | ||
execName: EXEC_NAME, | ||
destination: binPath, | ||
extract: true, | ||
} | ||
await fetchLatest(release) | ||
} | ||
|
||
async function fetchTunnelClient(execPath) { | ||
if (!execExist(execPath)) { | ||
return true | ||
} | ||
|
||
const { stdout } = await execa(execPath, ['version']) | ||
if (!stdout) { | ||
return false | ||
} | ||
|
||
const match = stdout.match(/^live-tunnel-client\/v?([^\s]+)/) | ||
if (!match) { | ||
return false | ||
} | ||
|
||
return updateAvailable('netlify/live-tunnel-client', match[1]) | ||
} | ||
|
||
function execExist(binPath) { | ||
if (!fs.existsSync(binPath)) { | ||
return false | ||
} | ||
const stat = fs.statSync(binPath) | ||
return stat && stat.isFile() && isExe(stat.mode, stat.gid, stat.uid) | ||
extension: process.platform === 'win32' ? 'zip' : 'tar.gz', | ||
}) | ||
} | ||
|
||
function isExe(mode, gid, uid) { | ||
if (isWindows()) { | ||
return true | ||
} | ||
const startLiveTunnel = async ({ siteId, netlifyApiToken, localPort, log }) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
const session = await createTunnel({ | ||
siteId, | ||
netlifyApiToken, | ||
log, | ||
}) | ||
|
||
const isGroup = gid ? process.getgid && gid === process.getgid() : true | ||
const isUser = uid ? process.getuid && uid === process.getuid() : true | ||
await connectTunnel({ session, netlifyApiToken, localPort, log }) | ||
|
||
return Boolean(mode & 0o0001 || (mode & 0o0010 && isGroup) || (mode & 0o0100 && isUser)) | ||
return session.session_url | ||
} | ||
|
||
function isWindows() { | ||
return process.platform === 'win32' | ||
} | ||
|
||
module.exports = { | ||
createTunnel, | ||
connectTunnel, | ||
} | ||
module.exports = { startLiveTunnel } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of the logic here was extracted from https://github.com/netlify/cli/blob/93468a3fc43db5d637b30b9cbb21042da8626962/src/utils/live-tunnel.js
And made generic so we can re-use it.