Skip to content

Commit b6276cf

Browse files
warpforklmars
authored andcommitted
test: Improve cluster HTTP API
Signed-off-by: Eric Myhre <eric@flynn.io> Signed-off-by: Lewis Marshall <lewis@lmars.net>
1 parent f949e6a commit b6276cf

File tree

6 files changed

+205
-155
lines changed

6 files changed

+205
-155
lines changed

test/cluster/client/client.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package testcluster
2+
3+
import (
4+
"crypto/tls"
5+
"errors"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"time"
10+
11+
"github.com/flynn/flynn/host/types"
12+
"github.com/flynn/flynn/pkg/httpclient"
13+
tc "github.com/flynn/flynn/test/cluster"
14+
)
15+
16+
type Client struct {
17+
*httpclient.Client
18+
cluster *tc.Cluster
19+
}
20+
21+
var ErrNotFound = errors.New("testcluster: resource not found")
22+
23+
func NewClient(endpoint string) (*Client, error) {
24+
httpClient := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{ServerName: "ci.flynn.io"}}}
25+
client := &Client{
26+
Client: &httpclient.Client{
27+
ErrNotFound: ErrNotFound,
28+
URL: endpoint,
29+
HTTP: httpClient,
30+
},
31+
}
32+
var cluster tc.Cluster
33+
if err := client.Get("", &cluster); err != nil {
34+
return nil, err
35+
}
36+
client.cluster = &cluster
37+
return client, nil
38+
}
39+
40+
func (c *Client) Size() int {
41+
return c.cluster.Size()
42+
}
43+
44+
func (c *Client) BackoffPeriod() time.Duration {
45+
return c.cluster.BackoffPeriod
46+
}
47+
48+
func (c *Client) AddHost(ch chan *host.HostEvent) (string, error) {
49+
var instance tc.Instance
50+
if err := c.Post("", nil, &instance); err != nil {
51+
return "", err
52+
}
53+
c.cluster.Instances = append(c.cluster.Instances, &instance)
54+
for {
55+
select {
56+
case event := <-ch:
57+
if event.HostID == instance.ID {
58+
return instance.ID, nil
59+
}
60+
case <-time.After(60 * time.Second):
61+
return "", fmt.Errorf("timed out waiting for new host")
62+
}
63+
}
64+
}
65+
66+
func (c *Client) RemoveHost(id string) error {
67+
return c.Delete("/" + id)
68+
}
69+
70+
func (c *Client) DumpLogs(out io.Writer) error {
71+
res, err := c.RawReq("GET", "/dump-logs", nil, nil, nil)
72+
if err != nil {
73+
return err
74+
}
75+
_, err = io.Copy(out, res.Body)
76+
return err
77+
}

test/cluster/cluster.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"os/user"
1212
"strconv"
1313
"strings"
14+
"sync"
1415
"text/template"
1516
"time"
1617

@@ -36,6 +37,9 @@ type Cluster struct {
3637
ControllerKey string `json:"controller_key"`
3738
RouterIP string `json:"router_ip"`
3839

40+
discMtx sync.Mutex
41+
disc *discoverd.Client
42+
3943
bc BootConfig
4044
vm *VMManager
4145
out io.Writer
@@ -58,6 +62,15 @@ func (i instances) Get(id string) (*Instance, error) {
5862
return nil, fmt.Errorf("no such host: %s", id)
5963
}
6064

65+
func (c *Cluster) discoverdClient() *discoverd.Client {
66+
c.discMtx.Lock()
67+
defer c.discMtx.Unlock()
68+
if c.disc == nil {
69+
c.disc = discoverd.NewClientWithURL(fmt.Sprintf("http://%s:1111", c.RouterIP))
70+
}
71+
return c.disc
72+
}
73+
6174
type Streams struct {
6275
Stdin io.Reader
6376
Stdout io.Writer
@@ -191,12 +204,40 @@ func (c *Cluster) RemoveHost(id string) error {
191204
}
192205
c.log("removing host", id)
193206

207+
// Clean shutdown requires waiting for that host to unadvertise on discoverd.
208+
// Specifically: Wait for router-api services to disappear to indicate host
209+
// removal (rather than using StreamHostEvents), so that other
210+
// tests won't try and connect to this host via service discovery.
211+
events := make(chan *discoverd.Event)
212+
stream, err := c.discoverdClient().Service("router-api").Watch(events)
213+
if err != nil {
214+
return err
215+
}
216+
defer stream.Close()
217+
218+
// ssh into the host and tell the flynn-host daemon to stop
194219
var cmd string
195220
switch c.bc.Backend {
196221
case "libvirt-lxc":
197222
cmd = "sudo start-stop-daemon --stop --pidfile /var/run/flynn-host.pid --retry 15"
198223
}
199-
return inst.Run(cmd, nil)
224+
if err := inst.Run(cmd, nil); err != nil {
225+
return err
226+
}
227+
228+
loop:
229+
for {
230+
select {
231+
case event := <-events:
232+
if event.Kind == discoverd.EventKindDown {
233+
break loop
234+
}
235+
case <-time.After(20 * time.Second):
236+
return fmt.Errorf("timed out waiting for host removal")
237+
}
238+
}
239+
240+
return nil
200241
}
201242

202243
func (c *Cluster) Size() int {

test/main.go

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,12 @@ import (
44
"bytes"
55
"crypto/rand"
66
"crypto/rsa"
7-
"crypto/tls"
87
"crypto/x509"
9-
"encoding/json"
108
"encoding/pem"
119
"fmt"
1210
"io"
1311
"io/ioutil"
1412
"log"
15-
"net/http"
1613
"os"
1714
"os/exec"
1815
"regexp"
@@ -27,6 +24,7 @@ import (
2724
"github.com/flynn/flynn/pkg/shutdown"
2825
"github.com/flynn/flynn/test/arg"
2926
"github.com/flynn/flynn/test/cluster"
27+
"github.com/flynn/flynn/test/cluster/client"
3028
)
3129

3230
var sshWrapper = template.Must(template.New("ssh").Parse(`
@@ -38,8 +36,7 @@ ssh -o LogLevel=FATAL -o IdentitiesOnly=yes -o UserKnownHostsFile=/dev/null -o S
3836
var args *arg.Args
3937
var flynnrc string
4038
var routerIP string
41-
var testCluster *cluster.Cluster
42-
var httpClient *http.Client
39+
var testCluster *testcluster.Client
4340

4441
func init() {
4542
args = arg.Parse()
@@ -75,12 +72,12 @@ func main() {
7572
flynnrc = args.Flynnrc
7673
routerIP = args.RouterIP
7774
if flynnrc == "" {
75+
c := cluster.New(args.BootConfig, os.Stdout)
7876
var rootFS string
79-
testCluster = cluster.New(args.BootConfig, os.Stdout)
80-
rootFS, err = testCluster.BuildFlynn(args.RootFS, "origin/master", false, false)
77+
rootFS, err = c.BuildFlynn(args.RootFS, "origin/master", false, false)
8178
if err != nil {
8279
if args.Kill {
83-
testCluster.Shutdown()
80+
c.Shutdown()
8481
}
8582
log.Println("could not build flynn: ", err)
8683
if rootFS != "" {
@@ -89,40 +86,31 @@ func main() {
8986
return
9087
}
9188
if args.BuildRootFS {
92-
testCluster.Shutdown()
89+
c.Shutdown()
9390
fmt.Println("Built Flynn in rootfs:", rootFS)
9491
return
9592
} else {
9693
defer os.RemoveAll(rootFS)
9794
}
98-
if err = testCluster.Boot(rootFS, 3, nil, args.Kill); err != nil {
95+
if err = c.Boot(rootFS, 3, nil, args.Kill); err != nil {
9996
log.Println("could not boot cluster: ", err)
10097
return
10198
}
10299
if args.Kill {
103-
defer testCluster.Shutdown()
100+
defer c.Shutdown()
104101
}
105102

106-
if err = createFlynnrc(); err != nil {
103+
if err = createFlynnrc(c); err != nil {
107104
log.Println(err)
108105
return
109106
}
110107
defer os.RemoveAll(flynnrc)
111108

112-
routerIP = testCluster.RouterIP
109+
routerIP = c.RouterIP
113110
}
114111

115112
if args.ClusterAPI != "" {
116-
httpClient = &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{ServerName: "ci.flynn.io"}}}
117-
118-
res, err := httpClient.Get(args.ClusterAPI)
119-
if err != nil {
120-
log.Println(err)
121-
return
122-
}
123-
testCluster = &cluster.Cluster{}
124-
err = json.NewDecoder(res.Body).Decode(testCluster)
125-
res.Body.Close()
113+
testCluster, err = testcluster.NewClient(args.ClusterAPI)
126114
if err != nil {
127115
log.Println(err)
128116
return
@@ -221,14 +209,14 @@ func genSSHKey() (*sshData, error) {
221209
}, nil
222210
}
223211

224-
func createFlynnrc() error {
212+
func createFlynnrc(c *cluster.Cluster) error {
225213
tmpfile, err := ioutil.TempFile("", "flynnrc-")
226214
if err != nil {
227215
return err
228216
}
229217
path := tmpfile.Name()
230218

231-
config, err := testCluster.CLIConfig()
219+
config, err := c.CLIConfig()
232220
if err != nil {
233221
os.RemoveAll(path)
234222
return err

test/runner/assets/index.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ <h1>Flynn CI Builds</h1>
6060
</td>
6161
<td><span class="label <%= label_class %>"><%= state %></span></td>
6262
<td>
63-
<form action="/builds/" method="POST">
64-
<input type="hidden" name="id" value="<%= id %>" />
63+
<form action="/builds/<%= id %>/restart" method="POST">
6564
<button type="submit" class="btn btn-primary">
6665
<i class="fa fa-refresh"></i>
6766
</button>

0 commit comments

Comments
 (0)