mirror of https://github.com/coder/coder.git
Compare commits
21 Commits
860dbd990b
...
0e36a375b6
Author | SHA1 | Date |
---|---|---|
Muhammad Atif Ali | 0e36a375b6 | |
Colin Adler | 15157c1c40 | |
Muhammad Atif Ali | 7d7da2a27d | |
Muhammad Atif Ali | 3c860571b5 | |
Muhammad Atif Ali | 41e640fab3 | |
Muhammad Atif Ali | a6c09189d4 | |
Muhammad Atif Ali | 5012cbd2ec | |
Muhammad Atif Ali | 0c06d4d7fd | |
Muhammad Atif Ali | d3517ad6b8 | |
Muhammad Atif Ali | 84517971f3 | |
Muhammad Atif Ali | a0734cb71e | |
Muhammad Atif Ali | ea73a13e95 | |
Muhammad Atif Ali | 92422f17fa | |
Muhammad Atif Ali | 167bcecc82 | |
Muhammad Atif Ali | b256fda35b | |
Muhammad Atif Ali | 4dc0c74c32 | |
Muhammad Atif Ali | 8485db1e27 | |
Muhammad Atif Ali | 493035781e | |
Muhammad Atif Ali | ec1cd97382 | |
Muhammad Atif Ali | ac24772df5 | |
Muhammad Atif Ali | f2874697f6 |
|
@ -17,7 +17,7 @@ runs:
|
|||
- name: Setup Node
|
||||
uses: buildjet/setup-node@v3
|
||||
with:
|
||||
node-version: 18.19.0
|
||||
node-version: 20.12.2
|
||||
# See https://github.com/actions/setup-node#caching-global-packages-data
|
||||
cache: "pnpm"
|
||||
cache-dependency-path: ${{ inputs.directory }}/pnpm-lock.yaml
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
deb [signed-by=/usr/share/keyrings/kubernetes.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /
|
|
@ -1 +1 @@
|
|||
deb [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main
|
||||
deb [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main
|
||||
|
|
Binary file not shown.
|
@ -73,4 +73,8 @@ curl "${curl_flags[@]}" "https://www.postgresql.org/media/keys/ACCC4CF8.asc" |
|
|||
curl "${curl_flags[@]}" "https://dl.yarnpkg.com/debian/pubkey.gpg" |
|
||||
gpg "${gpg_flags[@]}" --output="yarnpkg.gpg"
|
||||
|
||||
# Kubernetes signing key
|
||||
curl "${curl_flags[@]}" "https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key" |
|
||||
gpg "${gpg_flags[@]}" --output="kubernetes.gpg"
|
||||
|
||||
popd
|
||||
|
|
|
@ -73,11 +73,11 @@
|
|||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1712439257,
|
||||
"narHash": "sha256-aSpiNepFOMk9932HOax0XwNxbA38GOUVOiXfUVPOrck=",
|
||||
"lastModified": 1712791164,
|
||||
"narHash": "sha256-3sbWO1mbpWsLepZGbWaMovSO7ndZeFqDSdX0hZ9nVyw=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "ff0dbd94265ac470dda06a657d5fe49de93b4599",
|
||||
"rev": "1042fd8b148a9105f3c0aca3a6177fd1d9360ba5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
# Workaround for: terraform has an unfree license (‘bsl11’), refusing to evaluate.
|
||||
pkgs = import nixpkgs { inherit system; config.allowUnfree = true; };
|
||||
formatter = pkgs.nixpkgs-fmt;
|
||||
nodejs = pkgs.nodejs-18_x;
|
||||
nodejs = pkgs.nodejs_20;
|
||||
yarn = pkgs.yarn.override { inherit nodejs; };
|
||||
# Check in https://search.nixos.org/packages to find new packages.
|
||||
# Use `nix --extra-experimental-features nix-command --extra-experimental-features flakes flake update`
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/react": "2.8.0",
|
||||
"@emotion/react": "11",
|
||||
"@emotion/styled": "11",
|
||||
"@emotion/react": "11.11.4",
|
||||
"@emotion/styled": "11.11.5",
|
||||
"@types/lodash": "4.14.196",
|
||||
"archiver": "6.0.0",
|
||||
"framer-motion": "10",
|
||||
"framer-motion": "10.18.0",
|
||||
"front-matter": "4.0.2",
|
||||
"fs-extra": "11.2.0",
|
||||
"lodash": "4.17.21",
|
||||
|
@ -34,7 +34,7 @@
|
|||
"devDependencies": {
|
||||
"@react-native-community/eslint-config": "3.2.0",
|
||||
"@react-native-community/eslint-plugin": "1.3.0",
|
||||
"@types/node": "18.19.0",
|
||||
"@types/node": "20.12.2",
|
||||
"@types/react": "18.2.17",
|
||||
"@types/react-dom": "18.2.7",
|
||||
"eslint": "8.56.0",
|
||||
|
@ -43,7 +43,7 @@
|
|||
"typescript": "5.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"npm": ">=9.0.0 <10.0.0",
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
"npm": ">=10.0.0 <11.0.0",
|
||||
"node": ">=20.0.0 <21.0.0"
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -15,40 +15,42 @@ dependencies:
|
|||
|
||||
packages:
|
||||
|
||||
/@babel/code-frame@7.22.5:
|
||||
resolution: {integrity: sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==}
|
||||
/@babel/code-frame@7.24.2:
|
||||
resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/highlight': 7.22.5
|
||||
'@babel/highlight': 7.24.2
|
||||
picocolors: 1.0.0
|
||||
dev: false
|
||||
|
||||
/@babel/helper-validator-identifier@7.22.5:
|
||||
resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==}
|
||||
/@babel/helper-validator-identifier@7.22.20:
|
||||
resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dev: false
|
||||
|
||||
/@babel/highlight@7.22.5:
|
||||
resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==}
|
||||
/@babel/highlight@7.24.2:
|
||||
resolution: {integrity: sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@babel/helper-validator-identifier': 7.22.5
|
||||
'@babel/helper-validator-identifier': 7.22.20
|
||||
chalk: 2.4.2
|
||||
js-tokens: 4.0.0
|
||||
picocolors: 1.0.0
|
||||
dev: false
|
||||
|
||||
/@babel/runtime@7.22.6:
|
||||
resolution: {integrity: sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==}
|
||||
/@babel/runtime@7.24.4:
|
||||
resolution: {integrity: sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
regenerator-runtime: 0.13.11
|
||||
regenerator-runtime: 0.14.1
|
||||
dev: false
|
||||
|
||||
/@exodus/schemasafe@1.0.1:
|
||||
resolution: {integrity: sha512-PQdbF8dGd4LnbwBlcc4ML8RKYdplm+e9sUeWBTr4zgF13/Shiuov9XznvM4T8cb1CfyKK21yTUkuAIIh/DAH/g==}
|
||||
/@exodus/schemasafe@1.3.0:
|
||||
resolution: {integrity: sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==}
|
||||
dev: false
|
||||
|
||||
/@types/json-schema@7.0.12:
|
||||
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
|
||||
/@types/json-schema@7.0.15:
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
dev: false
|
||||
|
||||
/ajv@5.5.2:
|
||||
|
@ -118,11 +120,11 @@ packages:
|
|||
peerDependencies:
|
||||
ajv: 4.11.8 - 6
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.22.5
|
||||
'@babel/runtime': 7.22.6
|
||||
'@babel/code-frame': 7.24.2
|
||||
'@babel/runtime': 7.24.4
|
||||
ajv: 5.5.2
|
||||
chalk: 2.4.2
|
||||
core-js: 3.31.0
|
||||
core-js: 3.36.1
|
||||
json-to-ast: 2.1.0
|
||||
jsonpointer: 5.0.1
|
||||
leven: 3.1.0
|
||||
|
@ -133,11 +135,11 @@ packages:
|
|||
peerDependencies:
|
||||
ajv: 4.11.8 - 6
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.22.5
|
||||
'@babel/runtime': 7.22.6
|
||||
'@babel/code-frame': 7.24.2
|
||||
'@babel/runtime': 7.24.4
|
||||
ajv: 6.12.6
|
||||
chalk: 2.4.2
|
||||
core-js: 3.31.0
|
||||
core-js: 3.36.1
|
||||
json-to-ast: 2.1.0
|
||||
jsonpointer: 5.0.1
|
||||
leven: 3.1.0
|
||||
|
@ -244,8 +246,8 @@ packages:
|
|||
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
|
||||
dev: false
|
||||
|
||||
/core-js@3.31.0:
|
||||
resolution: {integrity: sha512-NIp2TQSGfR6ba5aalZD+ZQ1fSxGhDo/s1w0nx3RYzf2pnJxt7YynxFlFScP6eV7+GZsKO95NSjGxyJsU3DZgeQ==}
|
||||
/core-js@3.36.1:
|
||||
resolution: {integrity: sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
|
@ -309,8 +311,8 @@ packages:
|
|||
resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==}
|
||||
dev: false
|
||||
|
||||
/escalade@3.1.1:
|
||||
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
|
||||
/escalade@3.1.2:
|
||||
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
|
@ -695,8 +697,8 @@ packages:
|
|||
http2-client: 1.3.5
|
||||
dev: false
|
||||
|
||||
/node-fetch@2.6.12:
|
||||
resolution: {integrity: sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==}
|
||||
/node-fetch@2.7.0:
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
|
@ -734,7 +736,7 @@ packages:
|
|||
/oas-linter@3.2.2:
|
||||
resolution: {integrity: sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==}
|
||||
dependencies:
|
||||
'@exodus/schemasafe': 1.0.1
|
||||
'@exodus/schemasafe': 1.3.0
|
||||
should: 13.2.3
|
||||
yaml: 1.10.2
|
||||
dev: false
|
||||
|
@ -775,10 +777,10 @@ packages:
|
|||
wrappy: 1.0.2
|
||||
dev: false
|
||||
|
||||
/openapi-sampler@1.3.1:
|
||||
resolution: {integrity: sha512-Ert9mvc2tLPmmInwSyGZS+v4Ogu9/YoZuq9oP3EdUklg2cad6+IGndP9yqJJwbgdXwZibiq5fpv6vYujchdJFg==}
|
||||
/openapi-sampler@1.4.0:
|
||||
resolution: {integrity: sha512-3FKJQCHAMG9T7RsRy9u5Ft4ERPq1QQmn77C8T3OSofYL9uur59AqychvQ0YQKijrqRwIkAbzkh+nQnAE3gjMVA==}
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.12
|
||||
'@types/json-schema': 7.0.15
|
||||
json-pointer: 0.6.2
|
||||
dev: false
|
||||
|
||||
|
@ -853,6 +855,10 @@ packages:
|
|||
through: 2.3.8
|
||||
dev: false
|
||||
|
||||
/picocolors@1.0.0:
|
||||
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
||||
dev: false
|
||||
|
||||
/pinkie-promise@1.0.0:
|
||||
resolution: {integrity: sha512-5mvtVNse2Ml9zpFKkWBpGsTPwm3DKhs+c95prO/F6E7d6DN0FPqxs6LONpLNpyD7Iheb7QN4BbUoKJgo+DnkQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -884,8 +890,8 @@ packages:
|
|||
once: 1.4.0
|
||||
dev: false
|
||||
|
||||
/punycode@2.3.0:
|
||||
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
|
||||
/punycode@2.3.1:
|
||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
|
@ -893,8 +899,8 @@ packages:
|
|||
resolution: {integrity: sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==}
|
||||
dev: false
|
||||
|
||||
/regenerator-runtime@0.13.11:
|
||||
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
|
||||
/regenerator-runtime@0.14.1:
|
||||
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||
dev: false
|
||||
|
||||
/require-directory@2.1.1:
|
||||
|
@ -1099,7 +1105,7 @@ packages:
|
|||
/uri-js@4.4.1:
|
||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||
dependencies:
|
||||
punycode: 2.3.0
|
||||
punycode: 2.3.1
|
||||
dev: false
|
||||
|
||||
/urijs@1.19.11:
|
||||
|
@ -1139,10 +1145,10 @@ packages:
|
|||
jgexml: 0.4.4
|
||||
markdown-it: 10.0.0
|
||||
markdown-it-emoji: 1.4.0
|
||||
node-fetch: 2.6.12
|
||||
node-fetch: 2.7.0
|
||||
oas-resolver: 2.5.6
|
||||
oas-schema-walker: 1.1.5
|
||||
openapi-sampler: 1.3.1
|
||||
openapi-sampler: 1.4.0
|
||||
reftools: 1.1.9
|
||||
swagger2openapi: 6.2.3(ajv@6.12.6)
|
||||
urijs: 1.19.11
|
||||
|
@ -1262,7 +1268,7 @@ packages:
|
|||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
cliui: 8.0.1
|
||||
escalade: 3.1.1
|
||||
escalade: 3.1.2
|
||||
get-caller-file: 2.0.5
|
||||
require-directory: 2.1.1
|
||||
string-width: 4.2.3
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
"@tanstack/react-query-devtools": "4.35.3",
|
||||
"ansi-to-html": "0.7.2",
|
||||
"axios": "1.6.0",
|
||||
"canvas": "2.11.0",
|
||||
"canvas": "2.11.2",
|
||||
"chart.js": "4.4.0",
|
||||
"chartjs-adapter-date-fns": "3.0.0",
|
||||
"chartjs-plugin-annotation": "3.0.1",
|
||||
|
@ -86,12 +86,12 @@
|
|||
"undici": "6.7.1",
|
||||
"unique-names-generator": "4.7.1",
|
||||
"uuid": "9.0.0",
|
||||
"xterm": "5.2.0",
|
||||
"xterm-addon-canvas": "0.5.0",
|
||||
"xterm-addon-fit": "0.8.0",
|
||||
"xterm-addon-unicode11": "0.6.0",
|
||||
"xterm-addon-web-links": "0.9.0",
|
||||
"xterm-addon-webgl": "0.16.0",
|
||||
"@xterm/xterm": "5.5.0",
|
||||
"@xterm/addon-canvas": "0.7.0",
|
||||
"@xterm/addon-fit": "0.10.0",
|
||||
"@xterm/addon-unicode11": "0.8.0",
|
||||
"@xterm/addon-web-links": "0.11.0",
|
||||
"@xterm/addon-webgl": "0.18.0",
|
||||
"yup": "1.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -118,7 +118,7 @@
|
|||
"@types/express": "4.17.17",
|
||||
"@types/jest": "29.5.2",
|
||||
"@types/lodash": "4.14.196",
|
||||
"@types/node": "18.19.0",
|
||||
"@types/node": "20.12.1",
|
||||
"@types/react": "18.2.6",
|
||||
"@types/react-color": "3.0.6",
|
||||
"@types/react-date-range": "1.4.4",
|
||||
|
@ -183,7 +183,7 @@
|
|||
"semver": "7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"npm": ">=9.0.0 <10.0.0",
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
"npm": ">=10.0.0 <11.0.0",
|
||||
"node": ">=20.0.0 <21.0.0"
|
||||
}
|
||||
}
|
||||
|
|
5221
site/pnpm-lock.yaml
5221
site/pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -1,16 +1,16 @@
|
|||
import "xterm/css/xterm.css";
|
||||
import "@xterm/xterm/css/xterm.css";
|
||||
import type { Interpolation, Theme } from "@emotion/react";
|
||||
import { CanvasAddon } from "@xterm/addon-canvas";
|
||||
import { FitAddon } from "@xterm/addon-fit";
|
||||
import { Unicode11Addon } from "@xterm/addon-unicode11";
|
||||
import { WebLinksAddon } from "@xterm/addon-web-links";
|
||||
import { WebglAddon } from "@xterm/addon-webgl";
|
||||
import * as XTerm from "@xterm/xterm";
|
||||
import { type FC, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useQuery } from "react-query";
|
||||
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import * as XTerm from "xterm";
|
||||
import { CanvasAddon } from "xterm-addon-canvas";
|
||||
import { FitAddon } from "xterm-addon-fit";
|
||||
import { Unicode11Addon } from "xterm-addon-unicode11";
|
||||
import { WebLinksAddon } from "xterm-addon-web-links";
|
||||
import { WebglAddon } from "xterm-addon-webgl";
|
||||
import { deploymentConfig } from "api/queries/deployment";
|
||||
import { workspaceByOwnerAndName } from "api/queries/workspaces";
|
||||
import { useProxy } from "contexts/ProxyContext";
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/xerrors"
|
||||
"nhooyr.io/websocket"
|
||||
"tailscale.com/tailcfg"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/tailnet"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
func NetworkSetupDefault(*testing.T) {}
|
||||
|
||||
func DERPMapTailscale(ctx context.Context, t *testing.T) *tailcfg.DERPMap {
|
||||
ctx, cancel := context.WithTimeout(ctx, testutil.WaitShort)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://controlplane.tailscale.com/derpmap/default", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
defer res.Body.Close()
|
||||
|
||||
dm := &tailcfg.DERPMap{}
|
||||
dec := json.NewDecoder(res.Body)
|
||||
err = dec.Decode(dm)
|
||||
require.NoError(t, err)
|
||||
|
||||
return dm
|
||||
}
|
||||
|
||||
func CoordinatorInMemory(t *testing.T, logger slog.Logger, dm *tailcfg.DERPMap) (coord tailnet.Coordinator, url string) {
|
||||
coord = tailnet.NewCoordinator(logger)
|
||||
var coordPtr atomic.Pointer[tailnet.Coordinator]
|
||||
coordPtr.Store(&coord)
|
||||
t.Cleanup(func() { _ = coord.Close() })
|
||||
|
||||
csvc, err := tailnet.NewClientService(logger, &coordPtr, 10*time.Minute, func() *tailcfg.DERPMap {
|
||||
return dm
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
idStr := strings.TrimPrefix(r.URL.Path, "/")
|
||||
id, err := uuid.Parse(idStr)
|
||||
if err != nil {
|
||||
httpapi.Write(r.Context(), w, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Bad agent id.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := websocket.Accept(w, r, nil)
|
||||
if err != nil {
|
||||
httpapi.Write(r.Context(), w, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Failed to accept websocket.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx, wsNetConn := codersdk.WebsocketNetConn(r.Context(), conn, websocket.MessageBinary)
|
||||
defer wsNetConn.Close()
|
||||
|
||||
err = csvc.ServeConnV2(ctx, wsNetConn, tailnet.StreamID{
|
||||
Name: "client-" + id.String(),
|
||||
ID: id,
|
||||
Auth: tailnet.SingleTailnetCoordinateeAuth{},
|
||||
})
|
||||
if err != nil && !xerrors.Is(err, io.EOF) && !xerrors.Is(err, context.Canceled) {
|
||||
_ = conn.Close(websocket.StatusInternalError, err.Error())
|
||||
return
|
||||
}
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
return coord, srv.URL
|
||||
}
|
||||
|
||||
func TailnetSetupDRPC(ctx context.Context, t *testing.T, logger slog.Logger,
|
||||
id, agentID uuid.UUID,
|
||||
coordinateURL string,
|
||||
dm *tailcfg.DERPMap,
|
||||
) *tailnet.Conn {
|
||||
ip := tailnet.IPFromUUID(id)
|
||||
conn, err := tailnet.NewConn(&tailnet.Options{
|
||||
Addresses: []netip.Prefix{netip.PrefixFrom(ip, 128)},
|
||||
DERPMap: dm,
|
||||
Logger: logger,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = conn.Close() })
|
||||
|
||||
//nolint:bodyclose
|
||||
ws, _, err := websocket.Dial(ctx, coordinateURL+"/"+id.String(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
client, err := tailnet.NewDRPCClient(
|
||||
websocket.NetConn(ctx, ws, websocket.MessageBinary),
|
||||
logger,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
coord, err := client.Coordinate(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
coordination := tailnet.NewRemoteCoordination(logger, coord, conn, agentID)
|
||||
t.Cleanup(func() { _ = coordination.Close() })
|
||||
return conn
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/tailcfg"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
"github.com/coder/coder/v2/tailnet"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
var (
|
||||
isChild = flag.Bool("child", false, "Run tests as a child")
|
||||
childTestID = flag.Int("child-test-id", 0, "Which test is being run")
|
||||
childCoordinateURL = flag.String("child-coordinate-url", "", "The coordinate url to connect back to")
|
||||
childAgentID = flag.String("child-agent-id", "", "The agent id of the child")
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if run := os.Getenv("CODER_TAILNET_TESTS"); run == "" {
|
||||
_, _ = fmt.Println("skipping tests...")
|
||||
return
|
||||
}
|
||||
if os.Getuid() != 0 {
|
||||
_, _ = fmt.Println("networking integration tests must run as root")
|
||||
return
|
||||
}
|
||||
flag.Parse()
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
var tests = []Test{{
|
||||
Name: "Normal",
|
||||
DERPMap: DERPMapTailscale,
|
||||
Coordinator: CoordinatorInMemory,
|
||||
Parent: Parent{
|
||||
NetworkSetup: NetworkSetupDefault,
|
||||
TailnetSetup: TailnetSetupDRPC,
|
||||
Run: func(ctx context.Context, t *testing.T, opts ParentOpts) {
|
||||
reach := opts.Conn.AwaitReachable(ctx, tailnet.IPFromUUID(opts.AgentID))
|
||||
assert.True(t, reach)
|
||||
},
|
||||
},
|
||||
Child: Child{
|
||||
NetworkSetup: NetworkSetupDefault,
|
||||
TailnetSetup: TailnetSetupDRPC,
|
||||
Run: func(ctx context.Context, t *testing.T, opts ChildOpts) {
|
||||
// wait until the parent kills us
|
||||
<-make(chan struct{})
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
//nolint:paralleltest
|
||||
func TestIntegration(t *testing.T) {
|
||||
if *isChild {
|
||||
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitSuperLong)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
agentID, err := uuid.Parse(*childAgentID)
|
||||
require.NoError(t, err)
|
||||
|
||||
test := tests[*childTestID]
|
||||
test.Child.NetworkSetup(t)
|
||||
dm := test.DERPMap(ctx, t)
|
||||
conn := test.Child.TailnetSetup(ctx, t, logger, agentID, uuid.Nil, *childCoordinateURL, dm)
|
||||
test.Child.Run(ctx, t, ChildOpts{
|
||||
Logger: logger,
|
||||
Conn: conn,
|
||||
AgentID: agentID,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
for id, test := range tests {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitSuperLong)
|
||||
t.Cleanup(cancel)
|
||||
|
||||
logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug)
|
||||
parentID, childID := uuid.New(), uuid.New()
|
||||
dm := test.DERPMap(ctx, t)
|
||||
_, coordURL := test.Coordinator(t, logger, dm)
|
||||
|
||||
child, waitChild := execChild(ctx, id, coordURL, childID)
|
||||
test.Parent.NetworkSetup(t)
|
||||
conn := test.Parent.TailnetSetup(ctx, t, logger, parentID, childID, coordURL, dm)
|
||||
test.Parent.Run(ctx, t, ParentOpts{
|
||||
Logger: logger,
|
||||
Conn: conn,
|
||||
ClientID: parentID,
|
||||
AgentID: childID,
|
||||
})
|
||||
child.Process.Signal(syscall.SIGINT)
|
||||
<-waitChild
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type Test struct {
|
||||
// Name is the name of the test.
|
||||
Name string
|
||||
|
||||
// DERPMap returns the DERP map to use for both the parent and child. It is
|
||||
// called once at the beginning of the test.
|
||||
DERPMap func(ctx context.Context, t *testing.T) *tailcfg.DERPMap
|
||||
// Coordinator returns a running tailnet coordinator, and the url to reach
|
||||
// it on.
|
||||
Coordinator func(t *testing.T, logger slog.Logger, dm *tailcfg.DERPMap) (coord tailnet.Coordinator, url string)
|
||||
|
||||
Parent Parent
|
||||
Child Child
|
||||
}
|
||||
|
||||
// Parent is the struct containing all of the parent specific configurations.
|
||||
// Functions are invoked in order of struct definition.
|
||||
type Parent struct {
|
||||
// NetworkSetup is run before all test code. It can be used to setup
|
||||
// networking scenarios.
|
||||
NetworkSetup func(t *testing.T)
|
||||
|
||||
// TailnetSetup creates a tailnet network.
|
||||
TailnetSetup func(
|
||||
ctx context.Context, t *testing.T, logger slog.Logger,
|
||||
id, agentID uuid.UUID, coordURL string, dm *tailcfg.DERPMap,
|
||||
) *tailnet.Conn
|
||||
|
||||
Run func(ctx context.Context, t *testing.T, opts ParentOpts)
|
||||
}
|
||||
|
||||
// Child is the struct containing all of the child specific configurations.
|
||||
// Functions are invoked in order of struct definition.
|
||||
type Child struct {
|
||||
// NetworkSetup is run before all test code. It can be used to setup
|
||||
// networking scenarios.
|
||||
NetworkSetup func(t *testing.T)
|
||||
|
||||
// TailnetSetup creates a tailnet network.
|
||||
TailnetSetup func(
|
||||
ctx context.Context, t *testing.T, logger slog.Logger,
|
||||
id, agentID uuid.UUID, coordURL string, dm *tailcfg.DERPMap,
|
||||
) *tailnet.Conn
|
||||
|
||||
// Run runs the actual test. Parents and children run in separate processes,
|
||||
// so it's important to ensure no communication happens over memory between
|
||||
// run functions of parents and children.
|
||||
Run func(ctx context.Context, t *testing.T, opts ChildOpts)
|
||||
}
|
||||
|
||||
type ParentOpts struct {
|
||||
Logger slog.Logger
|
||||
Conn *tailnet.Conn
|
||||
ClientID uuid.UUID
|
||||
AgentID uuid.UUID
|
||||
}
|
||||
|
||||
type ChildOpts struct {
|
||||
Logger slog.Logger
|
||||
Conn *tailnet.Conn
|
||||
AgentID uuid.UUID
|
||||
}
|
||||
|
||||
func execChild(ctx context.Context, testID int, coordURL string, agentID uuid.UUID) (*exec.Cmd, <-chan error) {
|
||||
ch := make(chan error)
|
||||
binary := os.Args[0]
|
||||
args := os.Args[1:]
|
||||
args = append(args,
|
||||
"--child=true",
|
||||
"--child-test-id="+strconv.Itoa(testID),
|
||||
"--child-coordinate-url="+coordURL,
|
||||
"--child-agent-id="+agentID.String(),
|
||||
)
|
||||
|
||||
cmd := exec.CommandContext(ctx, binary, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
go func() {
|
||||
ch <- cmd.Run()
|
||||
}()
|
||||
return cmd, ch
|
||||
}
|
Loading…
Reference in New Issue