From 5feeaae36be38b94fd648fb23ad374e093006dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Thu, 9 Nov 2023 16:37:32 +0100 Subject: [PATCH 1/8] Add JavaScript version of constants.go --- web/packages/shared/constants.ts | 42 +++++++++++++++++++ .../src/mainProcess/createAgentConfigFile.ts | 5 ++- 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 web/packages/shared/constants.ts diff --git a/web/packages/shared/constants.ts b/web/packages/shared/constants.ts new file mode 100644 index 0000000000000..0ea4505ed7591 --- /dev/null +++ b/web/packages/shared/constants.ts @@ -0,0 +1,42 @@ +/** + * Copyright 2023 Gravitational, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Be wary that this file is being loaded by both Node.js and browser environments, so keep the + * definitions simple and do not put environment-specific code here. + * + * If it turns out that the design package needs to depend on a constant from here, move this file + * to the design package or just its own package. This avoids cyclic dependencies as the shared + * package depends on the design package. + */ + +/** + * TeleportNamespace is used as the namespace prefix for labels defined by Teleport which can + * carry metadata such as cloud AWS account or instance. Those labels can be used for RBAC. + * + * If a label with this prefix is used in a config file, the associated feature must take into + * account that the label might be removed, modified or could have been set by the user. + * + * See also teleport.internal and teleport.hidden. + */ +export const TeleportNamespace = 'teleport.dev' as const; + +/** + * ConnectMyComputerNodeOwnerLabel is a label used to control access to the node managed by + * Teleport Connect as part of Connect My Computer. + */ +export const ConnectMyComputerNodeOwnerLabel = + `${TeleportNamespace}/connect-my-computer/owner` as const; diff --git a/web/packages/teleterm/src/mainProcess/createAgentConfigFile.ts b/web/packages/teleterm/src/mainProcess/createAgentConfigFile.ts index 501656c3ca8f7..ff2196619ae08 100644 --- a/web/packages/teleterm/src/mainProcess/createAgentConfigFile.ts +++ b/web/packages/teleterm/src/mainProcess/createAgentConfigFile.ts @@ -19,6 +19,8 @@ import { execFile } from 'node:child_process'; import { access, rm } from 'node:fs/promises'; import path from 'node:path'; +import * as constants from 'shared/constants'; + import { RootClusterUri, routing } from 'teleterm/ui/uri'; import { RuntimeSettings } from 'teleterm/mainProcess/types'; @@ -49,8 +51,7 @@ export async function createAgentConfigFile( } const labels = Object.entries({ - // TODO(ravicious): Move this to a JavaScript version of constants.go. - 'teleport.dev/connect-my-computer/owner': args.username, + [constants.ConnectMyComputerNodeOwnerLabel]: args.username, }) .map(keyAndValue => keyAndValue.join('=')) .join(','); From 39f40bbde3bdb89d89615fd7bac40843c8132b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Thu, 9 Nov 2023 16:38:03 +0100 Subject: [PATCH 2/8] HintBox: Make it possible to provide custom max-width --- web/packages/teleport/src/Discover/Shared/HintBox.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/web/packages/teleport/src/Discover/Shared/HintBox.tsx b/web/packages/teleport/src/Discover/Shared/HintBox.tsx index 9732a18b81b2e..02b566cbfea49 100644 --- a/web/packages/teleport/src/Discover/Shared/HintBox.tsx +++ b/web/packages/teleport/src/Discover/Shared/HintBox.tsx @@ -23,8 +23,9 @@ import * as Icons from 'design/Icon'; import { TextIcon } from 'teleport/Discover/Shared/Text'; -const HintBoxContainer = styled(Box)` - max-width: 1000px; +const HintBoxContainer = styled(Box).attrs(props => ({ + maxWidth: props.maxWidth, +}))` background-color: ${props => props.theme.colors.spotBackground[0]}; padding: ${props => `${props.theme.space[3]}px`}; border-radius: ${props => `${props.theme.space[2]}px`}; @@ -53,11 +54,12 @@ export const SuccessInfo = styled(Box)` interface HintBoxProps { header: string; + maxWidth?: string; } export function HintBox(props: React.PropsWithChildren) { return ( - + Date: Thu, 9 Nov 2023 16:40:39 +0100 Subject: [PATCH 3/8] Poll for Connect My Computer node --- .../SetupConnect/SetupConnect.test.ts | 81 ++++++++ .../SetupConnect/SetupConnect.tsx | 190 +++++++++++++++++- 2 files changed, 265 insertions(+), 6 deletions(-) create mode 100644 web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.test.ts diff --git a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.test.ts b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.test.ts new file mode 100644 index 0000000000000..c7184774d2851 --- /dev/null +++ b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.test.ts @@ -0,0 +1,81 @@ +/** + * Copyright 2023 Gravitational, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { renderHook } from '@testing-library/react-hooks'; + +import * as useTeleport from 'teleport/useTeleport'; +import NodeService from 'teleport/services/nodes/nodes'; +import TeleportContext from 'teleport/teleportContext'; + +import { nodes } from 'teleport/Nodes/fixtures'; + +import { Node } from 'teleport/services/nodes'; + +import { usePollForConnectMyComputerNode } from './SetupConnect'; + +beforeEach(() => { + jest.restoreAllMocks(); +}); + +describe('usePollForConnectMyComputerNode', () => { + const tests: Array<{ + name: string; + initialNodes: Node[]; + }> = [ + { + name: 'returns the correct node if the first polling request returns no nodes', + initialNodes: [], + }, + { + name: 'returns the correct node if the first polling request returns some nodes', + initialNodes: [nodes[1], nodes[2]], + }, + ]; + + test.each(tests)('$name', async ({ initialNodes }) => { + const expectedNode = nodes[0]; + + const nodeService = { + fetchNodes: jest.fn(), + } as Partial as NodeService; + + jest + .mocked(nodeService) + .fetchNodes.mockResolvedValue({ agents: [...initialNodes, expectedNode] }) + .mockResolvedValueOnce({ agents: initialNodes }); + + jest + .spyOn(useTeleport, 'default') + .mockReturnValue({ nodeService } as TeleportContext); + + const { result, waitForValueToChange } = renderHook(() => + usePollForConnectMyComputerNode({ + username: 'alice', + clusterId: 'foo', + pingInterval: 1, + }) + ); + + expect(result.error).toBeUndefined(); + expect(result.current.node).toBeFalsy(); + expect(result.current.isPolling).toBe(true); + + await waitForValueToChange(() => result.current.node, { interval: 3 }); + + expect(result.current.node).toEqual(expectedNode); + expect(result.current.isPolling).toBe(false); + }); +}); diff --git a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx index fc6d6a1aa6fa9..5e3ad660439ec 100644 --- a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx +++ b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx @@ -14,22 +14,45 @@ * limitations under the License. */ -import React from 'react'; +import React, { useEffect, useCallback, useState, useRef } from 'react'; import { ButtonSecondary } from 'design/Button'; import { Platform, getPlatform } from 'design/platform'; import { Text, Flex } from 'design'; +import * as Icons from 'design/Icon'; import { MenuButton, MenuItem } from 'shared/components/MenuAction'; import { Path, makeDeepLinkWithSafeInput } from 'shared/deepLinks'; +import * as constants from 'shared/constants'; import useTeleport from 'teleport/useTeleport'; +import { Node } from 'teleport/services/nodes'; -import { ActionButtons, Header, StyledBox } from 'teleport/Discover/Shared'; +import { + ActionButtons, + Header, + StyledBox, + TextIcon, +} from 'teleport/Discover/Shared'; +import { usePoll } from 'teleport/Discover/Shared/usePoll'; + +import { + HintBox, + SuccessBox, + WaitingInfo, +} from 'teleport/Discover/Shared/HintBox'; import type { AgentStepProps } from '../../types'; -export function SetupConnect(props: AgentStepProps) { +export function SetupConnect( + props: AgentStepProps & { + pingInterval?: number; + showHintTimeout?: number; + } +) { + const pingInterval = props.pingInterval || 1000 * 3; // 3 seconds + const showHintTimeout = props.showHintTimeout || 1000 * 60 * 5; // 5 minutes const ctx = useTeleport(); + const clusterId = ctx.storeUser.getClusterId(); const { cluster, username } = ctx.storeUser.state; const platform = getPlatform(); const downloadLinks = getConnectDownloadLinks(platform, cluster.proxyVersion); @@ -39,6 +62,89 @@ export function SetupConnect(props: AgentStepProps) { path: Path.ConnectMyComputer, }); + const { node, isPolling } = usePollForConnectMyComputerNode({ + username, + clusterId, + pingInterval, + }); + + const [showHint, setShowHint] = useState(false); + useEffect(() => { + if (isPolling) { + const id = window.setTimeout(() => setShowHint(true), showHintTimeout); + + return () => window.clearTimeout(id); + } + }, [isPolling, showHintTimeout]); + + let hint: JSX.Element; + if (showHint && !node) { + hint = ( + // Override max-width to match StyledBox's max-width. + + + + There are a couple of possible reasons for why we haven't been able + to detect your computer. + + +
    p.theme.space[3]}px; + `} + > +
  • + + You did not start Connect My Computer in Teleport Connect yet. + +
  • +
  • + + The Teleport agent started by Teleport Connect could not join + this Teleport cluster. Check if the Connect My Computer tab in + Teleport Connect shows any error messages. + +
  • +
  • + + The computer you are trying to add has already joined the + Teleport cluster before you entered this page. + +
  • +
+ + + We'll continue to look for the computer whilst you diagnose the + issue. + +
+
+ ); + } else if (node) { + hint = ( + + + Your computer, {node.hostname}, has been detected! + + + ); + } else { + hint = ( + + + + + After your computer is connected to the cluster, we’ll automatically + detect it. + + ); + } + return (
Set Up Teleport Connect
@@ -50,7 +156,7 @@ export function SetupConnect(props: AgentStepProps) { Teleport Connect is a native desktop application for browsing and accessing your resources. It can also connect your computer as an SSH resource and scope access to a unique role so it is not automatically - shared with anyone else in the cluster. + shared with all users in the cluster.

Once you’ve downloaded Teleport Connect, run the installer to add it @@ -80,15 +186,87 @@ export function SetupConnect(props: AgentStepProps) { + {hint} + {}} - disableProceed={true} + onProceed={props.nextStep} + disableProceed={!node} onPrev={props.prevStep} />
); } +/** + * usePollForConnectMyComputerNode polls for a Connect My Computer node that joined the cluster + * after starting opening the SetupConnect step. + * + * The first polling request fills out a set of node IDs (initialNodeIdsRef). Subsequent requests + * check the returned nodes against this set. The hook stops polling as soon as a node that is not + * in the set was found. + * + * Unlike the DownloadScript step responsible for adding a server, we don't have a unique ID that + * identifies the node that the user added after following the steps from the guided flow. In + * theory, we could make the deep link button pass such ID to Connect, but the user would still be + * able to just launch the app directly and not use the link. + * + * Because of that, we must depend on comparing the list of nodes against the initial set of IDs. + */ +export const usePollForConnectMyComputerNode = (args: { + username: string; + clusterId: string; + pingInterval: number; +}): { + node: Node | undefined; + isPolling: boolean; +} => { + const ctx = useTeleport(); + const [isPolling, setIsPolling] = useState(true); + const initialNodeIdsRef = useRef>(null); + + const node = usePoll( + useCallback( + async signal => { + const request = { + query: `labels["${constants.ConnectMyComputerNodeOwnerLabel}"] == "${args.username}"`, + // An arbitrary limit where we bank on the fact that no one is going to have 50 Connect My Computer + // nodes assigned to them running at the same time. + limit: 50, + }; + + const response = await ctx.nodeService.fetchNodes( + args.clusterId, + request, + signal + ); + + // Fill out the set with node IDs if it's empty. + if (initialNodeIdsRef.current === null) { + initialNodeIdsRef.current = new Set( + response.agents.map(agent => agent.id) + ); + return null; + } + + // On subsequent requests, compare the nodes from the response against the set. + const node = response.agents.find( + agent => !initialNodeIdsRef.current.has(agent.id) + ); + + if (node) { + setIsPolling(false); + return node; + } + }, + [ctx.nodeService, args.clusterId, args.username] + ), + isPolling, + args.pingInterval + ); + + return { node, isPolling }; +}; + type DownloadLink = { text: string; url: string }; const DownloadConnect = (props: { downloadLinks: Array }) => { From 0ec9a23485b8b169110be38022e5af698c9e9ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Thu, 9 Nov 2023 16:40:54 +0100 Subject: [PATCH 4/8] Add stories for polling in SetupConnect --- .../SetupConnect/SetupConnect.story.tsx | 91 +++++++++++++++++-- 1 file changed, 81 insertions(+), 10 deletions(-) diff --git a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.story.tsx b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.story.tsx index 087f79042c2f8..6fd736d598a5a 100644 --- a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.story.tsx +++ b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.story.tsx @@ -22,34 +22,105 @@ import { } from 'shared/components/OverrideUserAgent'; import { ContextProvider } from 'teleport'; +import cfg from 'teleport/config'; import { UserContext } from 'teleport/User/UserContext'; import { createTeleportContext } from 'teleport/mocks/contexts'; import { makeDefaultUserPreferences } from 'teleport/services/userPreferences/userPreferences'; import { SetupConnect } from './SetupConnect'; +const { worker, rest } = window.msw; + +const oneDay = 1000 * 60 * 60 * 24; + +const setupConnectProps = { + prevStep: () => {}, + nextStep: () => {}, + // Set high default intervals and timeouts so that stories don't poll for no reason. + pingInterval: oneDay, + showHintTimeout: oneDay, +}; + export default { title: 'Teleport/Discover/ConnectMyComputer/SetupConnect', + decorators: [ + Story => { + worker.resetHandlers(); + return ; + }, + ], +}; + +const workerNoNodes = () => { + worker.use( + rest.get(cfg.api.nodesPath, (req, res, ctx) => res(ctx.json({ items: [] }))) + ); +}; + +export const macOS = () => { + workerNoNodes(); + return ( + + + + + + ); +}; + +export const Linux = () => { + workerNoNodes(); + return ( + + + + + + ); +}; + +export const Polling = () => { + workerNoNodes(); + + return ( + + + + ); }; -export const macOS = () => ( - +export const PollingSuccess = () => { + worker.use( + rest.get(cfg.api.nodesPath, (req, res, ctx) => { + return res(ctx.json({ items: [{ id: '1234', hostname: 'foo' }] })); + }) + ); + worker.use( + rest.get(cfg.api.nodesPath, (req, res, ctx) => { + return res.once(ctx.json({ items: [] })); + }) + ); + + return ( - {}} /> + - -); + ); +}; -export const Linux = () => ( - +export const HintTimeout = () => { + workerNoNodes(); + + return ( - {}} /> + - -); + ); +}; const Provider = ({ children }) => { const ctx = createTeleportContext(); + // The proxy version is set mostly so that the download links point to actual artifacts. ctx.storeUser.state.cluster.proxyVersion = '14.1.0'; const preferences = makeDefaultUserPreferences(); From ea9ee36383938b5d66681d4357696a022b91a76c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Fri, 10 Nov 2023 14:54:03 +0100 Subject: [PATCH 5/8] Rename hint to pollingStatus --- .../ConnectMyComputer/SetupConnect/SetupConnect.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx index 5e3ad660439ec..9520b721ab367 100644 --- a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx +++ b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx @@ -77,9 +77,9 @@ export function SetupConnect( } }, [isPolling, showHintTimeout]); - let hint: JSX.Element; + let pollingStatus: JSX.Element; if (showHint && !node) { - hint = ( + pollingStatus = ( // Override max-width to match StyledBox's max-width. @@ -122,7 +122,7 @@ export function SetupConnect( ); } else if (node) { - hint = ( + pollingStatus = ( Your computer, {node.hostname}, has been detected! @@ -130,7 +130,7 @@ export function SetupConnect( ); } else { - hint = ( + pollingStatus = ( - {hint} + {pollingStatus} Date: Fri, 10 Nov 2023 16:36:30 +0100 Subject: [PATCH 6/8] Refactor SetupConnect stories to use msw from package --- .../SetupConnect/SetupConnect.story.tsx | 126 +++++++++--------- 1 file changed, 66 insertions(+), 60 deletions(-) diff --git a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.story.tsx b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.story.tsx index 6fd736d598a5a..f91876584e9b9 100644 --- a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.story.tsx +++ b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.story.tsx @@ -16,6 +16,9 @@ import React from 'react'; +import { initialize, mswLoader } from 'msw-storybook-addon'; +import { rest } from 'msw'; + import { OverrideUserAgent, UserAgent, @@ -29,8 +32,6 @@ import { makeDefaultUserPreferences } from 'teleport/services/userPreferences/us import { SetupConnect } from './SetupConnect'; -const { worker, rest } = window.msw; - const oneDay = 1000 * 60 * 60 * 24; const setupConnectProps = { @@ -41,81 +42,86 @@ const setupConnectProps = { showHintTimeout: oneDay, }; +initialize(); + export default { title: 'Teleport/Discover/ConnectMyComputer/SetupConnect', - decorators: [ - Story => { - worker.resetHandlers(); - return ; - }, - ], + loaders: [mswLoader], }; -const workerNoNodes = () => { - worker.use( - rest.get(cfg.api.nodesPath, (req, res, ctx) => res(ctx.json({ items: [] }))) - ); -}; +const noNodesHandler = rest.get(cfg.api.nodesPath, (req, res, ctx) => + res(ctx.json({ items: [] })) +); -export const macOS = () => { - workerNoNodes(); - return ( - - - - - - ); -}; +export const macOS = () => ( + + + + + +); -export const Linux = () => { - workerNoNodes(); - return ( - - - - - - ); +macOS.parameters = { + msw: { + handlers: [noNodesHandler], + }, }; -export const Polling = () => { - workerNoNodes(); - - return ( +export const Linux = () => ( + - ); + +); + +Linux.parameters = { + msw: { + handlers: [noNodesHandler], + }, }; -export const PollingSuccess = () => { - worker.use( - rest.get(cfg.api.nodesPath, (req, res, ctx) => { - return res(ctx.json({ items: [{ id: '1234', hostname: 'foo' }] })); - }) - ); - worker.use( - rest.get(cfg.api.nodesPath, (req, res, ctx) => { - return res.once(ctx.json({ items: [] })); - }) - ); +export const Polling = () => ( + + + +); - return ( - - - - ); +Polling.parameters = { + msw: { + handlers: [noNodesHandler], + }, }; -export const HintTimeout = () => { - workerNoNodes(); +export const PollingSuccess = () => ( + + + +); + +PollingSuccess.parameters = { + msw: { + handlers: [ + rest.get(cfg.api.nodesPath, (req, res, ctx) => { + return res.once(ctx.json({ items: [] })); + }), + rest.get(cfg.api.nodesPath, (req, res, ctx) => { + return res(ctx.json({ items: [{ id: '1234', hostname: 'foo' }] })); + }), + ], + }, +}; - return ( - - - - ); +export const HintTimeout = () => ( + + + +); + +HintTimeout.parameters = { + msw: { + handlers: [noNodesHandler], + }, }; const Provider = ({ children }) => { From 2b301daf233bd40628eb81c924dafff967a8d85a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Mon, 13 Nov 2023 11:38:25 +0100 Subject: [PATCH 7/8] Improve hint timeout message --- .../SetupConnect/SetupConnect.story.tsx | 24 ++++++++++--------- .../SetupConnect/SetupConnect.tsx | 9 ++++++- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.story.tsx b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.story.tsx index f91876584e9b9..8a9eab3dd228a 100644 --- a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.story.tsx +++ b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.story.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; - +import { MemoryRouter } from 'react-router'; import { initialize, mswLoader } from 'msw-storybook-addon'; import { rest } from 'msw'; @@ -135,15 +135,17 @@ const Provider = ({ children }) => { const updateClusterPinnedResources = () => Promise.resolve(); return ( - - {children} - + + + {children} + + ); }; diff --git a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx index 9520b721ab367..9acdb8a1886c1 100644 --- a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx +++ b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx @@ -15,6 +15,7 @@ */ import React, { useEffect, useCallback, useState, useRef } from 'react'; +import { Link } from 'react-router-dom'; import { ButtonSecondary } from 'design/Button'; import { Platform, getPlatform } from 'design/platform'; @@ -24,6 +25,7 @@ import { MenuButton, MenuItem } from 'shared/components/MenuAction'; import { Path, makeDeepLinkWithSafeInput } from 'shared/deepLinks'; import * as constants from 'shared/constants'; +import cfg from 'teleport/config'; import useTeleport from 'teleport/useTeleport'; import { Node } from 'teleport/services/nodes'; @@ -109,7 +111,12 @@ export function SetupConnect(
  • The computer you are trying to add has already joined the - Teleport cluster before you entered this page. + Teleport cluster before you entered this page. If that's the + case, you can go back to{' '} + + the resources + {' '} + and connect to it.
  • From 0cebd4184314ccf0f0bd6bdecc74e2fd3d4c910e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Mon, 13 Nov 2023 11:44:09 +0100 Subject: [PATCH 8/8] Explain the need for initialNodeIdsRef --- .../ConnectMyComputer/SetupConnect/SetupConnect.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx index 9acdb8a1886c1..01c7882c8053b 100644 --- a/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx +++ b/web/packages/teleport/src/Discover/ConnectMyComputer/SetupConnect/SetupConnect.tsx @@ -212,6 +212,9 @@ export function SetupConnect( * check the returned nodes against this set. The hook stops polling as soon as a node that is not * in the set was found. * + * There can be multiple nodes matching the search criteria and we want the one that was added only + * after the user has started the guided flow, hence why we need to keep track of the IDs in a set. + * * Unlike the DownloadScript step responsible for adding a server, we don't have a unique ID that * identifies the node that the user added after following the steps from the guided flow. In * theory, we could make the deep link button pass such ID to Connect, but the user would still be @@ -236,8 +239,8 @@ export const usePollForConnectMyComputerNode = (args: { async signal => { const request = { query: `labels["${constants.ConnectMyComputerNodeOwnerLabel}"] == "${args.username}"`, - // An arbitrary limit where we bank on the fact that no one is going to have 50 Connect My Computer - // nodes assigned to them running at the same time. + // An arbitrary limit where we bank on the fact that no one is going to have 50 Connect My + // Computer nodes assigned to them running at the same time. limit: 50, };