diff --git a/packages/frontend/src/components/FileSystemPanel.test.tsx b/packages/frontend/src/components/FileSystemPanel.test.tsx index 72e0bb2..8aa082b 100644 --- a/packages/frontend/src/components/FileSystemPanel.test.tsx +++ b/packages/frontend/src/components/FileSystemPanel.test.tsx @@ -102,3 +102,22 @@ test('cannot attach until the file system has a mount target', async () => { expect(await screen.findByText(/No mount targets yet/)).toBeInTheDocument() expect(within(screen.getByRole('form', {name: 'Attach to a function'})).getByRole('button', {name: 'Attach'})).toBeDisabled() }) + +test('shows how to mount the file system on an EC2 instance once a mount target is available', async () => { + renderPanel(fileSystem()) + + const commands = within(await screen.findByLabelText('Mount on an EC2 instance')) + expect(commands.getByText('fs-1.efs.us-east-1.amazonaws.com')).toBeInTheDocument() + expect(commands.getByText('sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport fs-1.efs.us-east-1.amazonaws.com:/ efs')).toBeInTheDocument() + expect(commands.getByText('sudo mount -t efs fs-1:/ efs')).toBeInTheDocument() + expect(commands.getByText('Mount by IP address in us-east-1a')).toBeInTheDocument() + expect(commands.getByText(/10\.0\.0\.10:\/ efs$/)).toBeInTheDocument() + expect(commands.getByRole('button', {name: 'Copy mount with the nfs client command'})).toBeInTheDocument() +}) + +test('holds the mount commands back while the mount target is still creating', async () => { + renderPanel(fileSystem({mountTargets: [{id: 'fsmt-1', subnetId: 'subnet-a', availabilityZone: 'us-east-1a', ipAddress: '10.0.0.10', state: 'creating'}]})) + + expect(await screen.findByText('The mount commands appear once a mount target is available.')).toBeInTheDocument() + expect(screen.queryByLabelText('Mount on an EC2 instance')).not.toBeInTheDocument() +}) diff --git a/packages/frontend/src/components/FileSystemPanel.tsx b/packages/frontend/src/components/FileSystemPanel.tsx index 1c20fd8..741478a 100644 --- a/packages/frontend/src/components/FileSystemPanel.tsx +++ b/packages/frontend/src/components/FileSystemPanel.tsx @@ -2,6 +2,7 @@ import {RefreshCw} from 'lucide-react' import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import {useEffect, useState} from 'react' import {Link} from 'react-router-dom' +import {CopyValueButton} from '@/components/CopyValueButton' import { attachFileSystemToFunction, createAccessPoint, @@ -26,6 +27,7 @@ type AccessPoint = {id: string; arn: string | null; name: string | null; path: s type MountedFunction = {name: string; accessPointId: string | null; mountPath: string | null} const EMPTY_ACCESS_POINT = {name: '', path: '/', uid: '1000', gid: '1000', permissions: '755'} +const NFS_OPTIONS = 'nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport' export function FileSystemPanel({resource, runtimeReachable}: Props) { const accountId = useAccountId() @@ -40,6 +42,11 @@ export function FileSystemPanel({resource, runtimeReachable}: Props) { queryKey: detailKey, queryFn: ({signal}) => getCloudResource('aws', 'filesystems', resource?.id ?? '', signal), enabled: Boolean(resource) && runtimeReachable, + // A new mount target is creating until its NFS server answers, a few seconds. + refetchInterval: (query) => { + const targets = (query.state.data?.metadata.mountTargets ?? []) as MountTarget[] + return targets.some((mountTarget) => mountTarget.state === 'creating') ? 2_000 : false + }, }) const functionsQuery = useQuery({ queryKey: cloudQueryKeys.resources('aws', 'serverless'), @@ -185,7 +192,36 @@ export function FileSystemPanel({resource, runtimeReachable}: Props) { } {accessPoints.length > 0 &&
Lambda needs a VPC to mount a file system. A function not yet in one is put in this file system's mount target subnets with the default security group.
} +{notice}
} {error instanceof Error &&{error.message}
} } + +function Ec2MountInstructions({fileSystemId, region, mountTargets}: {fileSystemId: string; region: string; mountTargets: MountTarget[]}) { + const available = mountTargets.filter((mountTarget) => mountTarget.state === 'available' && mountTarget.ipAddress) + const dnsName = `${fileSystemId}.efs.${region}.amazonaws.com` + const command = (label: string, value: string) =>{value}
+ EC2 instances
An instance in this file system's VPC mounts it over NFS through a mount target, as on AWS.{mountTargets.length === 0 ? 'Add a mount target to mount the file system on an instance.' : 'The mount commands appear once a mount target is available.'}
+ :DNS name {dnsName}
The EFS mount helper is the amazon-efs-utils package. TheCloud serves plain NFS, so its tls and accesspoint options are not supported. A function's files are in its access point's root directory, such as efs/my-function.