diff --git a/nixos/tests/lxd/ui.nix b/nixos/tests/lxd/ui.nix
index 86cb30d8c2b6..ff651725ba70 100644
--- a/nixos/tests/lxd/ui.nix
+++ b/nixos/tests/lxd/ui.nix
@@ -11,9 +11,37 @@ import ../make-test-python.nix ({ pkgs, lib, ... }: {
lxd.ui.enable = true;
};
- environment.systemPackages = [ pkgs.curl ];
+ environment.systemPackages =
+ let
+ seleniumScript = pkgs.writers.writePython3Bin "selenium-script"
+ {
+ libraries = with pkgs.python3Packages; [ selenium ];
+ } ''
+ from selenium import webdriver
+ from selenium.webdriver.common.by import By
+ from selenium.webdriver.firefox.options import Options
+ from selenium.webdriver.support.ui import WebDriverWait
+
+ options = Options()
+ options.add_argument("--headless")
+ service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") # noqa: E501
+
+ driver = webdriver.Firefox(options=options, service=service)
+ driver.implicitly_wait(10)
+ driver.get("https://localhost:8443/ui")
+
+ wait = WebDriverWait(driver, 60)
+
+ assert len(driver.find_elements(By.CLASS_NAME, "l-application")) > 0
+ assert len(driver.find_elements(By.CLASS_NAME, "l-navigation__drawer")) > 0
+
+ driver.close()
+ '';
+ in
+ with pkgs; [ curl firefox-unwrapped geckodriver seleniumScript ];
};
+
testScript = ''
machine.wait_for_unit("sockets.target")
machine.wait_for_unit("lxd.service")
@@ -31,5 +59,8 @@ import ../make-test-python.nix ({ pkgs, lib, ... }: {
# Ensure the endpoint returns an HTML page with 'LXD UI' in the title
machine.succeed("curl -kLs https://localhost:8443/ui | grep '
LXD UI'")
+
+ # Ensure the application is actually rendered by the Javascript
+ machine.succeed("PYTHONUNBUFFERED=1 selenium-script")
'';
})
diff --git a/pkgs/tools/admin/lxd/package.json b/pkgs/tools/admin/lxd/package.json
deleted file mode 100644
index e30b269605ee..000000000000
--- a/pkgs/tools/admin/lxd/package.json
+++ /dev/null
@@ -1,104 +0,0 @@
-{
- "name": "lxd-ui",
- "version": "0.0.1",
- "author": "Canonical Webteam",
- "license": "LGPL-3.0-only",
- "scripts": {
- "clean": "rm -rf node_modules yarn-error.log *.log build/ .jekyll-metadata .bundle playwright-report test-results haproxy-local.cfg",
- "build-html": "cp build/ui/index.html build/index.html",
- "build": "npx vite build && yarn build-html",
- "format-js-eslint": "eslint 'src/**/*.{json,jsx,tsx,ts}' 'tests/**/*.ts' --fix",
- "format-js-prettier": "prettier 'src/**/*.{json,jsx,tsx,ts}' 'tests/**/*.ts' --write",
- "format-js": "yarn format-js-eslint && yarn format-js-prettier",
- "lint-scss": "stylelint 'src/**/*.scss'",
- "lint-js-eslint": "eslint 'src/**/*.{json,tsx,ts}' 'tests/**/*.ts' .eslintrc.js babel.config.js",
- "lint-js-prettier": "prettier 'src/**/*.{json,tsx,ts}' 'tests/**/*.ts' .eslintrc.js babel.config.js --check",
- "lint-js": "yarn lint-js-eslint && yarn lint-js-prettier",
- "hooks-add": "husky install",
- "hooks-remove": "husky uninstall",
- "start": "concurrently --kill-others --raw 'vite | grep -v localhost' 'yarn serve'",
- "serve": "./entrypoint",
- "test-js": "react-scripts test src/ --watchAll=false"},
- "dependencies": {
- "@canonical/react-components": "0.47.0",
- "@monaco-editor/react": "^4.4.6",
- "@tanstack/react-query": "^4.14.5",
- "@use-it/event-listener": "^0.1.7",
- "axios": "1.3.2",
- "cytoscape": "3.23.0",
- "cytoscape-popper": "2.0.0",
- "formik": "2.2.9",
- "js-yaml": "4.1.0",
- "lodash.isequal": "4.5.0",
- "node-forge": "1.3.1",
- "parse-prometheus-text-format": "1.1.1",
- "react": "18.2.0",
- "react-cytoscapejs": "2.0.0",
- "react-dom": "18.2.0",
- "react-router-dom": "6.6.1",
- "react-scripts": "5.0.1",
- "react-useportal": "^1.0.17",
- "serve": "14.1.2",
- "vanilla-framework": "4.3.0",
- "xterm-addon-fit": "0.6.0",
- "xterm-for-react": "1.0.4",
- "yup": "0.32.11"
- },
- "devDependencies": {
- "@babel/core": "7.20.7",
- "@babel/eslint-parser": "7.19.1",
- "@babel/preset-env": "7.20.2",
- "@babel/preset-react": "7.18.6",
- "@babel/preset-typescript": "7.18.6",
- "@playwright/test": "^1.29.1",
- "@types/cytoscape-popper": "2.0.0",
- "@types/node-forge": "1.3.1",
- "@types/react": "18.0.26",
- "@types/react-cytoscapejs": "1.2.2",
- "@types/react-dom": "18.0.10",
- "@types/react-router-dom": "5.3.3",
- "@types/websocket": "1.0.5",
- "@typescript-eslint/eslint-plugin": "5.48.0",
- "@typescript-eslint/parser": "5.48.0",
- "@vitejs/plugin-react": "^3.1.0",
- "autoprefixer": "10.4.13",
- "babel-loader": "9.1.0",
- "babel-plugin-transform-es2015-modules-commonjs": "6.26.2",
- "concurrently": "7.6.0",
- "css-loader": "6.7.3",
- "eslint": "8.31.0",
- "eslint-config-prettier": "8.6.0",
- "eslint-plugin-prettier": "4.2.1",
- "eslint-plugin-react": "7.31.11",
- "husky": "8.0.0",
- "lint-staged": "13.1.1",
- "monaco-editor": "0.36.1",
- "postcss": "8.4.20",
- "postcss-cli": "10.1.0",
- "prettier": "2.8.1",
- "sass": "1.57.1",
- "sass-loader": "13.2.0",
- "style-loader": "3.3.1",
- "stylelint": "15.10.1",
- "stylelint-config-prettier": "9.0.4",
- "stylelint-config-standard-scss": "6.1.0",
- "stylelint-order": "5.0.0",
- "stylelint-prettier": "2.0.0",
- "stylelint-scss": "4.3.0",
- "ts-loader": "9.4.2",
- "typescript": "4.9.4",
- "vite": "^4.1.0",
- "vite-tsconfig-paths": "4.0.5"
- },
- "lint-staged": {
- "src/**/*.{json,jsx,ts,tsx}": [
- "eslint",
- "prettier --check"
- ],
- "tests/**/*.{json,jsx,ts,tsx}": [
- "eslint",
- "prettier --check"
- ],
- "src/**/*.scss": "stylelint"
- }
-}
diff --git a/pkgs/tools/admin/lxd/ui.nix b/pkgs/tools/admin/lxd/ui.nix
index b0582ef99969..c248199b4ad7 100644
--- a/pkgs/tools/admin/lxd/ui.nix
+++ b/pkgs/tools/admin/lxd/ui.nix
@@ -1,35 +1,61 @@
-{ mkYarnPackage
+{ lib
+, stdenv
, fetchFromGitHub
, fetchYarnDeps
-, lib
+, nodejs
+, prefetch-yarn-deps
+, yarn
}:
-mkYarnPackage rec {
+stdenv.mkDerivation rec {
pname = "lxd-ui";
- version = "0.2";
+ version = "0.4";
src = fetchFromGitHub {
owner = "canonical";
repo = "lxd-ui";
rev = "refs/tags/${version}";
- sha256 = "sha256-DygWNktangFlAqinBm6wWsRLGmX6yjhmRJ2iU0yjcgk=";
+ hash = "sha256-l9Fp/Vm7NxMCp5QcM8+frFyfahhPG7TyF6NhfU1SEaA=";
};
- packageJSON = ./package.json;
offlineCache = fetchYarnDeps {
yarnLock = "${src}/yarn.lock";
- sha256 = "sha256-B1SVCViX1LEFoBLMdFk9qaoayku7Y+zU5c4JEJkLmwE=";
+ hash = "sha256-R6OeaBC6xBHa229YGyc2LDjjenwvS805PW8ueU/o99I=";
};
+ nativeBuildInputs = [
+ nodejs
+ prefetch-yarn-deps
+ yarn
+ ];
+
+ configurePhase = ''
+ runHook preConfigure
+
+ export HOME=$(mktemp -d)
+ yarn config --offline set yarn-offline-mirror "$offlineCache"
+ fixup-yarn-lock yarn.lock
+ yarn --offline --frozen-lockfile --ignore-platform --ignore-scripts --no-progress --non-interactive install
+ patchShebangs node_modules
+
+ runHook postConfigure
+ '';
+
buildPhase = ''
+ runHook preBuild
+
yarn --offline build
+
+ runHook postBuild
'';
installPhase = ''
- cp -rv deps/lxd-ui/build/ui/ $out
- '';
+ runHook preInstall
- doDist = false;
+ cp -r build/ui/ $out
+
+ runHook postInstall
+ '';
meta = {
description = "Web user interface for LXD.";