Skip to content

Commit 7b74109

Browse files
committed
chore(e2e): create multiple VMs in one go through the CLI
1 parent 4d1d294 commit 7b74109

File tree

1 file changed

+42
-92
lines changed

1 file changed

+42
-92
lines changed

e2e/cluster/cmx/cluster.go

Lines changed: 42 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ import (
1212
"sync"
1313
"testing"
1414
"time"
15-
16-
"github.com/google/uuid"
17-
"golang.org/x/sync/errgroup"
1815
)
1916

2017
type ClusterInput struct {
@@ -36,87 +33,40 @@ type Cluster struct {
3633
}
3734

3835
type Node struct {
39-
ID string `json:"id"`
40-
Name string `json:"name"`
36+
ID string `json:"id"`
37+
Name string `json:"name"`
38+
NetworkID string `json:"network_id"`
4139

4240
privateIP string `json:"-"`
4341
sshEndpoint string `json:"-"`
4442
adminConsoleURL string `json:"-"`
4543
}
4644

4745
type Network struct {
48-
ID string `json:"id"`
49-
Name string `json:"name"`
46+
ID string `json:"id"`
5047
}
5148

5249
func NewCluster(in *ClusterInput) *Cluster {
5350
c := &Cluster{t: in.T, supportBundleNodeIndex: in.SupportBundleNodeIndex}
54-
c.Nodes = make([]Node, in.Nodes)
5551

56-
network, err := NewNetwork(in)
52+
nodes, err := NewNodes(in)
5753
if err != nil {
58-
in.T.Fatalf("failed to create network: %v", err)
59-
}
60-
c.network = network
61-
62-
g := new(errgroup.Group)
63-
var mu sync.Mutex
64-
65-
for i := range c.Nodes {
66-
func(i int) {
67-
g.Go(func() error {
68-
node, err := NewNode(in, i, network.ID)
69-
if err != nil {
70-
return fmt.Errorf("create node %d: %w", i, err)
71-
}
72-
in.T.Logf("node%d created with ID %s", i, node.ID)
73-
mu.Lock()
74-
c.Nodes[i] = *node
75-
mu.Unlock()
76-
return nil
77-
})
78-
}(i)
79-
}
80-
81-
if err := g.Wait(); err != nil {
8254
in.T.Fatalf("failed to create nodes: %v", err)
8355
}
56+
in.T.Logf("cluster created with network ID %s", nodes[0].NetworkID)
57+
c.Nodes = nodes
58+
c.network = &Network{ID: nodes[0].NetworkID}
8459

8560
return c
8661
}
8762

88-
func NewNetwork(in *ClusterInput) (*Network, error) {
89-
name := fmt.Sprintf("ec-e2e-%s", uuid.New().String())
90-
in.T.Logf("creating network %s", name)
91-
92-
output, err := exec.Command("replicated", "network", "create", "--name", name, "--wait", "5m", "-ojson").Output() // stderr can break json parsing
93-
if err != nil {
94-
if exitErr, ok := err.(*exec.ExitError); ok {
95-
return nil, fmt.Errorf("create network %s: %w: stderr: %s: stdout: %s", name, err, string(exitErr.Stderr), string(output))
96-
}
97-
return nil, fmt.Errorf("create network %s: %w: stdout: %s", name, err, string(output))
98-
}
99-
100-
var networks []Network
101-
if err := json.Unmarshal(output, &networks); err != nil {
102-
return nil, fmt.Errorf("parse networks output: %v: %s", err, string(output))
103-
}
104-
if len(networks) != 1 {
105-
return nil, fmt.Errorf("expected 1 network, got %d", len(networks))
106-
}
107-
network := &networks[0]
108-
in.T.Logf("Network created with ID %s", network.ID)
109-
return network, nil
110-
}
111-
112-
func NewNode(in *ClusterInput, index int, networkID string) (*Node, error) {
113-
nodeName := fmt.Sprintf("node%d", index)
114-
in.T.Logf("creating node %s", nodeName)
63+
func NewNodes(in *ClusterInput) ([]Node, error) {
64+
in.T.Logf("creating %s nodes", strconv.Itoa(in.Nodes))
11565

11666
args := []string{
11767
"vm", "create",
118-
"--name", nodeName,
119-
"--network", networkID,
68+
"--name", "ec-test-suite",
69+
"--count", strconv.Itoa(in.Nodes),
12070
"--wait", "5m",
12171
"-ojson",
12272
}
@@ -139,54 +89,54 @@ func NewNode(in *ClusterInput, index int, networkID string) (*Node, error) {
13989
output, err := exec.Command("replicated", args...).Output() // stderr can break json parsing
14090
if err != nil {
14191
if exitErr, ok := err.(*exec.ExitError); ok {
142-
return nil, fmt.Errorf("create node %s: %w: stderr: %s: stdout: %s", nodeName, err, string(exitErr.Stderr), string(output))
92+
return nil, fmt.Errorf("create nodes: %w: stderr: %s: stdout: %s", err, string(exitErr.Stderr), string(output))
14393
}
144-
return nil, fmt.Errorf("create node %s: %w: stdout: %s", nodeName, err, string(output))
94+
return nil, fmt.Errorf("create nodes: %w: stdout: %s", err, string(output))
14595
}
14696

14797
var nodes []Node
14898
if err := json.Unmarshal(output, &nodes); err != nil {
14999
return nil, fmt.Errorf("unmarshal node: %v: %s", err, string(output))
150100
}
151-
if len(nodes) != 1 {
152-
return nil, fmt.Errorf("expected 1 node, got %d", len(nodes))
153-
}
154-
node := nodes[0]
155101

156102
// TODO (@salah): remove this once the bug is fixed in CMX
157103
// note: the vm gets marked as ready before the services are actually running
158104
time.Sleep(30 * time.Second)
159105

160-
sshEndpoint, err := getSSHEndpoint(node.ID)
161-
if err != nil {
162-
return nil, fmt.Errorf("get ssh endpoint for node %s: %v", nodeName, err)
163-
}
164-
node.sshEndpoint = sshEndpoint
106+
for i := range nodes {
107+
sshEndpoint, err := getSSHEndpoint(nodes[i].ID)
108+
if err != nil {
109+
return nil, fmt.Errorf("get ssh endpoint for node %s: %v", nodes[i].ID, err)
110+
}
111+
nodes[i].sshEndpoint = sshEndpoint
165112

166-
privateIP, err := discoverPrivateIP(node)
167-
if err != nil {
168-
return nil, fmt.Errorf("discover node private IP: %v", err)
169-
}
170-
node.privateIP = privateIP
113+
privateIP, err := discoverPrivateIP(nodes[i])
114+
if err != nil {
115+
return nil, fmt.Errorf("discover node private IP: %v", err)
116+
}
117+
nodes[i].privateIP = privateIP
171118

172-
if err := ensureAssetsDir(node); err != nil {
173-
return nil, fmt.Errorf("ensure assets dir on node %s: %v", node.Name, err)
174-
}
119+
if err := ensureAssetsDir(nodes[i]); err != nil {
120+
return nil, fmt.Errorf("ensure assets dir on node %s: %v", nodes[i].ID, err)
121+
}
175122

176-
if err := copyScriptsToNode(node); err != nil {
177-
return nil, fmt.Errorf("copy scripts to node %s: %v", node.Name, err)
178-
}
123+
if err := copyScriptsToNode(nodes[i]); err != nil {
124+
return nil, fmt.Errorf("copy scripts to node %s: %v", nodes[i].ID, err)
125+
}
179126

180-
if index == 0 {
181-
in.T.Logf("exposing port 30003 on node %s", node.Name)
182-
hostname, err := exposePort(node, "30003")
183-
if err != nil {
184-
return nil, fmt.Errorf("expose port: %v", err)
127+
if in.Nodes == 1 {
128+
in.T.Logf("exposing port 30003 on node %s", nodes[i].ID)
129+
hostname, err := exposePort(nodes[i], "30003")
130+
if err != nil {
131+
return nil, fmt.Errorf("expose port: %v", err)
132+
}
133+
nodes[i].adminConsoleURL = fmt.Sprintf("http://%s", hostname)
185134
}
186-
node.adminConsoleURL = fmt.Sprintf("http://%s", hostname)
135+
136+
in.T.Logf("node %d created with ID %s and private IP %s", i, nodes[i].ID, nodes[i].privateIP)
187137
}
188138

189-
return &node, nil
139+
return nodes, nil
190140
}
191141

192142
func discoverPrivateIP(node Node) (string, error) {
@@ -334,7 +284,7 @@ func (c *Cluster) Destroy() {
334284
for _, node := range c.Nodes {
335285
output, err := exec.Command("replicated", "vm", "rm", node.ID).CombinedOutput()
336286
if err != nil {
337-
c.t.Logf("failed to destroy node %s: %v: %s", node.Name, err, string(output))
287+
c.t.Logf("failed to destroy node %s: %v: %s", node.ID, err, string(output))
338288
}
339289
}
340290
}

0 commit comments

Comments
 (0)