Skip to content

Commit 3beab91

Browse files
committed
feat(ec): update node join instructions for improved join experience
1 parent 8fadf68 commit 3beab91

File tree

4 files changed

+52
-49
lines changed

4 files changed

+52
-49
lines changed

pkg/embeddedcluster/node_join.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/base64"
66
"fmt"
7+
"net"
78
"strings"
89
"sync"
910
"time"
@@ -208,29 +209,37 @@ func findInternalIPAddress(addresses []corev1.NodeAddress) *corev1.NodeAddress {
208209
return nil
209210
}
210211

211-
// GenerateAddNodeCommand returns the command a user should run to add a node with the provided token
212-
// the command will be of the form 'embeddedcluster node join ip:port UUID'
213-
func GenerateAddNodeCommand(ctx context.Context, kbClient kbclient.Client, token string, isAirgap bool) (string, error) {
212+
// GenerateAddNodeCommand returns a list of commands a user should run to add a node with the
213+
// provided token.
214+
func GenerateAddNodeCommand(ctx context.Context, kbClient kbclient.Client, token string) ([]string, error) {
214215
installation, err := GetCurrentInstallation(ctx, kbClient)
215216
if err != nil {
216-
return "", fmt.Errorf("failed to get current installation: %w", err)
217+
return nil, fmt.Errorf("failed to get current installation: %w", err)
217218
}
218219

219220
binaryName := installation.Spec.BinaryName
220221

221222
// get the IP of a controller node
222223
nodeIP, err := getControllerNodeIP(ctx, kbClient)
223224
if err != nil {
224-
return "", fmt.Errorf("failed to get controller node IP: %w", err)
225+
return nil, fmt.Errorf("failed to get controller node IP: %w", err)
225226
}
226227

227228
// get the port of the 'admin-console' service
228229
port, err := getAdminConsolePort(ctx, kbClient)
229230
if err != nil {
230-
return "", fmt.Errorf("failed to get admin console port: %w", err)
231+
return nil, fmt.Errorf("failed to get admin console port: %w", err)
231232
}
232233

233-
return fmt.Sprintf("sudo ./%s join %s:%d %s", binaryName, nodeIP, port, token), nil
234+
address := net.JoinHostPort(nodeIP, fmt.Sprintf("%d", port))
235+
236+
commands := []string{
237+
fmt.Sprintf("curl -k https://%s/api/v1/embedded-cluster/binary -o %s.tar.gz", address, binaryName),
238+
fmt.Sprintf("tar -xvf %s.tar.gz", binaryName),
239+
fmt.Sprintf("sudo ./%s join %s %s", binaryName, address, token),
240+
}
241+
242+
return commands, nil
234243
}
235244

236245
// GenerateK0sJoinCommand returns the k0s node join command, without the token but with all other required flags

pkg/embeddedcluster/node_join_test.go

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,25 +80,18 @@ func TestGenerateAddNodeCommand(t *testing.T) {
8080

8181
req := require.New(t)
8282

83-
// Generate the add node command for online
84-
gotCommand, err := GenerateAddNodeCommand(context.Background(), kbClient, "token", false)
83+
gotCommand, err := GenerateAddNodeCommand(context.Background(), kbClient, "token")
8584
if err != nil {
8685
t.Fatalf("Failed to generate add node command: %v", err)
8786
}
8887

8988
// Verify the generated command
90-
wantCommand := "sudo ./my-app join 192.168.0.100:30000 token"
91-
req.Equal(wantCommand, gotCommand)
92-
93-
// Generate the add node command for airgap
94-
gotCommand, err = GenerateAddNodeCommand(context.Background(), kbClient, "token", true)
95-
if err != nil {
96-
t.Fatalf("Failed to generate add node command: %v", err)
89+
wantCommands := []string{
90+
"curl -k https://192.168.0.100:30000/api/v1/embedded-cluster/binary -o my-app.tar.gz",
91+
"tar -xvf my-app.tar.gz",
92+
"sudo ./my-app join 192.168.0.100:30000 token",
9793
}
98-
99-
// Verify the generated command
100-
wantCommand = "sudo ./my-app join 192.168.0.100:30000 token"
101-
req.Equal(wantCommand, gotCommand)
94+
req.Equal(wantCommands, gotCommand)
10295
}
10396

10497
func TestGetAllNodeIPAddresses(t *testing.T) {

pkg/handlers/embedded_cluster_node_join_command.go

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"github.com/google/uuid"
99
"github.com/replicatedhq/embedded-cluster/kinds/types/join"
1010

11-
"github.com/replicatedhq/kots/pkg/api/handlers/types"
11+
"github.com/replicatedhq/kots/pkg/api/handlers/types"
1212
"github.com/replicatedhq/kots/pkg/embeddedcluster"
1313
"github.com/replicatedhq/kots/pkg/k8sutil"
1414
"github.com/replicatedhq/kots/pkg/kotsutil"
@@ -38,35 +38,22 @@ func (h *Handler) GenerateEmbeddedClusterNodeJoinCommand(w http.ResponseWriter,
3838
return
3939
}
4040

41-
apps, err := store.GetStore().ListInstalledApps()
42-
if err != nil {
43-
logger.Error(fmt.Errorf("failed to list installed apps: %w", err))
44-
w.WriteHeader(http.StatusInternalServerError)
45-
return
46-
}
47-
if len(apps) == 0 {
48-
logger.Error(fmt.Errorf("no installed apps found"))
49-
w.WriteHeader(http.StatusInternalServerError)
50-
return
51-
}
52-
app := apps[0]
53-
5441
kbClient, err := h.GetKubeClient(r.Context())
5542
if err != nil {
5643
logger.Error(fmt.Errorf("failed to get kubeclient: %w", err))
5744
w.WriteHeader(http.StatusInternalServerError)
5845
return
5946
}
6047

61-
nodeJoinCommand, err := embeddedcluster.GenerateAddNodeCommand(r.Context(), kbClient, token, app.IsAirgap)
48+
nodeJoinCommands, err := embeddedcluster.GenerateAddNodeCommand(r.Context(), kbClient, token)
6249
if err != nil {
6350
logger.Error(fmt.Errorf("failed to generate add node command: %w", err))
6451
w.WriteHeader(http.StatusInternalServerError)
6552
return
6653
}
6754

6855
JSON(w, http.StatusOK, types.GenerateEmbeddedClusterNodeJoinCommandResponse{
69-
Command: []string{nodeJoinCommand},
56+
Command: nodeJoinCommands,
7057
})
7158
}
7259

web/src/components/apps/EmbeddedClusterManagement.tsx

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ const EmbeddedClusterManagement = ({
178178
});
179179

180180
type AddNodeCommandResponse = {
181-
command: string;
181+
command: string[];
182182
expiry: string;
183183
};
184184

@@ -438,6 +438,12 @@ const EmbeddedClusterManagement = ({
438438
);
439439
};
440440

441+
const addNodesCommandInstructions = [
442+
"Download the installation assets",
443+
"Extract the installation assets",
444+
"Join the node to the cluster",
445+
];
446+
441447
const AddNodeCommands = () => {
442448
return (
443449
<>
@@ -506,18 +512,26 @@ const EmbeddedClusterManagement = ({
506512
</p>
507513
)}
508514
{!generateAddNodeCommandLoading && generateAddNodeCommand?.command && (
509-
<>
510-
<CodeSnippet
511-
key={selectedNodeTypes.toString()}
512-
language="bash"
513-
canCopy={true}
514-
onCopyText={
515-
<span className="u-textColor--success">Copied!</span>
516-
}
517-
>
518-
{generateAddNodeCommand?.command}
519-
</CodeSnippet>
520-
</>
515+
<div className="tw-flex tw-flex-col tw-gap-4 tw-mt-4">
516+
{generateAddNodeCommand.command.map((command, index) => (
517+
<div key={command}>
518+
{addNodesCommandInstructions[index] && (
519+
<p className="tw-text-gray-600 tw-font-semibold">
520+
{addNodesCommandInstructions[index]}
521+
</p>
522+
)}
523+
<CodeSnippet
524+
language="bash"
525+
canCopy={true}
526+
onCopyText={
527+
<span className="u-textColor--success">Copied!</span>
528+
}
529+
>
530+
{command}
531+
</CodeSnippet>
532+
</div>
533+
))}
534+
</div>
521535
)}
522536
</div>
523537
</>

0 commit comments

Comments
 (0)