Skip to content

Commit 1f977e2

Browse files
authored
Allow packages without service deployer to have system tests (#1768)
This will allow to leverage the new custom definitions added in #1765, without forcing the packages to define a service deployer. This is useful when the only requirement to test the package is a running agent, for example when collecting information from the system where it is running.
1 parent 687a17b commit 1f977e2

File tree

18 files changed

+513
-91
lines changed

18 files changed

+513
-91
lines changed

internal/agentdeployer/factory.go

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,11 @@ type FactoryOptions struct {
4141
// Factory chooses the appropriate service runner for the given data stream, depending
4242
// on service configuration files defined in the package or data stream.
4343
func Factory(options FactoryOptions) (AgentDeployer, error) {
44-
devDeployPath, err := FindDevDeployPath(options)
45-
if err != nil {
46-
return nil, fmt.Errorf("can't find \"%s\" directory: %w", options.DevDeployDir, err)
47-
}
48-
49-
agentDeployerName, err := findAgentDeployer(devDeployPath)
44+
agentDeployerName, agentDeployerPath, err := selectAgentDeployerType(options)
5045
if err != nil {
51-
logger.Debugf("Not found any agent deployer, using default one")
52-
agentDeployerName = "default"
53-
}
54-
// if package defines `_dev/deploy/docker` folder to start their services, it should be
55-
// using the default agent deployer`
56-
if agentDeployerName == "docker" || agentDeployerName == "tf" {
57-
agentDeployerName = "default"
46+
return nil, fmt.Errorf("failed to select agent deployer type: %w", err)
5847
}
5948

60-
agentDeployerPath := filepath.Join(devDeployPath, agentDeployerName)
61-
6249
switch agentDeployerName {
6350
case "default":
6451
if options.Type != TypeTest {
@@ -80,46 +67,79 @@ func Factory(options FactoryOptions) (AgentDeployer, error) {
8067
// FIXME: this docker-compose scenario contains both agent and service
8168
return nil, nil
8269
case "k8s":
83-
if _, err := os.Stat(agentDeployerPath); err == nil {
84-
opts := KubernetesAgentDeployerOptions{
85-
Profile: options.Profile,
86-
DefinitionsDir: agentDeployerPath,
87-
StackVersion: options.StackVersion,
88-
PolicyName: options.PolicyName,
89-
DataStream: options.DataStream,
90-
RunSetup: options.RunSetup,
91-
RunTestsOnly: options.RunTestsOnly,
92-
RunTearDown: options.RunTearDown,
93-
}
94-
return NewKubernetesAgentDeployer(opts)
70+
opts := KubernetesAgentDeployerOptions{
71+
Profile: options.Profile,
72+
DefinitionsDir: agentDeployerPath,
73+
StackVersion: options.StackVersion,
74+
PolicyName: options.PolicyName,
75+
DataStream: options.DataStream,
76+
RunSetup: options.RunSetup,
77+
RunTestsOnly: options.RunTestsOnly,
78+
RunTearDown: options.RunTearDown,
9579
}
80+
return NewKubernetesAgentDeployer(opts)
9681
}
9782
return nil, fmt.Errorf("unsupported agent deployer (name: %s)", agentDeployerName)
9883
}
9984

85+
func selectAgentDeployerType(options FactoryOptions) (string, string, error) {
86+
devDeployPath, err := FindDevDeployPath(options)
87+
if errors.Is(err, os.ErrNotExist) {
88+
return "default", "", nil
89+
}
90+
if err != nil {
91+
return "", "", fmt.Errorf("can't find \"%s\" directory: %w", options.DevDeployDir, err)
92+
}
93+
94+
agentDeployerNames, err := findAgentDeployers(devDeployPath)
95+
if errors.Is(err, os.ErrNotExist) || len(agentDeployerNames) == 0 {
96+
logger.Debugf("Not agent deployer found, using default one")
97+
return "default", "", nil
98+
}
99+
if err != nil {
100+
return "", "", fmt.Errorf("failed to find agent deployer: %w", err)
101+
}
102+
if len(agentDeployerNames) != 1 {
103+
return "", "", fmt.Errorf("expected to find only one agent deployer in \"%s\"", devDeployPath)
104+
}
105+
agentDeployerName := agentDeployerNames[0]
106+
107+
// if package defines `_dev/deploy/docker` folder to start their services, it should be
108+
// using the default agent deployer`
109+
if agentDeployerName == "docker" || agentDeployerName == "tf" {
110+
return "default", "", nil
111+
}
112+
113+
// No need to check if this path exists because it comes from a directory list.
114+
agentDeployerPath := filepath.Join(devDeployPath, agentDeployerName)
115+
return agentDeployerName, agentDeployerPath, nil
116+
}
117+
100118
// FindDevDeployPath function returns a path reference to the "_dev/deploy" directory.
101119
func FindDevDeployPath(options FactoryOptions) (string, error) {
102120
dataStreamDevDeployPath := filepath.Join(options.DataStreamRootPath, options.DevDeployDir)
103-
if _, err := os.Stat(dataStreamDevDeployPath); err == nil {
121+
info, err := os.Stat(dataStreamDevDeployPath)
122+
if err == nil && info.IsDir() {
104123
return dataStreamDevDeployPath, nil
105-
} else if !errors.Is(err, os.ErrNotExist) {
124+
} else if err != nil && !errors.Is(err, os.ErrNotExist) {
106125
return "", fmt.Errorf("stat failed for data stream (path: %s): %w", dataStreamDevDeployPath, err)
107126
}
108127

109128
packageDevDeployPath := filepath.Join(options.PackageRootPath, options.DevDeployDir)
110-
if _, err := os.Stat(packageDevDeployPath); err == nil {
129+
info, err = os.Stat(packageDevDeployPath)
130+
if err == nil && info.IsDir() {
111131
return packageDevDeployPath, nil
112-
} else if !errors.Is(err, os.ErrNotExist) {
132+
} else if err != nil && !errors.Is(err, os.ErrNotExist) {
113133
return "", fmt.Errorf("stat failed for package (path: %s): %w", packageDevDeployPath, err)
114134
}
115135

116-
return "", fmt.Errorf("\"%s\" directory doesn't exist", options.DevDeployDir)
136+
return "", fmt.Errorf("\"%s\" %w", options.DevDeployDir, os.ErrNotExist)
117137
}
118138

119-
func findAgentDeployer(devDeployPath string) (string, error) {
139+
func findAgentDeployers(devDeployPath string) ([]string, error) {
120140
fis, err := os.ReadDir(devDeployPath)
121141
if err != nil {
122-
return "", fmt.Errorf("can't read directory (path: %s): %w", devDeployPath, err)
142+
return nil, fmt.Errorf("can't read directory (path: %s): %w", devDeployPath, err)
123143
}
124144

125145
var folders []os.DirEntry
@@ -129,8 +149,9 @@ func findAgentDeployer(devDeployPath string) (string, error) {
129149
}
130150
}
131151

132-
if len(folders) != 1 {
133-
return "", fmt.Errorf("expected to find only one agent deployer in \"%s\"", devDeployPath)
152+
var names []string
153+
for _, folder := range folders {
154+
names = append(names, folder.Name())
134155
}
135-
return folders[0].Name(), nil
156+
return names, nil
136157
}

internal/benchrunner/runners/system/runner.go

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -231,44 +231,13 @@ func (r *runner) setUp(ctx context.Context) error {
231231
func (r *runner) run(ctx context.Context) (report reporters.Reportable, err error) {
232232
var service servicedeployer.DeployedService
233233
if r.scenario.Corpora.InputService != nil {
234-
stackVersion, err := r.options.KibanaClient.Version()
235-
if err != nil {
236-
return nil, fmt.Errorf("cannot request Kibana version: %w", err)
237-
}
238-
239-
// Setup service.
240-
logger.Debug("setting up service...")
241-
devDeployDir := filepath.Clean(filepath.Join(r.options.BenchPath, "deploy"))
242-
opts := servicedeployer.FactoryOptions{
243-
PackageRootPath: r.options.PackageRootPath,
244-
DevDeployDir: devDeployDir,
245-
Variant: r.options.Variant,
246-
Profile: r.options.Profile,
247-
Type: servicedeployer.TypeBench,
248-
StackVersion: stackVersion.Version(),
249-
DeployIndependentAgent: false,
250-
}
251-
serviceDeployer, err := servicedeployer.Factory(opts)
252-
253-
if err != nil {
254-
return nil, fmt.Errorf("could not create service runner: %w", err)
255-
}
256-
257-
r.svcInfo.Name = r.scenario.Corpora.InputService.Name
258-
service, err = serviceDeployer.SetUp(ctx, r.svcInfo)
259-
if err != nil {
260-
return nil, fmt.Errorf("could not setup service: %w", err)
261-
}
262-
263-
r.svcInfo = service.Info()
264-
r.shutdownServiceHandler = func(ctx context.Context) error {
265-
logger.Debug("tearing down service...")
266-
if err := service.TearDown(ctx); err != nil {
267-
return fmt.Errorf("error tearing down service: %w", err)
268-
}
269-
270-
return nil
234+
s, err := r.setupService(ctx)
235+
if errors.Is(err, os.ErrNotExist) {
236+
logger.Debugf("No service deployer defined for this benchmark")
237+
} else if err != nil {
238+
return nil, err
271239
}
240+
service = s
272241
}
273242

274243
r.startMetricsColletion(ctx)
@@ -288,7 +257,7 @@ func (r *runner) run(ctx context.Context) (report reporters.Reportable, err erro
288257
}
289258

290259
// Signal to the service that the agent is ready (policy is assigned).
291-
if r.scenario.Corpora.InputService != nil && r.scenario.Corpora.InputService.Signal != "" {
260+
if service != nil && r.scenario.Corpora.InputService != nil && r.scenario.Corpora.InputService.Signal != "" {
292261
if err = service.Signal(ctx, r.scenario.Corpora.InputService.Signal); err != nil {
293262
return nil, fmt.Errorf("failed to notify benchmark service: %w", err)
294263
}
@@ -314,6 +283,48 @@ func (r *runner) run(ctx context.Context) (report reporters.Reportable, err erro
314283
return createReport(r.options.BenchName, r.corporaFile, r.scenario, msum)
315284
}
316285

286+
func (r *runner) setupService(ctx context.Context) (servicedeployer.DeployedService, error) {
287+
stackVersion, err := r.options.KibanaClient.Version()
288+
if err != nil {
289+
return nil, fmt.Errorf("cannot request Kibana version: %w", err)
290+
}
291+
292+
// Setup service.
293+
logger.Debug("Setting up service...")
294+
devDeployDir := filepath.Clean(filepath.Join(r.options.BenchPath, "deploy"))
295+
opts := servicedeployer.FactoryOptions{
296+
PackageRootPath: r.options.PackageRootPath,
297+
DevDeployDir: devDeployDir,
298+
Variant: r.options.Variant,
299+
Profile: r.options.Profile,
300+
Type: servicedeployer.TypeBench,
301+
StackVersion: stackVersion.Version(),
302+
DeployIndependentAgent: false,
303+
}
304+
serviceDeployer, err := servicedeployer.Factory(opts)
305+
if err != nil {
306+
return nil, fmt.Errorf("could not create service runner: %w", err)
307+
}
308+
309+
r.svcInfo.Name = r.scenario.Corpora.InputService.Name
310+
service, err := serviceDeployer.SetUp(ctx, r.svcInfo)
311+
if err != nil {
312+
return nil, fmt.Errorf("could not setup service: %w", err)
313+
}
314+
315+
r.svcInfo = service.Info()
316+
r.shutdownServiceHandler = func(ctx context.Context) error {
317+
logger.Debug("tearing down service...")
318+
if err := service.TearDown(ctx); err != nil {
319+
return fmt.Errorf("error tearing down service: %w", err)
320+
}
321+
322+
return nil
323+
}
324+
325+
return service, nil
326+
}
327+
317328
func (r *runner) startMetricsColletion(ctx context.Context) {
318329
// TODO collect agent hosts metrics using system integration
319330
r.mcollector = newCollector(

internal/service/boot.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package service
66

77
import (
88
"context"
9+
"errors"
910
"fmt"
1011
"os"
1112
"os/signal"
@@ -44,6 +45,10 @@ func BootUp(ctx context.Context, options Options) error {
4445
StackVersion: options.StackVersion,
4546
DeployIndependentAgent: false,
4647
})
48+
if errors.Is(err, os.ErrNotExist) {
49+
fmt.Println("No service defined.")
50+
return nil
51+
}
4752
if err != nil {
4853
return fmt.Errorf("can't create the service deployer instance: %w", err)
4954
}
@@ -67,6 +72,7 @@ func BootUp(ctx context.Context, options Options) error {
6772
fmt.Println("Service is up, please use ctrl+c to take it down")
6873
ch := make(chan os.Signal, 1)
6974
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
75+
defer signal.Stop(ch)
7076
<-ch
7177

7278
// Tear down the service

internal/servicedeployer/factory.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func FindDevDeployPath(options FactoryOptions) (string, error) {
138138
return "", fmt.Errorf("stat failed for package (path: %s): %w", packageDevDeployPath, err)
139139
}
140140

141-
return "", fmt.Errorf("\"%s\" directory doesn't exist", options.DevDeployDir)
141+
return "", fmt.Errorf("\"%s\" %w", options.DevDeployDir, os.ErrNotExist)
142142
}
143143

144144
func findServiceDeployer(devDeployPath string) (string, error) {

internal/testrunner/runners/system/runner.go

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,20 @@ func (r *runner) initRun() error {
464464
DataStreamRootPath: r.dataStreamPath,
465465
DevDeployDir: DevDeployDir,
466466
})
467-
if err != nil {
468-
return fmt.Errorf("_dev/deploy directory not found: %w", err)
467+
switch {
468+
case errors.Is(err, os.ErrNotExist):
469+
r.variants = r.selectVariants(nil)
470+
case err != nil:
471+
return fmt.Errorf("failed fo find service deploy path: %w", err)
472+
default:
473+
variantsFile, err := servicedeployer.ReadVariantsFile(devDeployPath)
474+
if err != nil && !errors.Is(err, os.ErrNotExist) {
475+
return fmt.Errorf("can't read service variant: %w", err)
476+
}
477+
r.variants = r.selectVariants(variantsFile)
478+
}
479+
if r.options.ServiceVariant != "" && len(r.variants) == 0 {
480+
return fmt.Errorf("not found variant definition %q", r.options.ServiceVariant)
469481
}
470482

471483
if r.options.ConfigFilePath != "" {
@@ -486,16 +498,6 @@ func (r *runner) initRun() error {
486498
}
487499
}
488500

489-
variantsFile, err := servicedeployer.ReadVariantsFile(devDeployPath)
490-
if err != nil && !errors.Is(err, os.ErrNotExist) {
491-
return fmt.Errorf("can't read service variant: %w", err)
492-
}
493-
494-
r.variants = r.selectVariants(variantsFile)
495-
if r.options.ServiceVariant != "" && len(r.variants) == 0 {
496-
return fmt.Errorf("not found variant definition %q", r.options.ServiceVariant)
497-
}
498-
499501
return nil
500502
}
501503

@@ -804,7 +806,9 @@ func (r *runner) prepareScenario(ctx context.Context, config *testConfig, svcInf
804806
scenario.agent = agentDeployed
805807

806808
service, svcInfo, err := r.setupService(ctx, config, serviceOptions, svcInfo, agentInfo, agentDeployed, serviceStateData)
807-
if err != nil {
809+
if errors.Is(err, os.ErrNotExist) {
810+
logger.Debugf("No service deployer defined for this test")
811+
} else if err != nil {
808812
return nil, err
809813
}
810814

@@ -1000,7 +1004,7 @@ func (r *runner) prepareScenario(ctx context.Context, config *testConfig, svcInf
10001004
}
10011005

10021006
// Signal to the service that the agent is ready (policy is assigned).
1003-
if config.ServiceNotifySignal != "" {
1007+
if service != nil && config.ServiceNotifySignal != "" {
10041008
if err = service.Signal(ctx, config.ServiceNotifySignal); err != nil {
10051009
return nil, fmt.Errorf("failed to notify test service: %w", err)
10061010
}
@@ -1044,7 +1048,7 @@ func (r *runner) prepareScenario(ctx context.Context, config *testConfig, svcInf
10441048
return hits.size() > 0, nil
10451049
}, 1*time.Second, waitForDataTimeout)
10461050

1047-
if config.Service != "" && !config.IgnoreServiceError {
1051+
if service != nil && config.Service != "" && !config.IgnoreServiceError {
10481052
exited, code, err := service.ExitCode(ctx, config.Service)
10491053
if err != nil && !errors.Is(err, servicedeployer.ErrNotSupported) {
10501054
return nil, err

0 commit comments

Comments
 (0)