Compare commits

..

12 Commits
1.0 ... 1.1

Author SHA1 Message Date
Chris Hunt
68bc35fd49
Mention updating build_all_branches in the 1.0 branch as well 2024-09-12 14:19:19 +01:00
Chris Hunt
3fff8d24d8
Update readme and use previous size with update script 2024-08-21 12:47:52 +01:00
Chris Hunt
b86eb26acb
Add versions.json 2024-08-20 11:26:36 +01:00
Chris Hunt
a420ec4c28
Update text to use 1.16 2024-08-20 11:06:48 +01:00
Chris Hunt
aa0da08392
Add details about the compatibility property 2024-08-20 10:36:06 +01:00
Chris Hunt
53465c78b9
Change workspace count 2024-08-14 13:45:49 +01:00
Chris Hunt
39bac68494
Updates to files and upgrade advice 2024-08-13 17:37:46 +01:00
Chris Hunt
225926bdc1
Add missing variable 2024-08-13 11:25:38 +01:00
Chris Hunt
687738b75b
Update defaults 2024-08-13 11:05:25 +01:00
Chris Hunt
5278b3914d
Update readme 2024-08-13 10:40:21 +01:00
Chris Hunt
8ddd8aabe0
Add new files and put static version back to 1.0 2024-08-13 10:31:16 +01:00
Chris Hunt
4476528522
Add 1.1 support 2024-08-09 14:12:12 +01:00
9 changed files with 271 additions and 24 deletions

105
README.md
View File

@ -88,7 +88,7 @@ Click on the **Actions** tab in the top menu and check whether workflows need en
| --------- | ----------- |
| env.name | The name you want to display for your registry. |
| env.description | A short description to display when a store's information button is pressed. |
| env.icon | The image to display for your registry. You can upload an image to `/site/public/` and reference that by https://domain.com/1.0/image.png or if you aren't using a {sub}domain by referencing it from https://username.github.io/repositoryname/1.0/image.png where image.png is the name of the image you uploaded. Alternatively just put the url of an image available on the web. If you just want to get the registry up and working, leave the default value in place until later. |
| env.icon | The image to display for your registry. You can upload an image to `/site/public/` and reference that by https://domain.com/1.1/image.png or if you aren't using a {sub}domain by referencing it from https://username.github.io/repositoryname/1.1/image.png where image.png is the name of the image you uploaded. Alternatively just put the url of an image available on the web. If you just want to get the registry up and working, leave the default value in place until later. |
| env.listUrl | The link to the root of your site. For example https://username.github.io/repositoryname/ it should always include a trailing slash. |
| env.contactUrl | A link users can use to contact you on, such as your github issues page (right click the **Issues** tab in the top menu - next to the **Code** tab - and select `copy link address` and paste that in). |
| basePath | If you are using a domain or a subdomain, your basePath will just be `basePath: '/1.0',`, otherwise change the value to include what you chose for the repository name in step 2 `basePath: '/repositoryname/1.0',`. **The `1.0` will be replaced with the branch name automatically, so you should always keep it as 1.0.** |
@ -182,7 +182,6 @@ Workspace Name
{
"description": "Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud applications.",
"docker_registry": "https://index.docker.io/v1/",
"name": "kasmweb/vs-code:develop",
"image_src": "vs-code.png",
"categories": [
"Development"
@ -193,9 +192,26 @@ Workspace Name
"arm64"
],
"compatibility": [
"1.13.x"
],
"uncompressed_size_mb": 2170
{
"version": "1.16.x",
"image": "kasmweb/vs-code:1.16",
"uncompressed_size_mb": 2428,
"available_tags": [
"develop",
"1.16.0"
]
},
{
"version": "1.17.x",
"image": "kasmweb/vs-code:1.17",
"uncompressed_size_mb": 2528,
"available_tags": [
"develop",
"1.17.0"
]
}
]
}
```
@ -205,17 +221,15 @@ Don't forget to commit your changes!
### Schema
**Version** 1.0
**Version** 1.1
| Property | Required | Type | Description |
| --------------------- | -------- | --- | --- |
| friendly_name | True | String | The name to show |
| name | True | String | The docker image to use |
| description | True | String | A short description of the workspace |
| image_src | True | String | The name of the workspace icon used |
| architecture | True | Array | Json list containing either "amd64", "arm64" or both |
| compatability | True | Array | A list of Kasm versions the workspace should work with |
| uncompressed_size_mb | True | Integer | Integer of the approximate size of the workspace when it's uncompressed in MB. This doesn't take into account layers. For example if an image is 2.46GB you would enter 2460 |
| categories | False | Array | Json list containing the categories the workspace belongs too. This should be limited to a max of 3. |
| docker_registry | False | String | Which docker registry to use |
| run_config | False | Object | Any additional parameters to add to the run config |
@ -226,17 +240,90 @@ Don't forget to commit your changes!
| gpu_count | False | Integer | Specify the amount of GPUs to use for this workspace |
| cpu_allocation_method | False | String | What CPU allocation method to use for this workspace. Can be either "Inherit", "Quotas" or "Shares" |
The compatibility property is an array of objects and needs a bit more explanation
```json
"compatibility": [
{
"version": "1.16.x",
"image": "kasmweb/chromium:1.16.0-rolling-daily",
"uncompressed_size_mb": 2643,
"available_tags": [
"develop",
"1.16.0",
"1.16.0-rolling-weekly",
"1.16.0-rolling-daily"
]
}
]
```
* **version** - This is the version of kasm the entry is compatible with
* **image** - The docker image. The tag is included for things like estimating the size and is used if there are no available_tags.
* **uncompressed_size_mb** - Integer of the approximate size of the workspace when its uncompressed in MB. This doesnt take into account layers. For example if an image is 2.46GB you would enter 2460
* **available_tags** - These values are what will determine the available "channels" on the front end. If you don't want/need channels, remove the available_tags section completely. You shouldn't mix and match though, if you specify available_tags for 1 workspace, it should be specified for all of them. That doesn't mean every workspace has to have all the same tags, if a workspace only has develop tags then it will only show when develop is the selected channel.
Head to the **Actions** tab to check your progress and once `Page build and deployment` is complete, your site should be ready.
### New Kasm Workspaces version
When a new version of Kasm is released then a new entry needs to be added to the compatibility list to support it. If you have a lot of workspaces defined then there are a couple of scripts included that can help.
Go to the processing folder and edit add_next_version.js changing the `baseversion` to match the new version. Also make sure the rest of the file matches your setup, if you anre't using channels then completely remove the `available_tags` section.
Then in a terminal run
```
cd processing
npm install
node add_next_version.js
```
This will add a new entry for every single workspace, but the size will be set to 0, this is so you can run the `get_image_sizes.js` script. This will loop through each `image` that has an uncompressed_size_mb of 0 and will pull the image, get the size, update the workspace json and remove the image.
This can take a long time if you have a lot of workspaces and dependng on their sizes, but if the script crashes out, you can just start it agin and it will carry on from where it left off.
```
node add_next_version.js
```
### New schema version
When a new schema version comes out, you just need to create a new branch that refrlects the new schema, for example `1.1` and make it the default branch.
When a new schema version comes out, you just need to create a new branch that reflects the new schema, for example `1.2` and make it the default branch.
In the new branch, make any updates that are needed, when the changes are committed a new version will be built.
Kasm Workspaces will automatically pull the version of the schema that it understands.
If only the latest version is building (so 1.1 works but 1.0 doesn't), open build_all_branches.sh, search for `echo "All branches:` and check if there is `git fetch --all` on the line underneath, if not, add it, this will need to be added to the 1.0 branch as well if it's missing, otherwise if you make a change to 1.0 (for kasm versions 1.12.x - 1.15.x) it won't build all the branches.
**Updating to 1.16.x support**
1.16.x changed the schema from 1.0 to 1.1, the main changes to this are the compatibility changes from a simple array to an array of objects, this allows us to tie the image used and the image size to the kasm version.
In addition the top level name is removed as is top level uncompessed_size_mb as these are now available in the compatibility matrix (name is called image).
If you have an older version you will probably need to update the following files in your 1.1 branch:
* build_all_branches.sh
* processing/processjson.js
* site/components/Workspace.js
* site/pages/index.js
* site/pages/new/[[..workspace]].js
If you have a lot of workspaces (or just want an easier way to update your workspaces), there is an update file in processing called `update_1_0_to_1_1.js` copy that across to your own install, edit it and make sure the tags etc match your install. If you don't want to use channels, you can remove the available_tags section entirely.
Then to use it, create a 1.1 branch from your current 1.0 source, then in a terminal:
```
cd processing
npm install
node update_1_0_to_1_1.js
```
This will convert your existing workspaces to a 1.1 compatible format.
## Channels
Schema 1.1 added the concept of channels. Each registry can specify the channels they support, these are defined by the tags an image has. For example you might have develop, 1.16.0 and 1.16.0-rolling-daily. When the registry json is built it loops through all the workspaces and generates a list of all the possible "Channels" (tags) that are listed in compatibility.available_tags. Available tags is an optional list, if you don't include it on any of the workspaces then your registry will work as before without presenting the end user with a channels option. You shouldn't mix and match though, if you add available tags to 1 workspace, you should add available tags to all workspaces.
If you are using channels, update processing/processjson.js and specify the `default_channel` such as `'develop'`. If you aren't using channels you don't need to do anything, it will automatically detect there are no channels and set the correct value.
 
## 6. Discovery

View File

@ -0,0 +1,48 @@
const fs = require("fs");
const glob = require("glob");
const baseversion = '1.16'
const tag = ':develop'
const version = baseversion + '.x'
const tagversion = baseversion + '.0'
glob("../workspaces/**/workspace.json", async function (err, files) {
if (err) {
console.log(
"cannot read the folder, something goes wrong with glob",
err
);
}
for (const file of files) {
let filedata = fs.readFileSync(file);
let parsed = JSON.parse(filedata);
const current = parsed.compatibility[parsed.compatibility.length - 1]
const image = current.image.split(':')[0]
const exists = parsed.compatibility.findIndex(el => el.version === version)
let details = {
version,
image: image + tag,
uncompressed_size_mb: 0,
available_tags: [
'develop',
tagversion,
tagversion + '-rolling-weekly',
tagversion + '-rolling-daily'
]
}
if (exists === -1) {
parsed.compatibility.push(details)
fs.writeFileSync(file, JSON.stringify(parsed, null, 2));
}
}
});

View File

@ -0,0 +1,45 @@
const fs = require("fs");
const glob = require("glob");
const { execSync } = require('child_process');
glob("../workspaces/**/workspace.json", function (err, files) {
if (err) {
console.log(
"cannot read the folder, something goes wrong with glob",
err
);
}
let total = 0
for (const file of files) {
let filedata = fs.readFileSync(file);
let parsed = JSON.parse(filedata);
parsed.compatibility.forEach((element, index) => {
total++
if (element.uncompressed_size_mb === 0) {
execSync('docker image prune -a -f')
execSync('docker system prune --all --force --volumes')
let pull = execSync('docker pull ' + element.image)
// console.log(pull)
let inspect = execSync('docker inspect -f "{{ .Size }}" ' + element.image)
let size = Math.round(inspect / 1000000)
let remove = execSync('docker rmi ' + element.image)
console.log(remove)
parsed.compatibility[index].uncompressed_size_mb = size
console.log('Write file: ' + parsed.friendly_name + ' - ' + element.version + ': ' + size)
fs.writeFileSync(file, JSON.stringify(parsed, null, 2));
} else {
console.log(parsed.friendly_name + ' - ' + element.version + ': skipped')
}
})
}
console.log(total + ' entries processed')
});

View File

@ -29,6 +29,9 @@ glob("**/workspace.json", async function (err, files) {
encoding: "hex",
};
let channels = new Set()
let versions = new Set()
for (const file of files) {
//files.forEach(async function(file) {
@ -39,7 +42,17 @@ glob("**/workspace.json", async function (err, files) {
let parsed = JSON.parse(filedata);
parsed.sha = hash.hash;
console.log(parsed.name + ' added')
console.log(parsed.friendly_name + ' added')
parsed.compatibility.forEach((element, index) => {
if ('available_tags' in element) {
element.available_tags.forEach((el) => {
channels.add(el)
})
}
if ('version' in element) {
versions.add(element.version)
}
})
workspaces.push(parsed);
if (fs.existsSync(folder + "/" + parsed.image_src)) {
@ -60,9 +73,19 @@ glob("**/workspace.json", async function (err, files) {
contact_url: nextConfig.env.contactUrl || null,
modified: Date.now(),
workspaces: workspaces,
channels: [...channels],
default_channel: 'develop'
};
if (channels.size === 0) {
json.default_channel = null
}
let data = JSON.stringify(json);
fs.writeFileSync(dir + "/list.json", data);
fs.writeFileSync(dir + "/versions.json", JSON.stringify({
versions: [...versions]
}));
});

View File

@ -0,0 +1,41 @@
const fs = require("fs");
const glob = require("glob");
glob("../workspaces/**/workspace.json", async function (err, files) {
if (err) {
console.log(
"cannot read the folder, something goes wrong with glob",
err
);
}
for (const file of files) {
let filedata = fs.readFileSync(file);
let parsed = JSON.parse(filedata);
delete parsed.compatibility
parsed.compatibility = []
let details = {
version: '1.16.x',
image: parsed.name.split(':')[0] + ':1.16.0-rolling-daily',
uncompressed_size_mb: parsed.uncompressed_size_mb,
available_tags: [
'develop',
'1.16.0',
'1.16.0-rolling-weekly',
'1.16.0-rolling-daily'
]
}
parsed.compatibility.push(details)
delete parsed.uncompressed_size_mb
delete parsed.name
fs.writeFileSync(file, JSON.stringify(parsed, null, 2));
}
});

View File

@ -6,12 +6,12 @@ function Workspace({ Component, pageProps, workspace }) {
const viewexample = (workspace) => {
router.push({
pathname: '/new/[workspace]',
query: { workspace: btoa(workspace.name)}
query: { workspace: btoa(workspace.friendly_name)}
})
}
return (
<div onClick={() => viewexample(workspace)} className="w-[245px] h-[88px] transition-all relative cursor-pointer group flex p-2 items-center justify-center bg-slate-100/90 shadow rounded hover:shadow-xl hover:bg-gradient-to-r hover:from-slate-900 hover:to-cyan-800 hover:text-white">
<div onClick={() => viewexample(workspace)} className="w-[245px] h-[88px] transition-all relative cursor-pointer group flex p-2 items-center justify-center bg-slate-100/90 shadow rounded hover:shadow-xl hover:bg-gradient-to-r hover:from-[#162d48] hover:to-[#2980b9] hover:text-white">
<div className="w-full h-full">
<div className="show-grid flex h-full items-center">
<div className="kasmcard-img flex h-full mx-4 items-center justify-center">

View File

@ -18,7 +18,7 @@ export default function Home({ searchText }) {
workspaces.workspaces.forEach((workspace) => {
if(workspace.compatibility) {
workspace.compatibility.forEach((v) => {
const value = parseFloat(v)
const value = parseFloat(v.version)
if(wsversions.indexOf(value) === -1) {
wsversions.push(value)
}
@ -43,7 +43,7 @@ export default function Home({ searchText }) {
}
let filteredworkspaces = workspaces && workspaces.workspaces && workspaces.workspaces.length > 0 ? [...workspaces.workspaces] : [];
filteredworkspaces = filteredworkspaces.filter((v) => v.compatibility.some((el) => el === version + '.x'))
filteredworkspaces = filteredworkspaces.filter((v) => v.compatibility.some((el) => el.version === version + '.x'))
const lowerSearch = searchText && searchText.toLowerCase();
if (searchText && searchText !== "") {
filteredworkspaces = filteredworkspaces.filter((i) => {
@ -71,7 +71,7 @@ export default function Home({ searchText }) {
<h1 className='flex flex-wrap-reverse uppercase tracking-widest justify-center mb-10 gap-5'>
<span className='flex items-center text-lg bg-slate-100/90 rounded overflow-hidden shadow'>
<span className='flex px-3 text-xs opacity-100'>Workspaces</span>
<span className='text-white p-3 py-1 flex bg-[#2980b9]'>{workspaces && workspaces.workspacecount}</span>
<span className='text-white p-3 py-1 flex bg-[#2980b9]'>{filteredworkspaces && filteredworkspaces.length}</span>
</span>
<span className='flex items-center text-lg bg-slate-100/90 rounded overflow-hidden shadow'>
<span className='flex px-3 text-xs opacity-100'>Kasm Version</span>

View File

@ -10,7 +10,7 @@ import allworkspaces from '../../../public/list.json'
export async function getStaticPaths() {
let paths = allworkspaces.workspaces.map(workspace => ({
params: {
workspace: [btoa(workspace.name)]
workspace: [btoa(workspace.friendly_name)]
}
}))
paths.push({
@ -47,7 +47,6 @@ export default function New({ workspace }) {
friendly_name: null,
image_src: null,
description: null,
name: null,
cores: 2,
memory: 2768,
gpu_count: 0,
@ -75,7 +74,7 @@ export default function New({ workspace }) {
setCombined(defaultState)
}
else if (workspace && workspace[0]) {
const workspaceDetails = allworkspaces.workspaces.find(el => el.name === atob(workspace[0]))
const workspaceDetails = allworkspaces.workspaces.find(el => el.friendly_name === atob(workspace[0]))
delete workspaceDetails['sha']
description.current.value = workspaceDetails.description
name.current.value = workspaceDetails.name
@ -296,7 +295,7 @@ export default function New({ workspace }) {
<div className='w-full lg:w-1/2 p-16 bg-slate-100'>
<Workspace workspace={combined} icon={icon} inlineImage={inlineImage} />
<pre className='my-8 overflow-y-auto text-xs'>{JSON.stringify(displayWorkspace(), null, 2)}</pre>
<button onClick={downloadZip} className='p-4 relative z-10 px-5 bg-cyan-700 border-t border-white/20 border-solid hover:bg-slate-900 transition m-2 rounded items-center text-white/70 flex cursor-pointer'>Download</button>
<button onClick={downloadZip} className='p-4 relative z-10 px-5 bg-[#2980b9] border-t border-white/20 border-solid hover:bg-slate-900 transition m-2 rounded items-center text-white/70 flex cursor-pointer'>Download</button>
</div>
</div>
</div>

View File

@ -2,7 +2,6 @@
"description": "Chromium is a free and open-source browser, primarily developed and maintained by Google.",
"docker_registry": "https://index.docker.io/v1/",
"image_src": "chromium.png",
"name": "kasmweb/chromium:develop",
"run_config": {
"hostname": "kasm"
},
@ -23,8 +22,13 @@
"arm64"
],
"compatibility": [
"1.13.x",
"1.14.x"
],
"uncompressed_size_mb": 2170
{
"version": "1.17.x",
"image": "kasmweb/chromium:develop",
"uncompressed_size_mb": 2170,
"available_tags": [
"develop"
]
}
]
}