Skip to content

Commit 854b357

Browse files
Molter73Stringy
andauthored
Capture logs for all containers used in tests (#1869)
Co-authored-by: Giles Hutton <ghutton@redhat.com>
1 parent d7ceed3 commit 854b357

File tree

11 files changed

+111
-58
lines changed

11 files changed

+111
-58
lines changed

.github/workflows/integration-tests-vm-type.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,5 +127,6 @@ jobs:
127127
with:
128128
name: ${{ inputs.vm_type }}-logs
129129
path: |
130-
${{ github.workspace }}/integration-tests/container-logs/**/*
131-
${{ github.workspace }}/integration-tests/performance-logs/**/*
130+
${{ github.workspace }}/integration-tests/container-logs/
131+
${{ github.workspace }}/integration-tests/performance-logs/
132+
if-no-files-found: ignore

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ teardown-builder:
102102
clean:
103103
rm -rf cmake-build*
104104
make -C collector clean
105-
make -C integration-tests docker-clean
105+
make -C integration-tests clean
106106

107107
.PHONY: shfmt-check
108108
shfmt-check:

ansible/roles/run-test-target/tasks/test-collection-method.yml

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#
99

1010
- set_fact:
11-
logs_root: "{{ collector_root }}/integration-tests/container-logs/{{ vm_config }}/{{ collection_method }}"
11+
logs_root: "{{ collector_root }}/integration-tests/container-logs"
1212

1313
- name: Cleanup old containers
1414
become: "{{ runtime_as_root }}"
@@ -122,19 +122,16 @@
122122
path: "{{ logs_root }}"
123123
delegate_to: localhost
124124

125-
- name: Get log files
126-
find:
127-
paths: "{{ remote_log_mount }}/{{ vm_config }}/{{ collection_method }}/"
128-
register: remote_log_files
125+
- name: Compress log files
126+
ansible.builtin.archive:
127+
path: "{{ remote_log_mount }}"
128+
dest: /tmp/{{ vm_config }}-{{ collection_method }}.tar.gz
129129

130130
- name: Fetch log files
131131
fetch:
132-
src: "{{ remote_log_path }}"
132+
src: /tmp/{{ vm_config }}-{{ collection_method }}.tar.gz
133133
dest: "{{ logs_root }}/"
134134
flat: true
135-
loop: "{{ remote_log_files.files | map(attribute='path') | list }}"
136-
loop_control:
137-
loop_var: remote_log_path
138135

139136
- name: Write integration test log
140137
copy:

integration-tests/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ ci-benchmarks: benchmark
9595
docker-clean:
9696
docker rm -fv container-stats benchmark collector grpc-server 2>/dev/null || true
9797

98+
.PHONY: clean
99+
clean: docker-clean
100+
rm -rf container-logs/
101+
98102
.PHONY:
99103
benchmark: TestBenchmarkCollector
100104

integration-tests/pkg/collector/collector_docker.go

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/stackrox/collector/integration-tests/pkg/common"
1212
"github.com/stackrox/collector/integration-tests/pkg/config"
1313
"github.com/stackrox/collector/integration-tests/pkg/executor"
14-
"github.com/stackrox/collector/integration-tests/pkg/log"
1514
)
1615

1716
type DockerCollectorManager struct {
@@ -156,19 +155,7 @@ func (c *DockerCollectorManager) launchCollector() error {
156155
}
157156

158157
func (c *DockerCollectorManager) captureLogs(containerName string) (string, error) {
159-
logs, err := c.executor.GetContainerLogs(containerName)
160-
if err != nil {
161-
return "", log.Error("logs error (%v) for container %s\n", err, containerName)
162-
}
163-
164-
logFile, err := common.PrepareLog(c.testName, "collector.log")
165-
if err != nil {
166-
return "", err
167-
}
168-
defer logFile.Close()
169-
170-
_, err = logFile.WriteString(logs)
171-
return logs, nil
158+
return c.executor.CaptureLogs(c.testName, containerName)
172159
}
173160

174161
func (c *DockerCollectorManager) killContainer(name string) error {

integration-tests/pkg/common/utils.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,20 @@ func ArchSupported(supported ...string) (bool, string) {
5858

5959
// Creates a new file to dump logs into
6060
func PrepareLog(testName string, logName string) (*os.File, error) {
61-
logDirectory := filepath.Join(".", "container-logs", config.VMInfo().Config, config.CollectionMethod())
61+
pathSections := []string{
62+
".", "container-logs",
63+
config.VMInfo().Config,
64+
config.CollectionMethod(),
65+
testName,
66+
}
67+
68+
logDirectory := filepath.Join(pathSections...)
6269
err := os.MkdirAll(logDirectory, os.ModePerm)
6370
if err != nil {
6471
return nil, err
6572
}
6673

67-
logPath := filepath.Join(logDirectory, strings.ReplaceAll(testName, "/", "_")+"-"+logName)
74+
logPath := filepath.Join(logDirectory, logName)
6875
return os.Create(logPath)
6976
}
7077

integration-tests/pkg/executor/executor.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,25 @@ type ContainerFilter struct {
1111
Namespace string
1212
}
1313

14+
type ContainerLogs struct {
15+
Stdout string
16+
Stderr string
17+
}
18+
19+
func (c *ContainerLogs) Empty() bool {
20+
return *c == (ContainerLogs{})
21+
}
22+
23+
// Will return Stderr if it is not empty, otherwise it returns Stdout.
24+
// Useful for accessing the logs for collector and container-stats
25+
// that use a single stream.
26+
func (l *ContainerLogs) GetSingleLog() string {
27+
if l.Stderr != "" {
28+
return l.Stderr
29+
}
30+
return l.Stdout
31+
}
32+
1433
type Executor interface {
1534
PullImage(image string) error
1635
IsContainerRunning(container string) (bool, error)
@@ -23,7 +42,8 @@ type Executor interface {
2342
StartContainer(config config.ContainerStartConfig) (string, error)
2443
GetContainerHealthCheck(containerID string) (string, error)
2544
GetContainerIP(containerID string) (string, error)
26-
GetContainerLogs(containerID string) (string, error)
45+
GetContainerLogs(containerID string) (ContainerLogs, error)
46+
CaptureLogs(testName, containerName string) (string, error)
2747
GetContainerPort(containerID string) (string, error)
2848
IsContainerFoundFiltered(containerID, filter string) (bool, error)
2949
}

integration-tests/pkg/executor/executor_docker_api.go

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -170,30 +170,74 @@ func (d *dockerAPIExecutor) ExecContainer(containerName string, command []string
170170
return stdoutBuf.String(), nil
171171
}
172172

173-
func (d *dockerAPIExecutor) GetContainerLogs(containerID string) (string, error) {
173+
func (d *dockerAPIExecutor) GetContainerLogs(containerID string) (ContainerLogs, error) {
174174
timeoutContext, cancel := context.WithTimeout(context.Background(), 30*time.Second)
175175
defer cancel()
176176
logsReader, err := d.client.ContainerLogs(timeoutContext, containerID,
177177
container.LogsOptions{ShowStdout: true, ShowStderr: true})
178178
if err != nil {
179-
return "", fmt.Errorf("error getting container logs: %w", err)
179+
return ContainerLogs{}, fmt.Errorf("error getting container logs: %w", err)
180180
}
181181
defer logsReader.Close()
182182

183183
var sbStdOut, sbStdErr strings.Builder
184184
// if container doesn't have TTY (c.Config.Tty), output is multiplexed
185185
if _, err := stdcopy.StdCopy(&sbStdOut, &sbStdErr, logsReader); err != nil {
186-
return "", fmt.Errorf("error copying logs: %w", err)
186+
return ContainerLogs{}, fmt.Errorf("error copying logs: %w", err)
187187
}
188188
log.Info("logs %s (%d bytes stdout, %d bytes stderr)", containerID, sbStdOut.Len(), sbStdErr.Len())
189-
if sbStdErr.Len() > 0 {
190-
if sbStdOut.Len() > 0 {
191-
// not implemented
192-
return "", errors.New("unhandled container output to stdout and stderr")
189+
return ContainerLogs{
190+
Stdout: sbStdOut.String(),
191+
Stderr: sbStdErr.String(),
192+
}, nil
193+
}
194+
195+
func (c *dockerAPIExecutor) CaptureLogs(testName, containerName string) (string, error) {
196+
log.Info("%s: Gathering logs for %q", testName, containerName)
197+
logs, err := c.GetContainerLogs(containerName)
198+
if err != nil {
199+
return "", log.Error("logs error (%v) for container %s\n", err, containerName)
200+
}
201+
202+
type logFile = struct {
203+
name string
204+
content string
205+
}
206+
207+
var logFiles []logFile
208+
if logs.Empty() {
209+
// Nothing to log, still we create an empty file for awareness
210+
logFiles = []logFile{{
211+
name: fmt.Sprintf("%s.log", containerName),
212+
}}
213+
} else if logs.Stderr == "" || logs.Stdout == "" {
214+
// We only have stdout OR stderr to log
215+
logFiles = []logFile{{
216+
name: fmt.Sprintf("%s.log", containerName),
217+
content: logs.GetSingleLog(),
218+
}}
219+
} else {
220+
// We need to log both stdout and stderr, do so on separate files
221+
logFiles = []logFile{{
222+
name: fmt.Sprintf("%s-stderr.log", containerName),
223+
content: logs.Stderr,
224+
}, {
225+
name: fmt.Sprintf("%s-stdout.log", containerName),
226+
content: logs.Stdout,
227+
}}
228+
}
229+
230+
for _, lf := range logFiles {
231+
file, err := common.PrepareLog(testName, lf.name)
232+
if err != nil {
233+
return "", err
193234
}
194-
return sbStdErr.String(), nil
235+
defer file.Close()
236+
237+
file.WriteString(lf.content)
195238
}
196-
return sbStdOut.String(), nil
239+
240+
return logFiles[0].content, nil
197241
}
198242

199243
func (d *dockerAPIExecutor) KillContainer(containerID string) (string, error) {

integration-tests/pkg/mock_sensor/server.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"log"
66
"net"
77
"os"
8-
"path/filepath"
98
"strings"
109
"sync"
1110
"time"
@@ -17,7 +16,7 @@ import (
1716
"google.golang.org/grpc"
1817
"google.golang.org/grpc/keepalive"
1918

20-
"github.com/stackrox/collector/integration-tests/pkg/config"
19+
"github.com/stackrox/collector/integration-tests/pkg/common"
2120
"github.com/stackrox/collector/integration-tests/pkg/types"
2221
)
2322

@@ -223,10 +222,7 @@ func (m *MockSensor) HasEndpoint(containerID string, endpoint types.EndpointInfo
223222
func (m *MockSensor) Start() {
224223
var err error
225224

226-
m.logFile, err = os.OpenFile(
227-
filepath.Join(config.LogPath(), strings.ReplaceAll(m.testName, "/", "_")+"-events.log"),
228-
os.O_CREATE|os.O_WRONLY, 0644,
229-
)
225+
m.logFile, err = common.PrepareLog(m.testName, "events.log")
230226

231227
if err != nil {
232228
log.Fatalf("failed to open log file: %v", err)

integration-tests/suites/base.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ func (s *IntegrationTestSuiteBase) GetContainerStats() []ContainerStat {
192192
return nil
193193
}
194194

195-
logLines := strings.Split(logs, "\n")
195+
logLines := strings.Split(logs.Stderr, "\n")
196196
for i, line := range logLines {
197197
var stat ContainerStat
198198

@@ -304,13 +304,6 @@ func (s *IntegrationTestSuiteBase) AssertProcessInfoEqual(expected, actual types
304304
assert.Equal(expected.Args, actual.Args)
305305
}
306306

307-
func (s *IntegrationTestSuiteBase) GetLogLines(containerName string) []string {
308-
logs, err := s.containerLogs(containerName)
309-
s.Require().NoError(err, containerName+" failure")
310-
logLines := strings.Split(logs, "\n")
311-
return logLines
312-
}
313-
314307
// Wait for a container to become a certain status.
315308
// - tickSeconds -- how often to check for the status
316309
// - timeoutThreshold -- the overall time limit for waiting,
@@ -410,8 +403,12 @@ func (s *IntegrationTestSuiteBase) execContainerShellScript(containerName string
410403

411404
func (s *IntegrationTestSuiteBase) cleanupContainers(containers ...string) {
412405
for _, container := range containers {
413-
s.Executor().KillContainer(container)
414-
s.Executor().RemoveContainer(executor.ContainerFilter{Name: container})
406+
exists, _ := s.Executor().ContainerExists(executor.ContainerFilter{Name: container})
407+
if exists {
408+
s.Executor().KillContainer(container)
409+
s.Executor().CaptureLogs(s.T().Name(), container)
410+
s.Executor().RemoveContainer(executor.ContainerFilter{Name: container})
411+
}
415412
}
416413
}
417414

@@ -427,7 +424,7 @@ func (s *IntegrationTestSuiteBase) removeContainers(containers ...string) {
427424
}
428425
}
429426

430-
func (s *IntegrationTestSuiteBase) containerLogs(containerName string) (string, error) {
427+
func (s *IntegrationTestSuiteBase) containerLogs(containerName string) (executor.ContainerLogs, error) {
431428
return s.Executor().GetContainerLogs(containerName)
432429
}
433430

0 commit comments

Comments
 (0)