1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187// Copyright 2016 Péter Szilágyi. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package deepstream
import (
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"testing"
"time"
docker "github.com/fsouza/go-dockerclient"
)
// server is a deepstream test server backed by a docker container.
type server struct {
url string
daemon *docker.Client
container *docker.Container
waiter docker.CloseWaiter
workdir string
}
// newTestServer creates a new deepstream test server backed by a docker container.
func newTestServer(t *testing.T, configs map[string]string) *server {
server, err := newServer(configs, false)
if err != nil {
t.Fatalf("failed to create test server: %v", err)
}
return server
}
// newBenchServer creates a new deepstream benchmark server backed by a docker container.
func newBenchServer(b *testing.B, configs map[string]string) *server {
server, err := newServer(configs, false)
if err != nil {
b.Fatalf("failed to create benchmark server: %v", err)
}
return server
}
// newServer creates a new deepstream server backed by a docker container.
func newServer(configs map[string]string, attach bool) (*server, error) {
var (
server = &server{
url: "ws://localhost:6020/deepstream",
}
err error
)
// If we're using a live deepstream server, don't bother with docker
if url := os.Getenv("DEEPSTREAM_SERVER_URL"); url != "" {
server.url = url
return server, nil
}
// Connect to docker and ensure the deepstream image is downloaded
if server.daemon, err = docker.NewClient("unix:///var/run/docker.sock"); err != nil {
return nil, fmt.Errorf("failed to connect to docker daemon: %v", err)
}
// Create the deepstream config folder and contents
if server.workdir, err = ioutil.TempDir("", ""); err != nil {
return nil, fmt.Errorf("failed to create temporary workspace: %v", err)
}
for name, content := range configs {
if err = ioutil.WriteFile(filepath.Join(server.workdir, name), []byte(content), os.ModePerm); err != nil {
return nil, fmt.Errorf("failed to create config file: %v", err)
}
}
// Create the deepstream container, forward the ports and clean up afterwards
if server.container, err = server.daemon.CreateContainer(docker.CreateContainerOptions{
Config: &docker.Config{
Image: "deepstreamio/deepstream.io",
},
HostConfig: &docker.HostConfig{
PortBindings: map[docker.Port][]docker.PortBinding{
"6020/tcp": {docker.PortBinding{HostIP: "127.0.0.1", HostPort: "6020"}},
"6021/tcp": {docker.PortBinding{HostIP: "127.0.0.1", HostPort: "6021"}},
},
AutoRemove: true,
Binds: []string{
fmt.Sprintf("%s:/etc/deepstream:ro", server.workdir),
},
},
}); err != nil {
return nil, fmt.Errorf("failed to create deepstream container: %v", err)
}
// Attach to the container and start it up
if server.waiter, err = server.daemon.AttachToContainerNonBlocking(docker.AttachToContainerOptions{
Container: server.container.ID,
OutputStream: os.Stdout,
ErrorStream: os.Stderr,
Stream: attach,
Stdout: attach,
Stderr: attach,
}); err != nil {
return nil, fmt.Errorf("failed to attach to deepstream container: %v", err)
}
if err := server.daemon.StartContainer(server.container.ID, nil); err != nil {
return nil, fmt.Errorf("failed to start deepstream container: %v", err)
}
// Wait for the listener port to open and return
for {
// If the container died, bail out
container, err := server.daemon.InspectContainer(server.container.ID)
if err != nil {
return nil, fmt.Errorf("failed to inspect deepstream container: %v", err)
}
if !container.State.Running {
return nil, fmt.Errorf("unexpectedly terminated deepstream container")
}
// Container seems to be alive, check whether the listener socket is accepting connections
if conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", container.NetworkSettings.IPAddress, 6020)); err == nil {
conn.Close()
break
}
time.Sleep(100 * time.Millisecond)
}
return server, nil
}
// close terminates the deepstream container.
func (s *server) close() {
// Skip closing if there's no backing docker container
if s.waiter == nil {
return
}
// Otherwise detach console, stop container and remove working directory
s.waiter.Close()
s.daemon.StopContainer(s.container.ID, 0)
os.RemoveAll(s.workdir)
}
// Tests that logins work for various configuration and credential combinations.
func TestAuths(t *testing.T) {
tests := []struct {
configs map[string]string
auth Authenticator
fail bool
}{
// Default or disabled auth allows anyone to login, missing auth token result in an empty user/pass combo
{configs: map[string]string{
"config.yml": "",
}, auth: nil, fail: false},
{configs: map[string]string{
"config.yml": "auth:\n type: none\n",
}, auth: nil, fail: false},
// Default allows anyone to login, but a malformed request should still fail
// {configs: nil, auth: &JSONAuth{Credentials: nil}, fail: false}, // Crashes deepstream
// Default or disabled auth allows anyone to login, so an empty user/pass should succeed
{configs: map[string]string{
"config.yml": "",
}, auth: &PasswordAuth{}, fail: false},
{configs: map[string]string{
"config.yml": "auth:\n type: none\n",
}, auth: &PasswordAuth{}, fail: false},
// File based authentication requires valid password
{configs: map[string]string{
"config.yml": "auth:\n type: file\n options:\n path: /etc/deepstream/users.yml\n",
"users.yml": "johndoe:\n password: uY2zMQZXcFuWKeX/6eY43w==9wSp046KHAQfbvKcKgvwNA==\n",
}, auth: &PasswordAuth{Username: "johndoe", Password: ""}, fail: true},
{configs: map[string]string{
"config.yml": "auth:\n type: file\n options:\n path: /etc/deepstream/users.yml\n",
"users.yml": "johndoe:\n password: uY2zMQZXcFuWKeX/6eY43w==9wSp046KHAQfbvKcKgvwNA==\n",
}, auth: &PasswordAuth{Username: "johndoe", Password: "uY2zMQZXcFuWKeX/6eY43w==9wSp046KHAQfbvKcKgvwNA=="}, fail: false},
}
for i, tt := range tests {
server := newTestServer(t, tt.configs)
client, err := Dial(server.url)
if err != nil {
server.close()
t.Fatalf("test %d: failed to connect: %v", i, err)
}
if err := client.Login(tt.auth); (err != nil) != tt.fail {
t.Errorf("test %d: login failure mismatch: have %v, want %v", i, err, tt.fail)
}
client.Close()
server.close()
}
}