forked from canonical/maas-ui
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui): build filesystems list in machine storage (canonical#1854)
- Loading branch information
Caleb Ellis
authored
Nov 9, 2020
1 parent
15cec8f
commit 5a587c7
Showing
11 changed files
with
397 additions
and
33 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
...p/machines/views/MachineDetails/MachineStorage/FilesystemsTable/FilesystemsTable.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { mount } from "enzyme"; | ||
import React from "react"; | ||
|
||
import { | ||
machineDisk as diskFactory, | ||
machineFilesystem as fsFactory, | ||
machinePartition as partitionFactory, | ||
} from "testing/factories"; | ||
import FilesystemsTable from "./FilesystemsTable"; | ||
|
||
describe("FilesystemsTable", () => { | ||
it("can show an empty message", () => { | ||
const wrapper = mount( | ||
<FilesystemsTable disks={[]} specialFilesystems={[]} /> | ||
); | ||
|
||
expect(wrapper.find("TableRow TableCell").at(0).text()).toBe( | ||
"No filesystems defined." | ||
); | ||
}); | ||
|
||
it("can show filesystems associated with disks", () => { | ||
const disks = [ | ||
diskFactory({ | ||
filesystem: fsFactory({ mount_point: "/disk-fs/path" }), | ||
name: "disk-fs", | ||
partitions: [], | ||
}), | ||
]; | ||
const wrapper = mount( | ||
<FilesystemsTable disks={disks} specialFilesystems={[]} /> | ||
); | ||
|
||
expect(wrapper.find("TableRow TableCell").at(0).text()).toBe("disk-fs"); | ||
expect(wrapper.find("TableRow TableCell").at(3).text()).toBe( | ||
"/disk-fs/path" | ||
); | ||
}); | ||
|
||
it("can show filesystems associated with partitions", () => { | ||
const disks = [ | ||
diskFactory({ | ||
filesystem: null, | ||
partitions: [ | ||
partitionFactory({ | ||
filesystem: fsFactory({ mount_point: "/partition-fs/path" }), | ||
name: "partition-fs", | ||
}), | ||
], | ||
}), | ||
]; | ||
const wrapper = mount( | ||
<FilesystemsTable disks={disks} specialFilesystems={[]} /> | ||
); | ||
|
||
expect(wrapper.find("TableRow TableCell").at(0).text()).toBe( | ||
"partition-fs" | ||
); | ||
expect(wrapper.find("TableRow TableCell").at(3).text()).toBe( | ||
"/partition-fs/path" | ||
); | ||
}); | ||
|
||
it("can show special filesystems", () => { | ||
const specialFilesystems = [ | ||
fsFactory({ mount_point: "/special-fs/path", fstype: "tmpfs" }), | ||
]; | ||
const wrapper = mount( | ||
<FilesystemsTable disks={[]} specialFilesystems={specialFilesystems} /> | ||
); | ||
|
||
expect(wrapper.find("TableRow TableCell").at(0).text()).toBe("—"); | ||
expect(wrapper.find("TableRow TableCell").at(3).text()).toBe( | ||
"/special-fs/path" | ||
); | ||
}); | ||
}); |
164 changes: 164 additions & 0 deletions
164
...rc/app/machines/views/MachineDetails/MachineStorage/FilesystemsTable/FilesystemsTable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
import { MainTable } from "@canonical/react-components"; | ||
import React from "react"; | ||
|
||
import { formatBytes } from "app/utils"; | ||
import type { Disk, Filesystem, Partition } from "app/store/machine/types"; | ||
|
||
type FilesystemDetails = { | ||
fstype: Filesystem["fstype"]; | ||
id: Filesystem["id"]; | ||
mountOptions: Filesystem["mount_options"]; | ||
mountPoint: Filesystem["mount_point"]; | ||
name: string; | ||
size: number; | ||
}; | ||
|
||
type Props = { | ||
disks: Disk[]; | ||
specialFilesystems: Filesystem[]; | ||
}; | ||
|
||
/** | ||
* Returns whether a storage device has a mounted filesystem. If a filesystem is | ||
* unmounted, it will show in the "Available disks and partitions" table. | ||
* @param storageDevice - the storage device to check. | ||
* @returns whether the storage device has a mounted filesystem. | ||
*/ | ||
const hasMountedFilesystem = (storageDevice: Disk | Partition) => | ||
!!storageDevice?.filesystem?.mount_point; | ||
|
||
/** | ||
* Formats a filesystem for use in the filesystems table. | ||
* @param filesystem - the base filesystem object. | ||
* @param name - the name to give the filesystem. | ||
* @param size - the size to give the filesystem. | ||
* @returns formatted filesystem object. | ||
*/ | ||
const formatFilesystem = ( | ||
filesystem: Filesystem, | ||
name: string, | ||
size: number | ||
): FilesystemDetails => ({ | ||
fstype: filesystem.fstype, | ||
id: filesystem.id, | ||
mountPoint: filesystem.mount_point, | ||
mountOptions: filesystem.mount_options, | ||
name, | ||
size, | ||
}); | ||
|
||
/** | ||
* Returns a combined list of special filesystems, and filesystems associated | ||
* with disks and partitions. | ||
* @param disks - disks to check for filesystems | ||
* @param specialFilesystems - tmpfs or ramfs filesystems | ||
* @returns list of filesystems with extra details for use in table | ||
*/ | ||
const getFilesystems = ( | ||
disks: Disk[], | ||
specialFilesystems: Filesystem[] | ||
): FilesystemDetails[] => { | ||
const filesystems = disks.reduce( | ||
(diskFilesystems: FilesystemDetails[], disk: Disk) => { | ||
if (hasMountedFilesystem(disk)) { | ||
diskFilesystems.push( | ||
formatFilesystem(disk.filesystem, disk.name, disk.size) | ||
); | ||
} | ||
|
||
if (disk.partitions) { | ||
disk.partitions.forEach((partition) => { | ||
if (hasMountedFilesystem(partition)) { | ||
diskFilesystems.push( | ||
formatFilesystem( | ||
partition.filesystem, | ||
partition.name, | ||
partition.size | ||
) | ||
); | ||
} | ||
}); | ||
} | ||
|
||
return diskFilesystems; | ||
}, | ||
[] | ||
); | ||
|
||
specialFilesystems.forEach((fs) => { | ||
filesystems.push(formatFilesystem(fs, "—", 0)); | ||
}); | ||
|
||
return filesystems; | ||
}; | ||
|
||
const FilesystemsTable = ({ | ||
disks, | ||
specialFilesystems, | ||
}: Props): JSX.Element => { | ||
const filesystems = getFilesystems(disks, specialFilesystems); | ||
|
||
return ( | ||
<MainTable | ||
defaultSort="name" | ||
defaultSortDirection="ascending" | ||
headers={[ | ||
{ | ||
content: "Name", | ||
sortKey: "name", | ||
}, | ||
{ | ||
content: "Size", | ||
sortKey: "size", | ||
}, | ||
{ | ||
content: "Filesystem", | ||
sortKey: "fstype", | ||
}, | ||
{ | ||
content: "Mount point", | ||
sortKey: "mountPoint", | ||
}, | ||
{ | ||
content: "Mount options", | ||
}, | ||
{ | ||
content: "Actions", | ||
className: "u-align--right", | ||
}, | ||
]} | ||
rows={ | ||
filesystems.length > 0 | ||
? filesystems.map((fs) => { | ||
const size = formatBytes(fs.size, "B"); | ||
return { | ||
columns: [ | ||
{ content: fs.name }, | ||
{ | ||
content: fs.size === 0 ? "—" : `${size.value} ${size.unit}`, | ||
}, | ||
{ content: fs.fstype }, | ||
{ content: fs.mountPoint }, | ||
{ content: fs.mountOptions }, | ||
{ | ||
content: "", | ||
className: "u-align--right", | ||
}, | ||
], | ||
key: fs.id, | ||
sortData: { | ||
mountPoint: fs.mountPoint, | ||
name: fs.name, | ||
size: fs.size, | ||
fstype: fs.fstype, | ||
}, | ||
}; | ||
}) | ||
: [{ columns: [{ content: "No filesystems defined." }] }] | ||
} | ||
sortable | ||
/> | ||
); | ||
}; | ||
|
||
export default FilesystemsTable; |
1 change: 1 addition & 0 deletions
1
ui/src/app/machines/views/MachineDetails/MachineStorage/FilesystemsTable/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from "./FilesystemsTable"; |
34 changes: 34 additions & 0 deletions
34
ui/src/app/machines/views/MachineDetails/MachineStorage/MachineStorage.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { mount } from "enzyme"; | ||
import { Provider } from "react-redux"; | ||
import { MemoryRouter } from "react-router-dom"; | ||
import configureStore from "redux-mock-store"; | ||
import React from "react"; | ||
|
||
import { | ||
machineState as machineStateFactory, | ||
rootState as rootStateFactory, | ||
} from "testing/factories"; | ||
import MachineStorage from "./MachineStorage"; | ||
|
||
const mockStore = configureStore(); | ||
|
||
describe("MachineStorage", () => { | ||
it("displays a spinner if machine is loading", () => { | ||
const state = rootStateFactory({ | ||
machine: machineStateFactory({ | ||
items: [], | ||
}), | ||
}); | ||
const store = mockStore(state); | ||
const wrapper = mount( | ||
<Provider store={store}> | ||
<MemoryRouter | ||
initialEntries={[{ pathname: "/machine/abc123", key: "testKey" }]} | ||
> | ||
<MachineStorage /> | ||
</MemoryRouter> | ||
</Provider> | ||
); | ||
expect(wrapper.find("Spinner").exists()).toBe(true); | ||
}); | ||
}); |
35 changes: 35 additions & 0 deletions
35
ui/src/app/machines/views/MachineDetails/MachineStorage/MachineStorage.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { Spinner } from "@canonical/react-components"; | ||
import { useSelector } from "react-redux"; | ||
import { useParams } from "react-router"; | ||
import React from "react"; | ||
|
||
import { useWindowTitle } from "app/base/hooks"; | ||
import type { RouteParams } from "app/base/types"; | ||
import machineSelectors from "app/store/machine/selectors"; | ||
import type { RootState } from "app/store/root/types"; | ||
import FilesystemsTable from "./FilesystemsTable"; | ||
|
||
const MachineStorage = (): JSX.Element => { | ||
const params = useParams<RouteParams>(); | ||
const { id } = params; | ||
const machine = useSelector((state: RootState) => | ||
machineSelectors.getById(state, id) | ||
); | ||
|
||
useWindowTitle(`${`${machine?.fqdn} ` || "Machine"} storage`); | ||
|
||
if (machine && "disks" in machine && "special_filesystems" in machine) { | ||
return ( | ||
<> | ||
<h4>Filesystems</h4> | ||
<FilesystemsTable | ||
disks={machine.disks} | ||
specialFilesystems={machine.special_filesystems} | ||
/> | ||
</> | ||
); | ||
} | ||
return <Spinner text="Loading..." />; | ||
}; | ||
|
||
export default MachineStorage; |
1 change: 1 addition & 0 deletions
1
ui/src/app/machines/views/MachineDetails/MachineStorage/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from "./MachineStorage"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.