Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/cmd/grafana-agent-service/service_test.go
4093 views
1
package main
2
3
import (
4
"bytes"
5
"context"
6
"fmt"
7
"io"
8
"net/http"
9
"os/exec"
10
"path/filepath"
11
"runtime"
12
"sync"
13
"testing"
14
15
"github.com/go-kit/log"
16
"github.com/grafana/agent/pkg/flow/componenttest"
17
"github.com/grafana/agent/pkg/util"
18
"github.com/phayes/freeport"
19
"github.com/stretchr/testify/require"
20
)
21
22
const goosWindows = "windows"
23
24
func Test_serviceManager(t *testing.T) {
25
l := util.TestLogger(t)
26
27
serviceBinary := buildExampleService(t, l)
28
29
t.Run("can run service binary", func(t *testing.T) {
30
listenHost := getListenHost(t)
31
32
mgr := newServiceManager(l, serviceManagerConfig{
33
Path: serviceBinary,
34
Args: []string{"-listen-addr", listenHost},
35
})
36
go mgr.Run(componenttest.TestContext(t))
37
38
util.Eventually(t, func(t require.TestingT) {
39
resp, err := makeServiceRequest(listenHost, "/echo/response", []byte("Hello, world!"))
40
require.NoError(t, err)
41
require.Equal(t, []byte("Hello, world!"), resp)
42
})
43
})
44
45
t.Run("terminates service binary", func(t *testing.T) {
46
listenHost := getListenHost(t)
47
48
mgr := newServiceManager(l, serviceManagerConfig{
49
Path: serviceBinary,
50
Args: []string{"-listen-addr", listenHost},
51
})
52
53
ctx, cancel := context.WithCancel(componenttest.TestContext(t))
54
defer cancel()
55
go mgr.Run(ctx)
56
57
util.Eventually(t, func(t require.TestingT) {
58
resp, err := makeServiceRequest(listenHost, "/echo/response", []byte("Hello, world!"))
59
require.NoError(t, err)
60
require.Equal(t, []byte("Hello, world!"), resp)
61
})
62
63
// Cancel the context, which should stop the manager.
64
cancel()
65
66
util.Eventually(t, func(t require.TestingT) {
67
_, err := makeServiceRequest(listenHost, "/echo/response", []byte("Hello, world!"))
68
69
if runtime.GOOS == goosWindows {
70
require.ErrorContains(t, err, "No connection could be made")
71
} else {
72
require.ErrorContains(t, err, "connection refused")
73
}
74
})
75
})
76
77
t.Run("can forward to stdout", func(t *testing.T) {
78
listenHost := getListenHost(t)
79
80
var buf syncBuffer
81
82
mgr := newServiceManager(l, serviceManagerConfig{
83
Path: serviceBinary,
84
Args: []string{"-listen-addr", listenHost},
85
Stdout: &buf,
86
})
87
88
ctx, cancel := context.WithCancel(componenttest.TestContext(t))
89
defer cancel()
90
go mgr.Run(ctx)
91
92
// Test making the request and testing the buffer contents separately,
93
// otherwise we may log to stdout more than we intend to.
94
95
util.Eventually(t, func(t require.TestingT) {
96
_, err := makeServiceRequest(listenHost, "/echo/stdout", []byte("Hello, world!"))
97
require.NoError(t, err)
98
})
99
100
util.Eventually(t, func(t require.TestingT) {
101
require.Equal(t, []byte("Hello, world!"), buf.Bytes())
102
})
103
})
104
105
t.Run("can forward to stderr", func(t *testing.T) {
106
listenHost := getListenHost(t)
107
108
var buf syncBuffer
109
110
mgr := newServiceManager(l, serviceManagerConfig{
111
Path: serviceBinary,
112
Args: []string{"-listen-addr", listenHost},
113
Stderr: &buf,
114
})
115
116
ctx, cancel := context.WithCancel(componenttest.TestContext(t))
117
defer cancel()
118
go mgr.Run(ctx)
119
120
// Test making the request and testing the buffer contents separately,
121
// otherwise we may log to stderr more than we intend to.
122
123
util.Eventually(t, func(t require.TestingT) {
124
_, err := makeServiceRequest(listenHost, "/echo/stderr", []byte("Hello, world!"))
125
require.NoError(t, err)
126
})
127
128
util.Eventually(t, func(t require.TestingT) {
129
require.Equal(t, []byte("Hello, world!"), buf.Bytes())
130
})
131
})
132
}
133
134
func buildExampleService(t *testing.T, l log.Logger) string {
135
t.Helper()
136
137
writer := log.NewStdlibAdapter(l)
138
139
servicePath := filepath.Join(t.TempDir(), "example-service")
140
if runtime.GOOS == goosWindows {
141
servicePath = servicePath + ".exe"
142
}
143
144
cmd := exec.Command(
145
"go", "build",
146
"-o", servicePath,
147
"testdata/example_service.go",
148
)
149
cmd.Stdout = writer
150
cmd.Stderr = writer
151
152
require.NoError(t, cmd.Run())
153
154
return servicePath
155
}
156
157
func getListenHost(t *testing.T) string {
158
t.Helper()
159
160
port, err := freeport.GetFreePort()
161
require.NoError(t, err)
162
163
return fmt.Sprintf("127.0.0.1:%d", port)
164
}
165
166
func makeServiceRequest(host string, path string, body []byte) ([]byte, error) {
167
resp, err := http.Post(
168
fmt.Sprintf("http://%s%s", host, path),
169
"text/plain",
170
bytes.NewReader(body),
171
)
172
if err != nil {
173
return nil, err
174
}
175
defer resp.Body.Close()
176
177
if resp.StatusCode != http.StatusOK {
178
return nil, fmt.Errorf("unexpected status code %s", resp.Status)
179
}
180
return io.ReadAll(resp.Body)
181
}
182
183
// syncBuffer wraps around a bytes.Buffer and makes it safe to use from
184
// multiple goroutines.
185
type syncBuffer struct {
186
mut sync.RWMutex
187
buf bytes.Buffer
188
}
189
190
func (sb *syncBuffer) Bytes() []byte {
191
sb.mut.RLock()
192
defer sb.mut.RUnlock()
193
194
return sb.buf.Bytes()
195
}
196
197
func (sb *syncBuffer) Write(p []byte) (n int, err error) {
198
sb.mut.Lock()
199
defer sb.mut.Unlock()
200
201
return sb.buf.Write(p)
202
}
203
204