Skip to content

Commit

Permalink
feat: show cached images for instance creation [WD-14414] (#904)
Browse files Browse the repository at this point in the history
## Done

- Show cached images when creating instances
  • Loading branch information
mas-who authored Sep 19, 2024
2 parents e55c4ea + bfce1b3 commit e9cca8b
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 17 deletions.
50 changes: 37 additions & 13 deletions src/pages/images/ImageSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FC, OptionHTMLAttributes, useState } from "react";
import {
Button,
CheckboxInput,
Col,
MainTable,
Modal,
Expand All @@ -19,6 +20,7 @@ import {
isContainerOnlyImage,
isVmOnlyImage,
LOCAL_ISO,
LOCAL_IMAGE,
} from "util/images";
import Loader from "components/Loader";
import { getArchitectureAliases } from "util/architectures";
Expand Down Expand Up @@ -55,6 +57,7 @@ const ImageSelector: FC<Props> = ({ onSelect, onClose }) => {
const [arch, setArch] = useState<string>("amd64");
const [type, setType] = useState<LxdImageType | undefined>(undefined);
const [variant, setVariant] = useState<string>(ANY);
const [hideRemote, setHideRemote] = useState(false);
const { project } = useParams<{ project: string }>();

const loadImages = (file: string, server: string): Promise<RemoteImage[]> => {
Expand Down Expand Up @@ -105,15 +108,19 @@ const ImageSelector: FC<Props> = ({ onSelect, onClose }) => {
const archSupported = getArchitectureAliases(
settings?.environment?.architectures ?? [],
);
const images = isLoading
let images = isLoading
? []
: localImages
.filter((image) => !image.cached)
.map(localLxdToRemoteImage)
.concat([...canonicalImages].reverse().sort(byLtsFirst))
.concat([...minimalImages].reverse().sort(byLtsFirst))
.concat([...imagesLxdImages])
.filter((image) => archSupported.includes(image.arch));
.sort((a, b) => Number(b.cached) - Number(a.cached));

if (!hideRemote) {
images = images
.concat([...canonicalImages].reverse().sort(byLtsFirst))
.concat([...minimalImages].reverse().sort(byLtsFirst))
.concat([...imagesLxdImages])
.filter((image) => archSupported.includes(image.arch));
}

const archAll = [...new Set(images.map((item) => item.arch))]
.filter((arch) => arch !== "")
Expand Down Expand Up @@ -210,19 +217,20 @@ const ImageSelector: FC<Props> = ({ onSelect, onClose }) => {
: item.variant;

const getSource = () => {
if (item.created_at) {
return "Local";
let source = "Custom";
if (!item.cached && item.created_at) {
source = "Local";
}
if (item.server === canonicalServer) {
return "Ubuntu";
source = "Ubuntu";
}
if (item.server === minimalServer) {
return "Ubuntu Minimal";
source = "Ubuntu Minimal";
}
if (item.server === imagesLxdServer) {
return "LXD Images";
source = "LXD Images";
}
return "Custom";
return source;
};

return {
Expand Down Expand Up @@ -260,7 +268,12 @@ const ImageSelector: FC<Props> = ({ onSelect, onClose }) => {
onClick: selectImage,
},
{
content: getSource(),
content: (
<>
{getSource()}
{item.cached && <span className="u-text--muted"> cached</span>}
</>
),
role: "cell",
"aria-label": "Source",
onClick: selectImage,
Expand All @@ -272,6 +285,11 @@ const ImageSelector: FC<Props> = ({ onSelect, onClose }) => {
type="button"
dense
className="u-no-margin--bottom"
appearance={
item.cached || item.server === LOCAL_IMAGE
? "positive"
: "default"
}
>
Select
</Button>
Expand Down Expand Up @@ -403,6 +421,12 @@ const ImageSelector: FC<Props> = ({ onSelect, onClose }) => {
]}
value={type ?? ""}
/>
<CheckboxInput
aria-label="Only show cached images"
checked={hideRemote}
label="Show only cached images"
onChange={() => setHideRemote((prev) => !prev)}
/>
</div>
</Col>
<Col size={9}>
Expand Down
2 changes: 2 additions & 0 deletions src/types/image.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface LxdImage {
os: string;
release: string;
variant?: string;
version?: string;
};
update_source?: {
alias: string;
Expand Down Expand Up @@ -61,6 +62,7 @@ export interface RemoteImage {
volume?: LxdStorageVolume;
type?: LxdImageType;
fingerprint?: string;
cached?: boolean;
}

export interface RemoteImageList {
Expand Down
8 changes: 6 additions & 2 deletions src/util/images.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LxdImage, RemoteImage } from "types/image";
import { LxdStorageVolume } from "types/storage";
import { capitalizeFirstLetter } from "./helpers";

export const isVmOnlyImage = (image: RemoteImage): boolean | undefined => {
if (image.server === LOCAL_ISO || image.type === "virtual-machine") {
Expand Down Expand Up @@ -46,11 +47,14 @@ export const localLxdToRemoteImage = (image: LxdImage): RemoteImage => {
aliases: image.update_source?.alias ?? image.aliases?.[0]?.name ?? "",
fingerprint: image.fingerprint,
arch: image.architecture === "x86_64" ? "amd64" : image.architecture,
os: image.properties?.os ?? "",
os: capitalizeFirstLetter(image.properties?.os ?? ""),
created_at: new Date(image.uploaded_at).getTime(),
release: image.properties?.release ?? "",
server: LOCAL_IMAGE,
release_title: image.properties?.version ?? "",
type: image.type,
cached: image.cached,
server: image.cached ? image.update_source?.server : LOCAL_IMAGE,
variant: image.properties?.variant,
};
};

Expand Down
13 changes: 11 additions & 2 deletions tests/helpers/instances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ export const createInstance = async (
await page.getByRole("button", { name: "Browse images" }).click();
await page.getByPlaceholder("Search an image").click();
await page.getByPlaceholder("Search an image").fill(image);
await page.getByRole("button", { name: "Select" }).first().click();
await page
.getByRole("row")
.filter({ hasNotText: "cached" })
.getByRole("button", { name: "Select" })
.first()
.click();
await page
.getByRole("combobox", { name: "Instance type" })
.selectOption(type);
Expand Down Expand Up @@ -121,7 +126,11 @@ export const createAndStartInstance = async (
await page.getByRole("button", { name: "Browse images" }).click();
await page.getByPlaceholder("Search an image").click();
await page.getByPlaceholder("Search an image").fill("alpine/3.19/cloud");
await page.getByRole("button", { name: "Select" }).click();
await page
.getByRole("row")
.filter({ hasNotText: "cached" })
.getByRole("button", { name: "Select" })
.click();
await page
.getByRole("combobox", { name: "Instance type" })
.selectOption(type);
Expand Down

0 comments on commit e9cca8b

Please sign in to comment.