internal/agentdeployer/factory.go (122 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.
package agentdeployer
import (
"errors"
"fmt"
"os"
"path/filepath"
"github.com/elastic/elastic-package/internal/logger"
"github.com/elastic/elastic-package/internal/profile"
)
const (
TypeTest = "test"
TypeBench = "bench"
)
// FactoryOptions defines options used to create an instance of a service deployer.
type FactoryOptions struct {
Profile *profile.Profile
PackageRootPath string
DataStreamRootPath string
DevDeployDir string
Type string
StackVersion string
PolicyName string
PackageName string
DataStream string
RunTearDown bool
RunTestsOnly bool
RunSetup bool
}
// Factory chooses the appropriate service runner for the given data stream, depending
// on service configuration files defined in the package or data stream.
func Factory(options FactoryOptions) (AgentDeployer, error) {
agentDeployerName, err := selectAgentDeployerType(options)
if err != nil {
return nil, fmt.Errorf("failed to select agent deployer type: %w", err)
}
switch agentDeployerName {
case "default":
if options.Type != TypeTest {
return nil, fmt.Errorf("agent deployer is not supported for type %s", options.Type)
}
opts := DockerComposeAgentDeployerOptions{
Profile: options.Profile,
StackVersion: options.StackVersion,
PackageName: options.PackageName,
PolicyName: options.PolicyName,
DataStream: options.DataStream,
RunTearDown: options.RunTearDown,
RunTestsOnly: options.RunTestsOnly,
}
return NewCustomAgentDeployer(opts)
case "agent":
// FIXME: should this be just carried out by service deployer?
// FIXME: this docker-compose scenario contains both agent and service
return nil, nil
case "k8s":
opts := KubernetesAgentDeployerOptions{
Profile: options.Profile,
StackVersion: options.StackVersion,
PolicyName: options.PolicyName,
DataStream: options.DataStream,
RunSetup: options.RunSetup,
RunTestsOnly: options.RunTestsOnly,
RunTearDown: options.RunTearDown,
}
return NewKubernetesAgentDeployer(opts)
}
return nil, fmt.Errorf("unsupported agent deployer (name: %s)", agentDeployerName)
}
func selectAgentDeployerType(options FactoryOptions) (string, error) {
devDeployPath, err := FindDevDeployPath(options)
if errors.Is(err, os.ErrNotExist) {
return "default", nil
}
if err != nil {
return "", fmt.Errorf("can't find \"%s\" directory: %w", options.DevDeployDir, err)
}
agentDeployerNames, err := findAgentDeployers(devDeployPath)
if errors.Is(err, os.ErrNotExist) || len(agentDeployerNames) == 0 {
logger.Debugf("Not agent deployer found, using default one")
return "default", nil
}
if err != nil {
return "", fmt.Errorf("failed to find agent deployer: %w", err)
}
if len(agentDeployerNames) != 1 {
return "", fmt.Errorf("expected to find only one agent deployer in \"%s\"", devDeployPath)
}
agentDeployerName := agentDeployerNames[0]
// if package defines `_dev/deploy/docker` or `_dev/deploy/tf` folder to start their services,
// it should be using the default agent deployer`
if agentDeployerName == "docker" || agentDeployerName == "tf" {
return "default", nil
}
return agentDeployerName, nil
}
// FindDevDeployPath function returns a path reference to the "_dev/deploy" directory.
func FindDevDeployPath(options FactoryOptions) (string, error) {
dataStreamDevDeployPath := filepath.Join(options.DataStreamRootPath, options.DevDeployDir)
info, err := os.Stat(dataStreamDevDeployPath)
if err == nil && info.IsDir() {
return dataStreamDevDeployPath, nil
} else if err != nil && !errors.Is(err, os.ErrNotExist) {
return "", fmt.Errorf("stat failed for data stream (path: %s): %w", dataStreamDevDeployPath, err)
}
packageDevDeployPath := filepath.Join(options.PackageRootPath, options.DevDeployDir)
info, err = os.Stat(packageDevDeployPath)
if err == nil && info.IsDir() {
return packageDevDeployPath, nil
} else if err != nil && !errors.Is(err, os.ErrNotExist) {
return "", fmt.Errorf("stat failed for package (path: %s): %w", packageDevDeployPath, err)
}
return "", fmt.Errorf("\"%s\" %w", options.DevDeployDir, os.ErrNotExist)
}
func findAgentDeployers(devDeployPath string) ([]string, error) {
fis, err := os.ReadDir(devDeployPath)
if err != nil {
return nil, fmt.Errorf("can't read directory (path: %s): %w", devDeployPath, err)
}
var folders []os.DirEntry
for _, fi := range fis {
if fi.IsDir() {
folders = append(folders, fi)
}
}
var names []string
for _, folder := range folders {
names = append(names, folder.Name())
}
return names, nil
}