Compare commits

...

6 commits

Author SHA1 Message Date
Richard Koliser
f52ace270f Merge branch 'feature/update_1.19.0' into 'release/1.19.0'
Update wizard for Kasm Workspaces 1.19.0 rolling changes

See merge request kasm-technologies/internal/kasm_install_wizard!6
2026-06-25 19:00:38 +00:00
Richard Koliser
6b7640c261
Fix registry bug in copied file. 2026-06-18 23:00:31 -04:00
Richard Koliser
6cf97ecc22
KASM-1190 Fix image seed fields for 1.19.0 Image model
The Image model renamed memory to memory_bytes in 1.19.0. Update the
workspace image objects generated by fetchWorkspaceList to use
memory_bytes. Also add gpu_graphics_acceleration and gpu_video_encoding
fields to match the official seed YAML format.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-06-17 16:50:37 -04:00
Richard Koliser
1f82bbcb21
KASM-1190 Refresh workspace registry from registry.kasmweb.com
Pull latest list.json from the live registry (85 workspaces, up from
79). 71 workspaces now have 1.19.x compat entries with
1.19.0-rolling-weekly and 1.19.0-rolling-daily tags. Retires Parrot OS
(replaced by Parrot OS 6 and Parrot OS 7). Adds Alpine 3.22, Alpine
3.23, Fedora 42, Fedora 43, OpenSUSE Leap 16, Parrot OS 6, and
Parrot OS 7.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-06-17 16:47:58 -04:00
Richard Koliser
0774b59deb
KASM-1190 Remove legacy GPU seeding in favour of 1.19.0 agent management
In 1.19.0, the provision agent dynamically assigns GPUs to containers
using capability probing (EGL, DRI3, VAAPI, NVENC, VULKAN) and
load-balancing across devices. Pre-seeding GPU run_config/exec_config
into workspace images at install time conflicts with this system and
uses removed env var names (HW3D/DRINODE replaced by
KVNC_DESKTOP_GPU_HW3D/KVNC_DESKTOP_GPU_DRINODE).

Remove setGpu(), the gpuinfo.sh detection block, the GPU yaml-merge
call in install(), and the "Use GPU on all images" install UI option.
Replace the UI option with a notice directing admins to configure GPU
acceleration in the admin panel post-install.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-06-17 14:16:12 -04:00
Richard Koliser
64f3768748
KASM-1190 Update wizard for Kasm Workspaces 1.19.0 rolling changes
Rolling is now the default in 1.19.0's install.sh, so the wizard no
longer needs to patch service image tags manually. Remove
appendRollingToServiceImages() and both call sites (install and
upgrade paths). Update the version fallback from 1.18.1 to 1.19.0.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-06-17 13:53:39 -04:00
3 changed files with 5445 additions and 113 deletions

107
index.js
View file

@ -19,12 +19,11 @@ const docker = new Docker({socketPath: '/var/run/docker.sock'});
const arch = os.arch().replace('x64', 'amd64');
const baseUrl = process.env.SUBFOLDER || '/';
const port = process.env.KASM_PORT || '443';
const { spawn } = require('node:child_process');
let EULA;
let images;
let currentVersion;
let sourceLocal = false;
let gpuInfo;
let gpuInfo = {};
let installSettings = {};
let upgradeSettings = {};
@ -48,7 +47,7 @@ function matchVersion(currentVer, compatibilityList) {
}
// Build the image tag for a given compat entry.
// Returns e.g. "1.18.1-rolling-weekly", falling back to "develop".
// Returns e.g. "1.19.0-rolling-weekly", falling back to "develop".
function buildImageTag(compat) {
const compatTag = compat.image.split(':')[1] || '';
const versionPrefix = compatTag.replace(/-[^-]+-[^-]+$/, ''); // strip last two dash-segments
@ -70,7 +69,7 @@ async function fetchListData() {
}
// Return the rolling-weekly tag string for the current version by finding
// the first compatible workspace in the registry (e.g. "1.18.1-rolling-weekly").
// the first compatible workspace in the registry (e.g. "1.19.0-rolling-weekly").
async function getRollingWeeklyTag(currentVer, archName) {
const listData = await fetchListData();
const ws = (listData.workspaces || []).find(
@ -109,12 +108,14 @@ async function fetchWorkspaceList(currentVer, archName) {
exec_config: {},
friendly_name: ws.friendly_name,
gpu_count: 0,
gpu_graphics_acceleration: ['MESA'],
gpu_video_encoding: ['SW'],
hidden: false,
image_id: '${uuid:image_id:' + imageIdx + '}',
image_src: 'https://registry.kasmweb.com/1.1/icons/' + ws.image_src,
image_type: 'Container',
launch_config: {},
memory: 2768000000,
memory_bytes: 2768000000,
name: imageName,
notes: ws.notes || null,
run_config: {},
@ -142,97 +143,12 @@ async function installerBlobs() {
try {
currentVersion = fs.readFileSync('/version.txt', 'utf8').replace(/(\r\n|\n|\r)/gm,'');
} catch (err) {
currentVersion = '1.18.1';
currentVersion = '1.19.0';
}
images = await fetchWorkspaceList(currentVersion, arch);
gpuInfo = {};
await new Promise((resolve) => {
let gpuData = [];
let gpuCmd = spawn('/gpuinfo.sh');
gpuCmd.stdout.on('data', function(data) {
gpuData.push(data);
});
gpuCmd.on('close', function(code) {
try {
if (code == 0) {
gpuInfo = JSON.parse(gpuData.join(''));
} else {
gpuInfo = {};
}
} catch (err) {
// Manually backfill GPU info if available
gpuInfo = {};
for (let i = 0; i < 10; i++) {
let num = i.toString();
if (fs.existsSync('/dev/dri/card' + num)) {
gpuInfo['/dev/dri/card' + num] = "Unknown GPU";
}
}
}
resolve();
});
});
}
installerBlobs();
// GPU image yaml merging
async function setGpu(imagesI) {
if (upgradeSettings['forceGpu'] !== undefined) {
installSettings = upgradeSettings;
}
const forceGpu = installSettings.forceGpu;
if (!forceGpu || forceGpu === 'disabled' || !forceGpu.includes('|')) {
console.error('setGpu: invalid or missing forceGpu value:', forceGpu);
return imagesI;
}
const [gpu, gpuName] = forceGpu.split('|');
if (!gpu || !gpuName) {
console.error('setGpu: could not parse GPU path or name from forceGpu:', forceGpu);
return imagesI;
}
const card = gpu.slice(-1);
const render = (Number(card) + 128).toString();
// Handle NVIDIA Gpus
let baseRun;
if (gpuName.includes('NVIDIA')) {
baseRun = JSON.parse('{"runtime":"nvidia","environment":{"NVIDIA_DRIVER_CAPABILITIES":"all","KASM_EGL_CARD":"/dev/dri/card' + card + '","KASM_RENDERD":"/dev/dri/renderD' + render + '"},"device_requests":[{"driver": "","count": -1,"device_ids": null,"capabilities":[["gpu"]],"options":{}}]}');
} else {
baseRun = JSON.parse('{"environment":{"DRINODE":"/dev/dri/renderD' + render + '", "HW3D": true},"devices":["/dev/dri/card' + card + ':/dev/dri/card' + card + ':rwm","/dev/dri/renderD' + render + ':/dev/dri/renderD' + render + ':rwm"]}');
}
let baseExec = JSON.parse('{"first_launch":{"user":"root","cmd": "bash -c \'chown -R kasm-user:kasm-user /dev/dri/*\'"}}');
for (let i=0; i<imagesI.images.length; i++) {
console.log(imagesI.images[i]['run_config']);
let finalRun = _.merge(imagesI.images[i]['run_config'], baseRun)
let finalExec = _.merge(imagesI.images[i]['exec_config'], baseExec)
imagesI.images[i]['run_config'] = finalRun;
imagesI.images[i]['exec_config'] = finalExec;
}
return imagesI;
}
// For rolling-weekly installs, append -rolling to service image tags in docker conf yamls.
// Prevents -rolling-rolling by only modifying tags that don't already end with -rolling.
async function appendRollingToServiceImages() {
const confDir = '/kasm_release/docker';
let entries;
try {
entries = await fsw.readdir(confDir);
} catch (err) {
return;
}
for (const file of entries.filter(f => f.endsWith('.yaml'))) {
const filePath = confDir + '/' + file;
const content = await fsw.readFile(filePath, 'utf8');
const updated = content.split('\n').map(line => {
if (/ image:/.test(line) && /"$/.test(line) && !/-rolling"$/.test(line) && !/develop"$/.test(line)) {
return line.replace(/"$/, '-rolling"');
}
return line;
}).join('\n');
await fsw.writeFile(filePath, updated);
}
}
//// Http server ////
baserouter.use('/public', express.static(__dirname + '/public'));
baserouter.get("/", function (req, res) {
@ -257,18 +173,12 @@ io.on('connection', async function (socket) {
installFlags.push('-b');
}
// GPU yaml merge
if (installSettings.forceGpu !== 'disabled' && imagesI && imagesI.images) {
imagesI = await setGpu(imagesI);
}
// Write finalized image data
let yamlStr = yaml.dump(imagesI);
if (yamlStr.startsWith("false")) {
installFlags = installFlags.filter(function(e) { return e !== '-W' });
} else {
await fsw.writeFile('/kasm_release/conf/database/seed_data/default_images_' + arch + '.yaml', yamlStr);
await appendRollingToServiceImages();
}
// Copy over version
@ -290,9 +200,6 @@ io.on('connection', async function (socket) {
async function upgrade(data) {
let upgradeFlags = ['/kasm_release/upgrade.sh', '-L', port];
// Patch service image tags for rolling builds
await appendRollingToServiceImages();
// Run upgrade
let cmd = pty.spawn('/bin/bash', upgradeFlags);
cmd.on('data', function(data) {

5437
list.json

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,6 @@
// Variables
let EULA;
let images;
let gpus;
let term;
let installImages = [];
const installSettings = {};
@ -93,7 +92,6 @@ function renderInstall(data) {
titleChange('EULA');
EULA = data[0];
images = data[1];
gpus = data[2] || {};
versionChange(data[3], data[4]);
let EULADiv = $('<div>', {id: 'EULA'}).text(EULA);
$('#container').append(EULADiv);
@ -230,14 +228,7 @@ async function pickSettings() {
$('<label>', {for: 'userPass'}).text('user@kasm.local Password: '),
$('<input>', {name: 'userPass', id: 'userPass', type: 'password', required: true, placeholder: 'required'})
]);
let gpuOptions = [$('<option>', {value: 'disabled'}).text('Disabled')];
for (let card of Object.keys(gpus || {})) {
gpuOptions.push($('<option>', {value: card + '|' + gpus[card]}).text(card + ' - ' + gpus[card]));
}
let forceGpu = $('<div>', {class: 'form-group'}).append([
$('<label>', {for: 'forceGpu'}).text('Use GPU on all images: '),
$('<select>', {name: 'forceGpu', id: 'forceGpu',}).append(gpuOptions)
]);
let gpuNotice = $('<p>').text('GPU acceleration is automatically managed by Kasm Workspaces 1.19.0. Configure GPU settings after installation in the admin panel under Infrastructure > Agents.');
let submit = $('<div>', {class: 'form-group'}).append([
$('<input>', {name: 'submit', type: 'submit', value: 'Next', class: 'btn btn-default btn-ghost'})
]);
@ -249,7 +240,7 @@ async function pickSettings() {
passHint,
adminPass,
userPass,
forceGpu,
gpuNotice,
submit
]);
form.append(fieldset);
@ -267,7 +258,6 @@ async function pickSettings() {
$('#pass-hint').css('color', 'inherit');
installSettings.adminPass = adminPassVal;
installSettings.userPass = userPassVal;
installSettings.forceGpu = $('#forceGpu').val();
pickImages(false);
});
}